mirror of
				https://github.com/9001/copyparty.git
				synced 2025-11-04 05:43:17 +00:00 
			
		
		
		
	Compare commits
	
		
			18 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					a359d64d44 | ||
| 
						 | 
					22396e8c33 | ||
| 
						 | 
					5ded5a4516 | ||
| 
						 | 
					79c7639aaf | ||
| 
						 | 
					5bbf875385 | ||
| 
						 | 
					5e159432af | ||
| 
						 | 
					1d6ae409f6 | ||
| 
						 | 
					9d729d3d1a | ||
| 
						 | 
					4dd5d4e1b7 | ||
| 
						 | 
					acd8149479 | ||
| 
						 | 
					b97a1088fa | ||
| 
						 | 
					b77bed3324 | ||
| 
						 | 
					a2b7c85a1f | ||
| 
						 | 
					b28533f850 | ||
| 
						 | 
					bd8c7e538a | ||
| 
						 | 
					89e48cff24 | ||
| 
						 | 
					ae90a7b7b6 | ||
| 
						 | 
					6fc1be04da | 
							
								
								
									
										24
									
								
								.vscode/launch.py
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										24
									
								
								.vscode/launch.py
									
									
									
									
										vendored
									
									
								
							@@ -3,14 +3,12 @@
 | 
				
			|||||||
# launches 10x faster than mspython debugpy
 | 
					# launches 10x faster than mspython debugpy
 | 
				
			||||||
# and is stoppable with ^C
 | 
					# and is stoppable with ^C
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import re
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import sys
 | 
					import sys
 | 
				
			||||||
import shlex
 | 
					import shlex
 | 
				
			||||||
 | 
					 | 
				
			||||||
sys.path.insert(0, os.getcwd())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import jstyleson
 | 
					import jstyleson
 | 
				
			||||||
from copyparty.__main__ import main as copyparty
 | 
					import subprocess as sp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
with open(".vscode/launch.json", "r", encoding="utf-8") as f:
 | 
					with open(".vscode/launch.json", "r", encoding="utf-8") as f:
 | 
				
			||||||
    tj = f.read()
 | 
					    tj = f.read()
 | 
				
			||||||
@@ -25,11 +23,19 @@ except:
 | 
				
			|||||||
    pass
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
argv = [os.path.expanduser(x) if x.startswith("~") else x for x in argv]
 | 
					argv = [os.path.expanduser(x) if x.startswith("~") else x for x in argv]
 | 
				
			||||||
try:
 | 
					
 | 
				
			||||||
    copyparty(["a"] + argv)
 | 
					if re.search(" -j ?[0-9]", " ".join(argv)):
 | 
				
			||||||
except SystemExit as ex:
 | 
					    argv = [sys.executable, "-m", "copyparty"] + argv
 | 
				
			||||||
    if ex.code:
 | 
					    sp.check_call(argv)
 | 
				
			||||||
        raise
 | 
					else:
 | 
				
			||||||
 | 
					    sys.path.insert(0, os.getcwd())
 | 
				
			||||||
 | 
					    from copyparty.__main__ import main as copyparty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        copyparty(["a"] + argv)
 | 
				
			||||||
 | 
					    except SystemExit as ex:
 | 
				
			||||||
 | 
					        if ex.code:
 | 
				
			||||||
 | 
					            raise
 | 
				
			||||||
 | 
					
 | 
				
			||||||
print("\n\033[32mokke\033[0m")
 | 
					print("\n\033[32mokke\033[0m")
 | 
				
			||||||
sys.exit(1)
 | 
					sys.exit(1)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -68,12 +68,16 @@ you may also want these, especially on servers:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## notes
 | 
					## notes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					general:
 | 
				
			||||||
 | 
					* paper-printing is affected by dark/light-mode! use lightmode for color, darkmode for grayscale
 | 
				
			||||||
 | 
					  * because no browsers currently implement the media-query to do this properly orz
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					browser-specific:
 | 
				
			||||||
* iPhone/iPad: use Firefox to download files
 | 
					* iPhone/iPad: use Firefox to download files
 | 
				
			||||||
* Android-Chrome: increase "parallel uploads" for higher speed (android bug)
 | 
					* Android-Chrome: increase "parallel uploads" for higher speed (android bug)
 | 
				
			||||||
* Android-Firefox: takes a while to select files (their fix for ☝️)
 | 
					* Android-Firefox: takes a while to select files (their fix for ☝️)
 | 
				
			||||||
* Desktop-Firefox: ~~may use gigabytes of RAM if your files are massive~~ *seems to be OK now*
 | 
					* Desktop-Firefox: ~~may use gigabytes of RAM if your files are massive~~ *seems to be OK now*
 | 
				
			||||||
* paper-printing is affected by dark/light-mode! use lightmode for color, darkmode for grayscale
 | 
					* Desktop-Firefox: may stop you from deleting folders you've uploaded until you visit `about:memory` and click `Minimize memory usage`
 | 
				
			||||||
  * because no browsers currently implement the media-query to do this properly orz
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## status
 | 
					## status
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -54,6 +54,12 @@ MACOS = platform.system() == "Darwin"
 | 
				
			|||||||
info = log = dbg = None
 | 
					info = log = dbg = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					print("{} v{} @ {}".format(
 | 
				
			||||||
 | 
					    platform.python_implementation(),
 | 
				
			||||||
 | 
					    ".".join([str(x) for x in sys.version_info]),
 | 
				
			||||||
 | 
					    sys.executable))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
try:
 | 
					try:
 | 
				
			||||||
    from fuse import FUSE, FuseOSError, Operations
 | 
					    from fuse import FUSE, FuseOSError, Operations
 | 
				
			||||||
except:
 | 
					except:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -238,6 +238,7 @@ def run_argparse(argv, formatter):
 | 
				
			|||||||
              --ls '**'          # list all files which are possible to read
 | 
					              --ls '**'          # list all files which are possible to read
 | 
				
			||||||
              --ls '**,*,ln'     # check for dangerous symlinks
 | 
					              --ls '**,*,ln'     # check for dangerous symlinks
 | 
				
			||||||
              --ls '**,*,ln,p,r' # check, then start normally if safe
 | 
					              --ls '**,*,ln,p,r' # check, then start normally if safe
 | 
				
			||||||
 | 
					            \033[0m
 | 
				
			||||||
            """
 | 
					            """
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
@@ -377,6 +378,9 @@ def main(argv=None):
 | 
				
			|||||||
            + "  (if you crash with codec errors then that is why)"
 | 
					            + "  (if you crash with codec errors then that is why)"
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if WINDOWS and sys.version_info < (3, 6):
 | 
				
			||||||
 | 
					        al.no_scandir = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # signal.signal(signal.SIGINT, sighandler)
 | 
					    # signal.signal(signal.SIGINT, sighandler)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    SvcHub(al).run()
 | 
					    SvcHub(al).run()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
# coding: utf-8
 | 
					# coding: utf-8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
VERSION = (0, 11, 8)
 | 
					VERSION = (0, 11, 11)
 | 
				
			||||||
CODENAME = "the grid"
 | 
					CODENAME = "the grid"
 | 
				
			||||||
BUILD_DT = (2021, 6, 6)
 | 
					BUILD_DT = (2021, 6, 8)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
S_VERSION = ".".join(map(str, VERSION))
 | 
					S_VERSION = ".".join(map(str, VERSION))
 | 
				
			||||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
 | 
					S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ import sys
 | 
				
			|||||||
import stat
 | 
					import stat
 | 
				
			||||||
import threading
 | 
					import threading
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .__init__ import PY2, WINDOWS
 | 
					from .__init__ import WINDOWS
 | 
				
			||||||
from .util import IMPLICATIONS, undot, Pebkac, fsdec, fsenc, statdir, nuprint
 | 
					from .util import IMPLICATIONS, undot, Pebkac, fsdec, fsenc, statdir, nuprint
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -22,7 +22,7 @@ class VFS(object):
 | 
				
			|||||||
        self.uadm = uadm  # users who are regular admins
 | 
					        self.uadm = uadm  # users who are regular admins
 | 
				
			||||||
        self.flags = flags  # config switches
 | 
					        self.flags = flags  # config switches
 | 
				
			||||||
        self.nodes = {}  # child nodes
 | 
					        self.nodes = {}  # child nodes
 | 
				
			||||||
        self.all_vols = {vpath: self}  # flattened recursive
 | 
					        self.all_vols = {vpath: self} if realpath else {}  # flattened recursive
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __repr__(self):
 | 
					    def __repr__(self):
 | 
				
			||||||
        return "VFS({})".format(
 | 
					        return "VFS({})".format(
 | 
				
			||||||
@@ -96,6 +96,7 @@ class VFS(object):
 | 
				
			|||||||
        ]
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get(self, vpath, uname, will_read, will_write):
 | 
					    def get(self, vpath, uname, will_read, will_write):
 | 
				
			||||||
 | 
					        # type: (str, str, bool, bool) -> tuple[VFS, str]
 | 
				
			||||||
        """returns [vfsnode,fs_remainder] if user has the requested permissions"""
 | 
					        """returns [vfsnode,fs_remainder] if user has the requested permissions"""
 | 
				
			||||||
        vn, rem = self._find(vpath)
 | 
					        vn, rem = self._find(vpath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -136,6 +137,7 @@ class VFS(object):
 | 
				
			|||||||
            return os.path.realpath(rp)
 | 
					            return os.path.realpath(rp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def ls(self, rem, uname, scandir, incl_wo=False, lstat=False):
 | 
					    def ls(self, rem, uname, scandir, incl_wo=False, lstat=False):
 | 
				
			||||||
 | 
					        # type: (str, str, bool, bool, bool) -> tuple[str, str, dict[str, VFS]]
 | 
				
			||||||
        """return user-readable [fsdir,real,virt] items at vpath"""
 | 
					        """return user-readable [fsdir,real,virt] items at vpath"""
 | 
				
			||||||
        virt_vis = {}  # nodes readable by user
 | 
					        virt_vis = {}  # nodes readable by user
 | 
				
			||||||
        abspath = self.canonical(rem)
 | 
					        abspath = self.canonical(rem)
 | 
				
			||||||
@@ -156,13 +158,21 @@ class VFS(object):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return [abspath, real, virt_vis]
 | 
					        return [abspath, real, virt_vis]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def walk(self, rel, rem, uname, dots, scandir, lstat=False):
 | 
					    def walk(self, rel, rem, seen, uname, dots, scandir, lstat):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        recursively yields from ./rem;
 | 
					        recursively yields from ./rem;
 | 
				
			||||||
        rel is a unix-style user-defined vpath (not vfs-related)
 | 
					        rel is a unix-style user-defined vpath (not vfs-related)
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        fsroot, vfs_ls, vfs_virt = self.ls(rem, uname, scandir, False, lstat)
 | 
					        fsroot, vfs_ls, vfs_virt = self.ls(
 | 
				
			||||||
 | 
					            rem, uname, scandir, incl_wo=False, lstat=lstat
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if seen and not fsroot.startswith(seen[-1]) and fsroot in seen:
 | 
				
			||||||
 | 
					            print("bailing from symlink loop,\n  {}\n  {}".format(seen[-1], fsroot))
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        seen = seen[:] + [fsroot]
 | 
				
			||||||
        rfiles = [x for x in vfs_ls if not stat.S_ISDIR(x[1].st_mode)]
 | 
					        rfiles = [x for x in vfs_ls if not stat.S_ISDIR(x[1].st_mode)]
 | 
				
			||||||
        rdirs = [x for x in vfs_ls if stat.S_ISDIR(x[1].st_mode)]
 | 
					        rdirs = [x for x in vfs_ls if stat.S_ISDIR(x[1].st_mode)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -177,7 +187,7 @@ class VFS(object):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            wrel = (rel + "/" + rdir).lstrip("/")
 | 
					            wrel = (rel + "/" + rdir).lstrip("/")
 | 
				
			||||||
            wrem = (rem + "/" + rdir).lstrip("/")
 | 
					            wrem = (rem + "/" + rdir).lstrip("/")
 | 
				
			||||||
            for x in self.walk(wrel, wrem, uname, scandir, lstat):
 | 
					            for x in self.walk(wrel, wrem, seen, uname, dots, scandir, lstat):
 | 
				
			||||||
                yield x
 | 
					                yield x
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for n, vfs in sorted(vfs_virt.items()):
 | 
					        for n, vfs in sorted(vfs_virt.items()):
 | 
				
			||||||
@@ -185,14 +195,16 @@ class VFS(object):
 | 
				
			|||||||
                continue
 | 
					                continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            wrel = (rel + "/" + n).lstrip("/")
 | 
					            wrel = (rel + "/" + n).lstrip("/")
 | 
				
			||||||
            for x in vfs.walk(wrel, "", uname, scandir, lstat):
 | 
					            for x in vfs.walk(wrel, "", seen, uname, dots, scandir, lstat):
 | 
				
			||||||
                yield x
 | 
					                yield x
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def zipgen(self, vrem, flt, uname, dots, scandir):
 | 
					    def zipgen(self, vrem, flt, uname, dots, scandir):
 | 
				
			||||||
        if flt:
 | 
					        if flt:
 | 
				
			||||||
            flt = {k: True for k in flt}
 | 
					            flt = {k: True for k in flt}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for vpath, apath, files, rd, vd in self.walk("", vrem, uname, dots, scandir):
 | 
					        for vpath, apath, files, rd, vd in self.walk(
 | 
				
			||||||
 | 
					            "", vrem, [], uname, dots, scandir, False
 | 
				
			||||||
 | 
					        ):
 | 
				
			||||||
            if flt:
 | 
					            if flt:
 | 
				
			||||||
                files = [x for x in files if x[0] in flt]
 | 
					                files = [x for x in files if x[0] in flt]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -418,7 +430,7 @@ class AuthSrv(object):
 | 
				
			|||||||
            vfs = VFS(os.path.abspath("."), "", ["*"], ["*"])
 | 
					            vfs = VFS(os.path.abspath("."), "", ["*"], ["*"])
 | 
				
			||||||
        elif "" not in mount:
 | 
					        elif "" not in mount:
 | 
				
			||||||
            # there's volumes but no root; make root inaccessible
 | 
					            # there's volumes but no root; make root inaccessible
 | 
				
			||||||
            vfs = VFS(os.path.abspath("."), "")
 | 
					            vfs = VFS(None, "")
 | 
				
			||||||
            vfs.flags["d2d"] = True
 | 
					            vfs.flags["d2d"] = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        maxdepth = 0
 | 
					        maxdepth = 0
 | 
				
			||||||
@@ -616,13 +628,13 @@ class AuthSrv(object):
 | 
				
			|||||||
                    continue
 | 
					                    continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                atop = vn.realpath
 | 
					                atop = vn.realpath
 | 
				
			||||||
                g = vn.walk("", "", u, True, not self.args.no_scandir, lstat=False)
 | 
					                g = vn.walk("", "", [], u, True, not self.args.no_scandir, False)
 | 
				
			||||||
                for vpath, apath, files, _, _ in g:
 | 
					                for vpath, apath, files, _, _ in g:
 | 
				
			||||||
                    fnames = [n[0] for n in files]
 | 
					                    fnames = [n[0] for n in files]
 | 
				
			||||||
                    vpaths = [vpath + "/" + n for n in fnames] if vpath else fnames
 | 
					                    vpaths = [vpath + "/" + n for n in fnames] if vpath else fnames
 | 
				
			||||||
                    vpaths = [vtop + x for x in vpaths]
 | 
					                    vpaths = [vtop + x for x in vpaths]
 | 
				
			||||||
                    apaths = [os.path.join(apath, n) for n in fnames]
 | 
					                    apaths = [os.path.join(apath, n) for n in fnames]
 | 
				
			||||||
                    files = list(zip(vpaths, apaths))
 | 
					                    files = [[vpath + "/", apath + os.sep]] + list(zip(vpaths, apaths))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if flag_ln:
 | 
					                    if flag_ln:
 | 
				
			||||||
                        files = [x for x in files if not x[1].startswith(atop + os.sep)]
 | 
					                        files = [x for x in files if not x[1].startswith(atop + os.sep)]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,7 +44,9 @@ class BrokerMp(object):
 | 
				
			|||||||
            proc.clients = {}
 | 
					            proc.clients = {}
 | 
				
			||||||
            proc.workload = 0
 | 
					            proc.workload = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            thr = threading.Thread(target=self.collector, args=(proc,))
 | 
					            thr = threading.Thread(
 | 
				
			||||||
 | 
					                target=self.collector, args=(proc,), name="mp-collector"
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            thr.daemon = True
 | 
					            thr.daemon = True
 | 
				
			||||||
            thr.start()
 | 
					            thr.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -52,14 +54,19 @@ class BrokerMp(object):
 | 
				
			|||||||
            proc.start()
 | 
					            proc.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not self.args.q:
 | 
					        if not self.args.q:
 | 
				
			||||||
            thr = threading.Thread(target=self.debug_load_balancer)
 | 
					            thr = threading.Thread(
 | 
				
			||||||
 | 
					                target=self.debug_load_balancer, name="mp-dbg-loadbalancer"
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            thr.daemon = True
 | 
					            thr.daemon = True
 | 
				
			||||||
            thr.start()
 | 
					            thr.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def shutdown(self):
 | 
					    def shutdown(self):
 | 
				
			||||||
        self.log("broker", "shutting down")
 | 
					        self.log("broker", "shutting down")
 | 
				
			||||||
        for proc in self.procs:
 | 
					        for n, proc in enumerate(self.procs):
 | 
				
			||||||
            thr = threading.Thread(target=proc.q_pend.put([0, "shutdown", []]))
 | 
					            thr = threading.Thread(
 | 
				
			||||||
 | 
					                target=proc.q_pend.put([0, "shutdown", []]),
 | 
				
			||||||
 | 
					                name="mp-shutdown-{}-{}".format(n, len(self.procs)),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            thr.start()
 | 
					            thr.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with self.mutex:
 | 
					        with self.mutex:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,7 +27,7 @@ class MpWorker(object):
 | 
				
			|||||||
        self.retpend = {}
 | 
					        self.retpend = {}
 | 
				
			||||||
        self.retpend_mutex = threading.Lock()
 | 
					        self.retpend_mutex = threading.Lock()
 | 
				
			||||||
        self.mutex = threading.Lock()
 | 
					        self.mutex = threading.Lock()
 | 
				
			||||||
        self.workload_thr_active = False
 | 
					        self.workload_thr_alive = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # we inherited signal_handler from parent,
 | 
					        # we inherited signal_handler from parent,
 | 
				
			||||||
        # replace it with something harmless
 | 
					        # replace it with something harmless
 | 
				
			||||||
@@ -35,12 +35,12 @@ class MpWorker(object):
 | 
				
			|||||||
            signal.signal(signal.SIGINT, self.signal_handler)
 | 
					            signal.signal(signal.SIGINT, self.signal_handler)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # instantiate all services here (TODO: inheritance?)
 | 
					        # instantiate all services here (TODO: inheritance?)
 | 
				
			||||||
        self.httpsrv = HttpSrv(self)
 | 
					        self.httpsrv = HttpSrv(self, True)
 | 
				
			||||||
        self.httpsrv.disconnect_func = self.httpdrop
 | 
					        self.httpsrv.disconnect_func = self.httpdrop
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # on winxp and some other platforms,
 | 
					        # on winxp and some other platforms,
 | 
				
			||||||
        # use thr.join() to block all signals
 | 
					        # use thr.join() to block all signals
 | 
				
			||||||
        thr = threading.Thread(target=self.main)
 | 
					        thr = threading.Thread(target=self.main, name="mpw-main")
 | 
				
			||||||
        thr.daemon = True
 | 
					        thr.daemon = True
 | 
				
			||||||
        thr.start()
 | 
					        thr.start()
 | 
				
			||||||
        thr.join()
 | 
					        thr.join()
 | 
				
			||||||
@@ -75,13 +75,15 @@ class MpWorker(object):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                if self.args.log_conn:
 | 
					                if self.args.log_conn:
 | 
				
			||||||
                    self.log("%s %s" % addr, "|%sC-qpop" % ("-" * 4,), c="1;30")
 | 
					                    self.log("%s %s" % addr, "|%sC-qpop" % ("-" * 4,), c="1;30")
 | 
				
			||||||
                
 | 
					
 | 
				
			||||||
                self.httpsrv.accept(sck, addr)
 | 
					                self.httpsrv.accept(sck, addr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                with self.mutex:
 | 
					                with self.mutex:
 | 
				
			||||||
                    if not self.workload_thr_active:
 | 
					                    if not self.workload_thr_alive:
 | 
				
			||||||
                        self.workload_thr_alive = True
 | 
					                        self.workload_thr_alive = True
 | 
				
			||||||
                        thr = threading.Thread(target=self.thr_workload)
 | 
					                        thr = threading.Thread(
 | 
				
			||||||
 | 
					                            target=self.thr_workload, name="mpw-workload"
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
                        thr.daemon = True
 | 
					                        thr.daemon = True
 | 
				
			||||||
                        thr.start()
 | 
					                        thr.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,6 +16,7 @@ import calendar
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from .__init__ import E, PY2, WINDOWS, ANYWIN
 | 
					from .__init__ import E, PY2, WINDOWS, ANYWIN
 | 
				
			||||||
from .util import *  # noqa  # pylint: disable=unused-wildcard-import
 | 
					from .util import *  # noqa  # pylint: disable=unused-wildcard-import
 | 
				
			||||||
 | 
					from .authsrv import AuthSrv
 | 
				
			||||||
from .szip import StreamZip
 | 
					from .szip import StreamZip
 | 
				
			||||||
from .star import StreamTar
 | 
					from .star import StreamTar
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -35,12 +36,13 @@ class HttpCli(object):
 | 
				
			|||||||
    def __init__(self, conn):
 | 
					    def __init__(self, conn):
 | 
				
			||||||
        self.t0 = time.time()
 | 
					        self.t0 = time.time()
 | 
				
			||||||
        self.conn = conn
 | 
					        self.conn = conn
 | 
				
			||||||
        self.s = conn.s
 | 
					        self.s = conn.s  # type: socket
 | 
				
			||||||
        self.sr = conn.sr
 | 
					        self.sr = conn.sr  # type: Unrecv
 | 
				
			||||||
        self.ip = conn.addr[0]
 | 
					        self.ip = conn.addr[0]
 | 
				
			||||||
        self.addr = conn.addr
 | 
					        self.addr = conn.addr  # type: tuple[str, int]
 | 
				
			||||||
        self.args = conn.args
 | 
					        self.args = conn.args
 | 
				
			||||||
        self.auth = conn.auth
 | 
					        self.is_mp = conn.is_mp
 | 
				
			||||||
 | 
					        self.auth = conn.auth  # type: AuthSrv
 | 
				
			||||||
        self.ico = conn.ico
 | 
					        self.ico = conn.ico
 | 
				
			||||||
        self.thumbcli = conn.thumbcli
 | 
					        self.thumbcli = conn.thumbcli
 | 
				
			||||||
        self.log_func = conn.log_func
 | 
					        self.log_func = conn.log_func
 | 
				
			||||||
@@ -506,6 +508,7 @@ class HttpCli(object):
 | 
				
			|||||||
        items = items.replace("\r", "").split("\n")
 | 
					        items = items.replace("\r", "").split("\n")
 | 
				
			||||||
        items = [unquotep(x) for x in items if items]
 | 
					        items = [unquotep(x) for x in items if items]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.parser.drop()
 | 
				
			||||||
        return self.tx_zip(k, v, vn, rem, items, self.args.ed)
 | 
					        return self.tx_zip(k, v, vn, rem, items, self.args.ed)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def handle_post_json(self):
 | 
					    def handle_post_json(self):
 | 
				
			||||||
@@ -991,6 +994,8 @@ class HttpCli(object):
 | 
				
			|||||||
        cli_lastmod = self.headers.get("if-modified-since")
 | 
					        cli_lastmod = self.headers.get("if-modified-since")
 | 
				
			||||||
        if cli_lastmod:
 | 
					        if cli_lastmod:
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
 | 
					                # some browser append "; length=573"
 | 
				
			||||||
 | 
					                cli_lastmod = cli_lastmod.split(";")[0].strip()
 | 
				
			||||||
                cli_dt = time.strptime(cli_lastmod, HTTP_TS_FMT)
 | 
					                cli_dt = time.strptime(cli_lastmod, HTTP_TS_FMT)
 | 
				
			||||||
                cli_ts = calendar.timegm(cli_dt)
 | 
					                cli_ts = calendar.timegm(cli_dt)
 | 
				
			||||||
                return file_lastmod, int(file_ts) > int(cli_ts)
 | 
					                return file_lastmod, int(file_ts) > int(cli_ts)
 | 
				
			||||||
@@ -1160,7 +1165,8 @@ class HttpCli(object):
 | 
				
			|||||||
            if use_sendfile:
 | 
					            if use_sendfile:
 | 
				
			||||||
                remains = sendfile_kern(lower, upper, f, self.s)
 | 
					                remains = sendfile_kern(lower, upper, f, self.s)
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                remains = sendfile_py(lower, upper, f, self.s)
 | 
					                actor = self.conn if self.is_mp else None
 | 
				
			||||||
 | 
					                remains = sendfile_py(lower, upper, f, self.s, actor)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if remains > 0:
 | 
					        if remains > 0:
 | 
				
			||||||
            logmsg += " \033[31m" + unicode(upper - remains) + "\033[0m"
 | 
					            logmsg += " \033[31m" + unicode(upper - remains) + "\033[0m"
 | 
				
			||||||
@@ -1376,15 +1382,31 @@ class HttpCli(object):
 | 
				
			|||||||
        if self.args.no_stack:
 | 
					        if self.args.no_stack:
 | 
				
			||||||
            raise Pebkac(403, "disabled by argv")
 | 
					            raise Pebkac(403, "disabled by argv")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ret = []
 | 
					        threads = {}
 | 
				
			||||||
        names = dict([(t.ident, t.name) for t in threading.enumerate()])
 | 
					        names = dict([(t.ident, t.name) for t in threading.enumerate()])
 | 
				
			||||||
        for tid, stack in sys._current_frames().items():
 | 
					        for tid, stack in sys._current_frames().items():
 | 
				
			||||||
            ret.append("\n\n# {} ({:x})".format(names.get(tid), tid))
 | 
					            name = "{} ({:x})".format(names.get(tid), tid)
 | 
				
			||||||
 | 
					            threads[name] = stack
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        rret = []
 | 
				
			||||||
 | 
					        bret = []
 | 
				
			||||||
 | 
					        for name, stack in sorted(threads.items()):
 | 
				
			||||||
 | 
					            ret = ["\n\n# {}".format(name)]
 | 
				
			||||||
 | 
					            pad = None
 | 
				
			||||||
            for fn, lno, name, line in traceback.extract_stack(stack):
 | 
					            for fn, lno, name, line in traceback.extract_stack(stack):
 | 
				
			||||||
 | 
					                fn = os.sep.join(fn.split(os.sep)[-3:])
 | 
				
			||||||
                ret.append('File: "{}", line {}, in {}'.format(fn, lno, name))
 | 
					                ret.append('File: "{}", line {}, in {}'.format(fn, lno, name))
 | 
				
			||||||
                if line:
 | 
					                if line:
 | 
				
			||||||
                    ret.append("  " + str(line.strip()))
 | 
					                    ret.append("  " + str(line.strip()))
 | 
				
			||||||
 | 
					                    if "self.not_empty.wait()" in line:
 | 
				
			||||||
 | 
					                        pad = " " * 4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if pad:
 | 
				
			||||||
 | 
					                bret += [ret[0]] + [pad + x for x in ret[1:]]
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                rret += ret
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ret = rret + bret
 | 
				
			||||||
        ret = ("<pre>" + "\n".join(ret)).encode("utf-8")
 | 
					        ret = ("<pre>" + "\n".join(ret)).encode("utf-8")
 | 
				
			||||||
        self.reply(ret)
 | 
					        self.reply(ret)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1418,7 +1440,7 @@ class HttpCli(object):
 | 
				
			|||||||
        try:
 | 
					        try:
 | 
				
			||||||
            vn, rem = self.auth.vfs.get(top, self.uname, True, False)
 | 
					            vn, rem = self.auth.vfs.get(top, self.uname, True, False)
 | 
				
			||||||
            fsroot, vfs_ls, vfs_virt = vn.ls(
 | 
					            fsroot, vfs_ls, vfs_virt = vn.ls(
 | 
				
			||||||
                rem, self.uname, not self.args.no_scandir, True
 | 
					                rem, self.uname, not self.args.no_scandir, incl_wo=True
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        except:
 | 
					        except:
 | 
				
			||||||
            vfs_ls = []
 | 
					            vfs_ls = []
 | 
				
			||||||
@@ -1585,7 +1607,7 @@ class HttpCli(object):
 | 
				
			|||||||
                return self.tx_zip(k, v, vn, rem, [], self.args.ed)
 | 
					                return self.tx_zip(k, v, vn, rem, [], self.args.ed)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        fsroot, vfs_ls, vfs_virt = vn.ls(
 | 
					        fsroot, vfs_ls, vfs_virt = vn.ls(
 | 
				
			||||||
            rem, self.uname, not self.args.no_scandir, True
 | 
					            rem, self.uname, not self.args.no_scandir, incl_wo=True
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        stats = {k: v for k, v in vfs_ls}
 | 
					        stats = {k: v for k, v in vfs_ls}
 | 
				
			||||||
        vfs_ls = [x[0] for x in vfs_ls]
 | 
					        vfs_ls = [x[0] for x in vfs_ls]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,6 +35,7 @@ class HttpConn(object):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        self.args = hsrv.args
 | 
					        self.args = hsrv.args
 | 
				
			||||||
        self.auth = hsrv.auth
 | 
					        self.auth = hsrv.auth
 | 
				
			||||||
 | 
					        self.is_mp = hsrv.is_mp
 | 
				
			||||||
        self.cert_path = hsrv.cert_path
 | 
					        self.cert_path = hsrv.cert_path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        enth = HAVE_PIL and not self.args.no_thumb
 | 
					        enth = HAVE_PIL and not self.args.no_thumb
 | 
				
			||||||
@@ -174,6 +175,11 @@ class HttpConn(object):
 | 
				
			|||||||
            self.sr = Unrecv(self.s)
 | 
					            self.sr = Unrecv(self.s)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        while True:
 | 
					        while True:
 | 
				
			||||||
 | 
					            if self.is_mp:
 | 
				
			||||||
 | 
					                self.workload += 50
 | 
				
			||||||
 | 
					                if self.workload >= 2 ** 31:
 | 
				
			||||||
 | 
					                    self.workload = 100
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            cli = HttpCli(self)
 | 
					            cli = HttpCli(self)
 | 
				
			||||||
            if not cli.run():
 | 
					            if not cli.run():
 | 
				
			||||||
                return
 | 
					                return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,8 +25,8 @@ except ImportError:
 | 
				
			|||||||
    sys.exit(1)
 | 
					    sys.exit(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .__init__ import E, MACOS
 | 
					from .__init__ import E, MACOS
 | 
				
			||||||
from .httpconn import HttpConn
 | 
					 | 
				
			||||||
from .authsrv import AuthSrv
 | 
					from .authsrv import AuthSrv
 | 
				
			||||||
 | 
					from .httpconn import HttpConn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class HttpSrv(object):
 | 
					class HttpSrv(object):
 | 
				
			||||||
@@ -35,8 +35,9 @@ class HttpSrv(object):
 | 
				
			|||||||
    relying on MpSrv for performance (HttpSrv is just plain threads)
 | 
					    relying on MpSrv for performance (HttpSrv is just plain threads)
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, broker):
 | 
					    def __init__(self, broker, is_mp=False):
 | 
				
			||||||
        self.broker = broker
 | 
					        self.broker = broker
 | 
				
			||||||
 | 
					        self.is_mp = is_mp
 | 
				
			||||||
        self.args = broker.args
 | 
					        self.args = broker.args
 | 
				
			||||||
        self.log = broker.log
 | 
					        self.log = broker.log
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -66,7 +67,11 @@ class HttpSrv(object):
 | 
				
			|||||||
        if self.args.log_conn:
 | 
					        if self.args.log_conn:
 | 
				
			||||||
            self.log("%s %s" % addr, "|%sC-cthr" % ("-" * 5,), c="1;30")
 | 
					            self.log("%s %s" % addr, "|%sC-cthr" % ("-" * 5,), c="1;30")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        thr = threading.Thread(target=self.thr_client, args=(sck, addr))
 | 
					        thr = threading.Thread(
 | 
				
			||||||
 | 
					            target=self.thr_client,
 | 
				
			||||||
 | 
					            args=(sck, addr),
 | 
				
			||||||
 | 
					            name="httpsrv-{}-{}".format(addr[0].split(".", 2)[-1][-6:], addr[1]),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        thr.daemon = True
 | 
					        thr.daemon = True
 | 
				
			||||||
        thr.start()
 | 
					        thr.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -84,13 +89,16 @@ class HttpSrv(object):
 | 
				
			|||||||
        cli = HttpConn(sck, addr, self)
 | 
					        cli = HttpConn(sck, addr, self)
 | 
				
			||||||
        with self.mutex:
 | 
					        with self.mutex:
 | 
				
			||||||
            self.clients[cli] = 0
 | 
					            self.clients[cli] = 0
 | 
				
			||||||
            self.workload += 50
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if not self.workload_thr_alive:
 | 
					            if self.is_mp:
 | 
				
			||||||
                self.workload_thr_alive = True
 | 
					                self.workload += 50
 | 
				
			||||||
                thr = threading.Thread(target=self.thr_workload)
 | 
					                if not self.workload_thr_alive:
 | 
				
			||||||
                thr.daemon = True
 | 
					                    self.workload_thr_alive = True
 | 
				
			||||||
                thr.start()
 | 
					                    thr = threading.Thread(
 | 
				
			||||||
 | 
					                        target=self.thr_workload, name="httpsrv-workload"
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                    thr.daemon = True
 | 
				
			||||||
 | 
					                    thr.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            if self.args.log_conn:
 | 
					            if self.args.log_conn:
 | 
				
			||||||
@@ -99,6 +107,7 @@ class HttpSrv(object):
 | 
				
			|||||||
            cli.run()
 | 
					            cli.run()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        finally:
 | 
					        finally:
 | 
				
			||||||
 | 
					            sck = cli.s
 | 
				
			||||||
            if self.args.log_conn:
 | 
					            if self.args.log_conn:
 | 
				
			||||||
                self.log("%s %s" % addr, "|%sC-cdone" % ("-" * 7,), c="1;30")
 | 
					                self.log("%s %s" % addr, "|%sC-cdone" % ("-" * 7,), c="1;30")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,6 @@
 | 
				
			|||||||
 | 
					# coding: utf-8
 | 
				
			||||||
 | 
					from __future__ import print_function, unicode_literals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import hashlib
 | 
					import hashlib
 | 
				
			||||||
import colorsys
 | 
					import colorsys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,6 @@
 | 
				
			|||||||
 | 
					# coding: utf-8
 | 
				
			||||||
 | 
					from __future__ import print_function, unicode_literals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import tarfile
 | 
					import tarfile
 | 
				
			||||||
import threading
 | 
					import threading
 | 
				
			||||||
@@ -42,7 +45,7 @@ class StreamTar(object):
 | 
				
			|||||||
        fmt = tarfile.GNU_FORMAT
 | 
					        fmt = tarfile.GNU_FORMAT
 | 
				
			||||||
        self.tar = tarfile.open(fileobj=self.qfile, mode="w|", format=fmt)
 | 
					        self.tar = tarfile.open(fileobj=self.qfile, mode="w|", format=fmt)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        w = threading.Thread(target=self._gen)
 | 
					        w = threading.Thread(target=self._gen, name="star-gen")
 | 
				
			||||||
        w.daemon = True
 | 
					        w.daemon = True
 | 
				
			||||||
        w.start()
 | 
					        w.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,6 @@
 | 
				
			|||||||
 | 
					# coding: utf-8
 | 
				
			||||||
 | 
					from __future__ import print_function, unicode_literals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import time
 | 
					import time
 | 
				
			||||||
import tempfile
 | 
					import tempfile
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -71,7 +71,7 @@ class SvcHub(object):
 | 
				
			|||||||
        self.broker = Broker(self)
 | 
					        self.broker = Broker(self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def run(self):
 | 
					    def run(self):
 | 
				
			||||||
        thr = threading.Thread(target=self.tcpsrv.run)
 | 
					        thr = threading.Thread(target=self.tcpsrv.run, name="svchub-main")
 | 
				
			||||||
        thr.daemon = True
 | 
					        thr.daemon = True
 | 
				
			||||||
        thr.start()
 | 
					        thr.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -95,9 +95,11 @@ class SvcHub(object):
 | 
				
			|||||||
                        break
 | 
					                        break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if n == 3:
 | 
					                    if n == 3:
 | 
				
			||||||
                        print("waiting for thumbsrv...")
 | 
					                        print("waiting for thumbsrv (10sec)...")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            print("nailed it")
 | 
					            print("nailed it", end="")
 | 
				
			||||||
 | 
					        finally:
 | 
				
			||||||
 | 
					            print("\033[0m")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _log_disabled(self, src, msg, c=0):
 | 
					    def _log_disabled(self, src, msg, c=0):
 | 
				
			||||||
        pass
 | 
					        pass
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,6 @@
 | 
				
			|||||||
 | 
					# coding: utf-8
 | 
				
			||||||
 | 
					from __future__ import print_function, unicode_literals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import time
 | 
					import time
 | 
				
			||||||
import zlib
 | 
					import zlib
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,6 @@
 | 
				
			|||||||
 | 
					# coding: utf-8
 | 
				
			||||||
 | 
					from __future__ import print_function, unicode_literals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import time
 | 
					import time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,6 @@
 | 
				
			|||||||
 | 
					# coding: utf-8
 | 
				
			||||||
 | 
					from __future__ import print_function, unicode_literals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import sys
 | 
					import sys
 | 
				
			||||||
import time
 | 
					import time
 | 
				
			||||||
@@ -114,8 +117,10 @@ class ThumbSrv(object):
 | 
				
			|||||||
        self.stopping = False
 | 
					        self.stopping = False
 | 
				
			||||||
        self.nthr = os.cpu_count() if hasattr(os, "cpu_count") else 4
 | 
					        self.nthr = os.cpu_count() if hasattr(os, "cpu_count") else 4
 | 
				
			||||||
        self.q = Queue(self.nthr * 4)
 | 
					        self.q = Queue(self.nthr * 4)
 | 
				
			||||||
        for _ in range(self.nthr):
 | 
					        for n in range(self.nthr):
 | 
				
			||||||
            t = threading.Thread(target=self.worker)
 | 
					            t = threading.Thread(
 | 
				
			||||||
 | 
					                target=self.worker, name="thumb-{}-{}".format(n, self.nthr)
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            t.daemon = True
 | 
					            t.daemon = True
 | 
				
			||||||
            t.start()
 | 
					            t.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -131,7 +136,7 @@ class ThumbSrv(object):
 | 
				
			|||||||
            msg += ", ".join(missing)
 | 
					            msg += ", ".join(missing)
 | 
				
			||||||
            self.log(msg, c=3)
 | 
					            self.log(msg, c=3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        t = threading.Thread(target=self.cleaner)
 | 
					        t = threading.Thread(target=self.cleaner, name="thumb-cleaner")
 | 
				
			||||||
        t.daemon = True
 | 
					        t.daemon = True
 | 
				
			||||||
        t.start()
 | 
					        t.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -192,6 +192,7 @@ class U2idx(object):
 | 
				
			|||||||
                self.active_id,
 | 
					                self.active_id,
 | 
				
			||||||
                done_flag,
 | 
					                done_flag,
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
 | 
					            name="u2idx-terminator",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        thr.daemon = True
 | 
					        thr.daemon = True
 | 
				
			||||||
        thr.start()
 | 
					        thr.start()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -83,7 +83,7 @@ class Up2k(object):
 | 
				
			|||||||
        if ANYWIN:
 | 
					        if ANYWIN:
 | 
				
			||||||
            # usually fails to set lastmod too quickly
 | 
					            # usually fails to set lastmod too quickly
 | 
				
			||||||
            self.lastmod_q = Queue()
 | 
					            self.lastmod_q = Queue()
 | 
				
			||||||
            thr = threading.Thread(target=self._lastmodder)
 | 
					            thr = threading.Thread(target=self._lastmodder, name="up2k-lastmod")
 | 
				
			||||||
            thr.daemon = True
 | 
					            thr.daemon = True
 | 
				
			||||||
            thr.start()
 | 
					            thr.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -96,7 +96,9 @@ class Up2k(object):
 | 
				
			|||||||
        if self.args.no_fastboot:
 | 
					        if self.args.no_fastboot:
 | 
				
			||||||
            self.deferred_init(all_vols)
 | 
					            self.deferred_init(all_vols)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            t = threading.Thread(target=self.deferred_init, args=(all_vols,))
 | 
					            t = threading.Thread(
 | 
				
			||||||
 | 
					                target=self.deferred_init, args=(all_vols,), name="up2k-deferred-init"
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            t.daemon = True
 | 
					            t.daemon = True
 | 
				
			||||||
            t.start()
 | 
					            t.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -104,20 +106,20 @@ class Up2k(object):
 | 
				
			|||||||
        have_e2d = self.init_indexes(all_vols)
 | 
					        have_e2d = self.init_indexes(all_vols)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if have_e2d:
 | 
					        if have_e2d:
 | 
				
			||||||
            thr = threading.Thread(target=self._snapshot)
 | 
					            thr = threading.Thread(target=self._snapshot, name="up2k-snapshot")
 | 
				
			||||||
            thr.daemon = True
 | 
					            thr.daemon = True
 | 
				
			||||||
            thr.start()
 | 
					            thr.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            thr = threading.Thread(target=self._hasher)
 | 
					            thr = threading.Thread(target=self._hasher, name="up2k-hasher")
 | 
				
			||||||
            thr.daemon = True
 | 
					            thr.daemon = True
 | 
				
			||||||
            thr.start()
 | 
					            thr.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if self.mtag:
 | 
					            if self.mtag:
 | 
				
			||||||
                thr = threading.Thread(target=self._tagger)
 | 
					                thr = threading.Thread(target=self._tagger, name="up2k-tagger")
 | 
				
			||||||
                thr.daemon = True
 | 
					                thr.daemon = True
 | 
				
			||||||
                thr.start()
 | 
					                thr.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                thr = threading.Thread(target=self._run_all_mtp)
 | 
					                thr = threading.Thread(target=self._run_all_mtp, name="up2k-mtp-init")
 | 
				
			||||||
                thr.daemon = True
 | 
					                thr.daemon = True
 | 
				
			||||||
                thr.start()
 | 
					                thr.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -132,7 +134,11 @@ class Up2k(object):
 | 
				
			|||||||
            return "cannot initiate; scan is already in progress"
 | 
					            return "cannot initiate; scan is already in progress"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        args = (all_vols, scan_vols)
 | 
					        args = (all_vols, scan_vols)
 | 
				
			||||||
        t = threading.Thread(target=self.init_indexes, args=args)
 | 
					        t = threading.Thread(
 | 
				
			||||||
 | 
					            target=self.init_indexes,
 | 
				
			||||||
 | 
					            args=args,
 | 
				
			||||||
 | 
					            name="up2k-rescan-{}".format(scan_vols[0]),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        t.daemon = True
 | 
					        t.daemon = True
 | 
				
			||||||
        t.start()
 | 
					        t.start()
 | 
				
			||||||
        return None
 | 
					        return None
 | 
				
			||||||
@@ -186,12 +192,14 @@ class Up2k(object):
 | 
				
			|||||||
                self.log("cannot access " + vol.realpath, c=1)
 | 
					                self.log("cannot access " + vol.realpath, c=1)
 | 
				
			||||||
                continue
 | 
					                continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if scan_vols and vol.vpath not in scan_vols:
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if not self.register_vpath(vol.realpath, vol.flags):
 | 
					            if not self.register_vpath(vol.realpath, vol.flags):
 | 
				
			||||||
                # self.log("db not enabled for {}".format(m, vol.realpath))
 | 
					                # self.log("db not enabled for {}".format(m, vol.realpath))
 | 
				
			||||||
                continue
 | 
					                continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if vol.vpath in scan_vols or not scan_vols:
 | 
					            live_vols.append(vol)
 | 
				
			||||||
                live_vols.append(vol)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if vol.vpath not in self.volstate:
 | 
					            if vol.vpath not in self.volstate:
 | 
				
			||||||
                self.volstate[vol.vpath] = "OFFLINE (pending initialization)"
 | 
					                self.volstate[vol.vpath] = "OFFLINE (pending initialization)"
 | 
				
			||||||
@@ -271,7 +279,7 @@ class Up2k(object):
 | 
				
			|||||||
        if self.mtag:
 | 
					        if self.mtag:
 | 
				
			||||||
            m = "online (running mtp)"
 | 
					            m = "online (running mtp)"
 | 
				
			||||||
            if scan_vols:
 | 
					            if scan_vols:
 | 
				
			||||||
                thr = threading.Thread(target=self._run_all_mtp)
 | 
					                thr = threading.Thread(target=self._run_all_mtp, name="up2k-mtp-scan")
 | 
				
			||||||
                thr.daemon = True
 | 
					                thr.daemon = True
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            del self.pp
 | 
					            del self.pp
 | 
				
			||||||
@@ -288,7 +296,10 @@ class Up2k(object):
 | 
				
			|||||||
    def register_vpath(self, ptop, flags):
 | 
					    def register_vpath(self, ptop, flags):
 | 
				
			||||||
        db_path = os.path.join(ptop, ".hist", "up2k.db")
 | 
					        db_path = os.path.join(ptop, ".hist", "up2k.db")
 | 
				
			||||||
        if ptop in self.registry:
 | 
					        if ptop in self.registry:
 | 
				
			||||||
            return [self.cur[ptop], db_path]
 | 
					            try:
 | 
				
			||||||
 | 
					                return [self.cur[ptop], db_path]
 | 
				
			||||||
 | 
					            except:
 | 
				
			||||||
 | 
					                return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        _, flags = self._expr_idx_filter(flags)
 | 
					        _, flags = self._expr_idx_filter(flags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -370,7 +381,8 @@ class Up2k(object):
 | 
				
			|||||||
        self.pp.msg = "a{} {}".format(self.pp.n, cdir)
 | 
					        self.pp.msg = "a{} {}".format(self.pp.n, cdir)
 | 
				
			||||||
        histdir = os.path.join(top, ".hist")
 | 
					        histdir = os.path.join(top, ".hist")
 | 
				
			||||||
        ret = 0
 | 
					        ret = 0
 | 
				
			||||||
        for iname, inf in statdir(self.log, not self.args.no_scandir, False, cdir):
 | 
					        g = statdir(self.log, not self.args.no_scandir, False, cdir)
 | 
				
			||||||
 | 
					        for iname, inf in sorted(g):
 | 
				
			||||||
            abspath = os.path.join(cdir, iname)
 | 
					            abspath = os.path.join(cdir, iname)
 | 
				
			||||||
            lmod = int(inf.st_mtime)
 | 
					            lmod = int(inf.st_mtime)
 | 
				
			||||||
            if stat.S_ISDIR(inf.st_mode):
 | 
					            if stat.S_ISDIR(inf.st_mode):
 | 
				
			||||||
@@ -753,7 +765,9 @@ class Up2k(object):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        mpool = Queue(nw)
 | 
					        mpool = Queue(nw)
 | 
				
			||||||
        for _ in range(nw):
 | 
					        for _ in range(nw):
 | 
				
			||||||
            thr = threading.Thread(target=self._tag_thr, args=(mpool,))
 | 
					            thr = threading.Thread(
 | 
				
			||||||
 | 
					                target=self._tag_thr, args=(mpool,), name="up2k-mpool"
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            thr.daemon = True
 | 
					            thr.daemon = True
 | 
				
			||||||
            thr.start()
 | 
					            thr.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -193,7 +193,7 @@ class ProgressPrinter(threading.Thread):
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self):
 | 
					    def __init__(self):
 | 
				
			||||||
        threading.Thread.__init__(self)
 | 
					        threading.Thread.__init__(self, name="pp")
 | 
				
			||||||
        self.daemon = True
 | 
					        self.daemon = True
 | 
				
			||||||
        self.msg = None
 | 
					        self.msg = None
 | 
				
			||||||
        self.end = False
 | 
					        self.end = False
 | 
				
			||||||
@@ -208,6 +208,8 @@ class ProgressPrinter(threading.Thread):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            msg = self.msg
 | 
					            msg = self.msg
 | 
				
			||||||
            uprint(" {}\033[K\r".format(msg))
 | 
					            uprint(" {}\033[K\r".format(msg))
 | 
				
			||||||
 | 
					            if PY2:
 | 
				
			||||||
 | 
					                sys.stdout.flush()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        print("\033[K", end="")
 | 
					        print("\033[K", end="")
 | 
				
			||||||
        sys.stdout.flush()  # necessary on win10 even w/ stderr btw
 | 
					        sys.stdout.flush()  # necessary on win10 even w/ stderr btw
 | 
				
			||||||
@@ -852,13 +854,14 @@ def yieldfile(fn):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def hashcopy(actor, fin, fout):
 | 
					def hashcopy(actor, fin, fout):
 | 
				
			||||||
    u32_lim = int((2 ** 31) * 0.9)
 | 
					    is_mp = actor.is_mp
 | 
				
			||||||
    hashobj = hashlib.sha512()
 | 
					    hashobj = hashlib.sha512()
 | 
				
			||||||
    tlen = 0
 | 
					    tlen = 0
 | 
				
			||||||
    for buf in fin:
 | 
					    for buf in fin:
 | 
				
			||||||
        actor.workload += 1
 | 
					        if is_mp:
 | 
				
			||||||
        if actor.workload > u32_lim:
 | 
					            actor.workload += 1
 | 
				
			||||||
            actor.workload = 100  # prevent overflow
 | 
					            if actor.workload > 2 ** 31:
 | 
				
			||||||
 | 
					                actor.workload = 100
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        tlen += len(buf)
 | 
					        tlen += len(buf)
 | 
				
			||||||
        hashobj.update(buf)
 | 
					        hashobj.update(buf)
 | 
				
			||||||
@@ -870,12 +873,17 @@ def hashcopy(actor, fin, fout):
 | 
				
			|||||||
    return tlen, hashobj.hexdigest(), digest_b64
 | 
					    return tlen, hashobj.hexdigest(), digest_b64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def sendfile_py(lower, upper, f, s):
 | 
					def sendfile_py(lower, upper, f, s, actor=None):
 | 
				
			||||||
    remains = upper - lower
 | 
					    remains = upper - lower
 | 
				
			||||||
    f.seek(lower)
 | 
					    f.seek(lower)
 | 
				
			||||||
    while remains > 0:
 | 
					    while remains > 0:
 | 
				
			||||||
 | 
					        if actor:
 | 
				
			||||||
 | 
					            actor.workload += 1
 | 
				
			||||||
 | 
					            if actor.workload > 2 ** 31:
 | 
				
			||||||
 | 
					                actor.workload = 100
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # time.sleep(0.01)
 | 
					        # time.sleep(0.01)
 | 
				
			||||||
        buf = f.read(min(4096, remains))
 | 
					        buf = f.read(min(1024 * 32, remains))
 | 
				
			||||||
        if not buf:
 | 
					        if not buf:
 | 
				
			||||||
            return remains
 | 
					            return remains
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -153,6 +153,9 @@ dbg.asyncStore.pendingBreakpoints = {}
 | 
				
			|||||||
# fix firefox phantom breakpoints
 | 
					# fix firefox phantom breakpoints
 | 
				
			||||||
about:config >> devtools.debugger.prefs-schema-version = -1
 | 
					about:config >> devtools.debugger.prefs-schema-version = -1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# determine server version
 | 
				
			||||||
 | 
					git reset --hard origin/HEAD && git log --format=format:"%H %ai %d" --decorate=full > /dev/shm/revs && cat /dev/shm/revs | while read -r rev extra; do (git reset --hard $rev >/dev/null 2>/dev/null && dsz=$(cat copyparty/web/{util,browser,up2k}.js 2>/dev/null | diff -wNarU0 - <(cat /mnt/Users/ed/Downloads/ref/{util,browser,up2k}.js) | wc -c) && printf '%s %6s %s\n' "$rev" $dsz "$extra") </dev/null; done                
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
##
 | 
					##
 | 
				
			||||||
## http 206
 | 
					## http 206
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,6 +32,10 @@ gtar=$(command -v gtar || command -v gnutar) || true
 | 
				
			|||||||
	[ -e /opt/local/bin/bzip2 ] &&
 | 
						[ -e /opt/local/bin/bzip2 ] &&
 | 
				
			||||||
		bzip2() { /opt/local/bin/bzip2 "$@"; }
 | 
							bzip2() { /opt/local/bin/bzip2 "$@"; }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					gawk=$(command -v gawk || command -v gnuawk || command -v awk)
 | 
				
			||||||
 | 
					awk() { $gawk "$@"; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pybin=$(command -v python3 || command -v python) || {
 | 
					pybin=$(command -v python3 || command -v python) || {
 | 
				
			||||||
	echo need python
 | 
						echo need python
 | 
				
			||||||
	exit 1
 | 
						exit 1
 | 
				
			||||||
@@ -194,11 +198,40 @@ tmv "$f"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# up2k goes from 28k to 22k laff
 | 
					# up2k goes from 28k to 22k laff
 | 
				
			||||||
echo entabbening
 | 
					echo entabbening
 | 
				
			||||||
find | grep -E '\.(js|css|html)$' | while IFS= read -r f; do
 | 
					find | grep -E '\.css$' | while IFS= read -r f; do
 | 
				
			||||||
 | 
						awk '{
 | 
				
			||||||
 | 
							sub(/^[ \t]+/,"");
 | 
				
			||||||
 | 
							sub(/[ \t]+$/,"");
 | 
				
			||||||
 | 
							$0=gensub(/^([a-z-]+) *: *(.*[^ ]) *;$/,"\\1:\\2;","1");
 | 
				
			||||||
 | 
							sub(/ +\{$/,"{");
 | 
				
			||||||
 | 
							gsub(/, /,",")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						!/\}$/ {printf "%s",$0;next}
 | 
				
			||||||
 | 
						1
 | 
				
			||||||
 | 
						' <$f | gsed 's/;\}$/}/' >t
 | 
				
			||||||
 | 
						tmv "$f"
 | 
				
			||||||
 | 
					done
 | 
				
			||||||
 | 
					find | grep -E '\.(js|html)$' | while IFS= read -r f; do
 | 
				
			||||||
	unexpand -t 4 --first-only <"$f" >t
 | 
						unexpand -t 4 --first-only <"$f" >t
 | 
				
			||||||
	tmv "$f"
 | 
						tmv "$f"
 | 
				
			||||||
done
 | 
					done
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					gzres() {
 | 
				
			||||||
 | 
					command -v pigz &&
 | 
				
			||||||
 | 
						pk='pigz -11 -J 34 -I 100' ||
 | 
				
			||||||
 | 
						pk='gzip'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo "$pk"
 | 
				
			||||||
 | 
					find | grep -E '\.(js|css)$' | while IFS= read -r f; do
 | 
				
			||||||
 | 
						echo -n .
 | 
				
			||||||
 | 
						$pk "$f"
 | 
				
			||||||
 | 
					done
 | 
				
			||||||
 | 
					echo
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					gzres
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
echo gen tarlist
 | 
					echo gen tarlist
 | 
				
			||||||
for d in copyparty dep-j2; do find $d -type f; done |
 | 
					for d in copyparty dep-j2; do find $d -type f; done |
 | 
				
			||||||
sed -r 's/(.*)\.(.*)/\2 \1/' | LC_ALL=C sort |
 | 
					sed -r 's/(.*)\.(.*)/\2 \1/' | LC_ALL=C sort |
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,7 +47,7 @@ grep -E '/(python|pypy)[0-9\.-]*$' >$dir/pys || true
 | 
				
			|||||||
	printf '\033[1;30mlooking for jinja2 in [%s]\033[0m\n' "$_py" >&2
 | 
						printf '\033[1;30mlooking for jinja2 in [%s]\033[0m\n' "$_py" >&2
 | 
				
			||||||
	$_py -c 'import jinja2' 2>/dev/null || continue
 | 
						$_py -c 'import jinja2' 2>/dev/null || continue
 | 
				
			||||||
	printf '%s\n' "$_py"
 | 
						printf '%s\n' "$_py"
 | 
				
			||||||
	mv $dir/{,x.}jinja2
 | 
						mv $dir/{,x.}dep-j2
 | 
				
			||||||
	break
 | 
						break
 | 
				
			||||||
done)"
 | 
					done)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,7 @@ from textwrap import dedent
 | 
				
			|||||||
from argparse import Namespace
 | 
					from argparse import Namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from tests import util as tu
 | 
					from tests import util as tu
 | 
				
			||||||
from copyparty.authsrv import AuthSrv
 | 
					from copyparty.authsrv import AuthSrv, VFS
 | 
				
			||||||
from copyparty import util
 | 
					from copyparty import util
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -47,6 +47,7 @@ class TestVFS(unittest.TestCase):
 | 
				
			|||||||
        self.assertEqual(util.undot(query), response)
 | 
					        self.assertEqual(util.undot(query), response)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def ls(self, vfs, vpath, uname):
 | 
					    def ls(self, vfs, vpath, uname):
 | 
				
			||||||
 | 
					        # type: (VFS, str, str) -> tuple[str, str, str]
 | 
				
			||||||
        """helper for resolving and listing a folder"""
 | 
					        """helper for resolving and listing a folder"""
 | 
				
			||||||
        vn, rem = vfs.get(vpath, uname, True, False)
 | 
					        vn, rem = vfs.get(vpath, uname, True, False)
 | 
				
			||||||
        r1 = vn.ls(rem, uname, False)
 | 
					        r1 = vn.ls(rem, uname, False)
 | 
				
			||||||
@@ -250,7 +251,7 @@ class TestVFS(unittest.TestCase):
 | 
				
			|||||||
        n = au.vfs
 | 
					        n = au.vfs
 | 
				
			||||||
        # root was not defined, so PWD with no access to anyone
 | 
					        # root was not defined, so PWD with no access to anyone
 | 
				
			||||||
        self.assertEqual(n.vpath, "")
 | 
					        self.assertEqual(n.vpath, "")
 | 
				
			||||||
        self.assertEqual(n.realpath, td)
 | 
					        self.assertEqual(n.realpath, None)
 | 
				
			||||||
        self.assertEqual(n.uread, [])
 | 
					        self.assertEqual(n.uread, [])
 | 
				
			||||||
        self.assertEqual(n.uwrite, [])
 | 
					        self.assertEqual(n.uwrite, [])
 | 
				
			||||||
        self.assertEqual(len(n.nodes), 1)
 | 
					        self.assertEqual(len(n.nodes), 1)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -116,6 +116,7 @@ class VHttpConn(object):
 | 
				
			|||||||
        self.addr = ("127.0.0.1", "42069")
 | 
					        self.addr = ("127.0.0.1", "42069")
 | 
				
			||||||
        self.args = args
 | 
					        self.args = args
 | 
				
			||||||
        self.auth = auth
 | 
					        self.auth = auth
 | 
				
			||||||
 | 
					        self.is_mp = False
 | 
				
			||||||
        self.log_func = log
 | 
					        self.log_func = log
 | 
				
			||||||
        self.log_src = "a"
 | 
					        self.log_src = "a"
 | 
				
			||||||
        self.lf_url = None
 | 
					        self.lf_url = None
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user