mirror of
https://github.com/9001/copyparty.git
synced 2025-08-16 16:42:13 -06:00
add SMB/CIFS server
This commit is contained in:
parent
4bcd30da6b
commit
f3a501db30
41
README.md
41
README.md
|
@ -59,6 +59,7 @@ try the **[read-only demo server](https://a.ocv.me/pub/demo/)** 👀 running fro
|
|||
* [qr-code](#qr-code) - print a qr-code [(screenshot)](https://user-images.githubusercontent.com/241032/194728533-6f00849b-c6ac-43c6-9359-83e454d11e00.png) for quick access
|
||||
* [ftp server](#ftp-server) - an FTP server can be started using `--ftp 3921`
|
||||
* [webdav server](#webdav-server) - enable with `--dav`
|
||||
* [smb server](#smb-server) - unsafe, not recommended for wan
|
||||
* [file indexing](#file-indexing) - enables dedup and music search ++
|
||||
* [exclude-patterns](#exclude-patterns) - to save some time
|
||||
* [filesystem guards](#filesystem-guards) - avoid traversing into other filesystems
|
||||
|
@ -168,7 +169,9 @@ feature summary
|
|||
* ☑ multiprocessing (actual multithreading)
|
||||
* ☑ volumes (mountpoints)
|
||||
* ☑ [accounts](#accounts-and-volumes)
|
||||
* ☑ [ftp-server](#ftp-server)
|
||||
* ☑ [ftp server](#ftp-server)
|
||||
* ☑ [webdav server](#webdav-server)
|
||||
* ☑ [smb/cifs server](#smb-server)
|
||||
* ☑ [qr-code](#qr-code) for quick access
|
||||
* upload
|
||||
* ☑ basic: plain multipart, ie6 support
|
||||
|
@ -735,6 +738,39 @@ known client bugs:
|
|||
* latin-1 is fine, hiragana is not (not even as shift-jis on japanese xp)
|
||||
|
||||
|
||||
## smb server
|
||||
|
||||
unsafe, not recommended for wan, enable with `--smb` for read-only or `--smbw` for read-write
|
||||
|
||||
dependencies: `python3 -m pip install --user -U impacket==0.10.0`
|
||||
* newer versions of impacket will hopefully work just fine but there is monkeypatching so maybe not
|
||||
|
||||
some big warnings specific to SMB/CIFS, in decreasing importance:
|
||||
* not entirely confident that read-only is read-only
|
||||
* the smb backend is not fully integrated with vfs, meaning there could be security issues (path traversal). Please use `--smb-port` (see below) and [./bin#prisonpartysh](prisonparty)
|
||||
* account passwords work per-volume as expected, but account permissions are ignored; all accounts have access to all volumes, and `--smbw` gives all accounts write-access everywhere
|
||||
* shadowing (hiding the contents in subfolders by creating overlapping volumes) probably works as expected but no guarantees
|
||||
|
||||
and some minor issues,
|
||||
* files are not [indexed](#file-indexing) when uploaded through smb; please [schedule rescans](#periodic-rescan) as a workaround
|
||||
* hot-reload of server config (`/?reload=cfg`) only works for volumes, not account passwords
|
||||
* listens on the first `-i` interface only (default = 0.0.0.0 = all)
|
||||
* login doesn't work on winxp, but anonymous access is ok -- remove all accounts from copyparty config for that to work
|
||||
* python3 only
|
||||
* slow
|
||||
|
||||
known client bugs:
|
||||
* on win7 only, `--smb1` is much faster than smb2 (default) because it keeps rescanning folders on smb2, however win10 onwards does not have smb1
|
||||
* windows cannot access folders which contain filenames with invalid unicode or forbidden characters (`<>:"/\|?*`), or names ending with `.`
|
||||
|
||||
the smb protocol listens on TCP port 445, which is a privileged port on linux and macos, which would require running copyparty as root. However, this can be avoided by listening on another port using `--smb-port 3945` and then using NAT to forward the traffic from 445 to there;
|
||||
* on linux: `iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 445 -j REDIRECT --to-port 3945`
|
||||
|
||||
authenticate with one of the following:
|
||||
* username `$username`, password `$password`
|
||||
* username `$password`, password blank
|
||||
|
||||
|
||||
## file indexing
|
||||
|
||||
enables dedup and music search ++
|
||||
|
@ -1317,6 +1353,9 @@ enable [thumbnails](#thumbnails) of...
|
|||
* **AVIF pictures:** `pyvips` or `ffmpeg` or `pillow-avif-plugin`
|
||||
* **JPEG XL pictures:** `pyvips` or `ffmpeg`
|
||||
|
||||
enable [smb](#smb-server) support:
|
||||
* `impacket==0.10.0`
|
||||
|
||||
`pyvips` gives higher quality thumbnails than `Pillow` and is 320% faster, using 270% more ram: `sudo apt install libvips42 && python3 -m pip install --user -U pyvips`
|
||||
|
||||
|
||||
|
|
|
@ -633,11 +633,18 @@ def run_argparse(argv: list[str], formatter: Any, retry: bool) -> argparse.Names
|
|||
ap2.add_argument("--ftp-pr", metavar="P-P", type=u, help="the range of TCP ports to use for passive connections, for example \033[32m12000-13000")
|
||||
|
||||
ap2 = ap.add_argument_group('WebDAV options')
|
||||
ap2.add_argument("--dav", action="store_true", help="enable webdav; read-only even if user has write-access")
|
||||
ap2.add_argument("--dav", action="store_true", help="enable webdav with limited write-support (most clients will fail to overwrite files)")
|
||||
ap2.add_argument("--daw", action="store_true", help="enable full write support. \033[1;31mNB!\033[0m This has side-effects -- PUT-operations will now \033[1;31mOVERWRITE\033[0m existing files, rather than inventing new filenames to avoid loss of data. You might want to instead set this as a volflag where needed. By not setting this flag, uploaded files can get written to a filename which the client does not expect (which might be okay, depending on client)")
|
||||
ap2.add_argument("--dav-nr", action="store_true", help="reject depth:infinite requests (recursive file listing); breaks spec compliance and some clients, which might be a good thing since depth:infinite is extremely server-heavy")
|
||||
ap2.add_argument("--dav-mac", action="store_true", help="disable apple-garbage filter -- allow macos to create junk files (._* and .DS_Store, .Spotlight-*, .fseventsd, .Trashes, .AppleDouble, __MACOS)")
|
||||
|
||||
ap2 = ap.add_argument_group('SMB/CIFS options')
|
||||
ap2.add_argument("--smb", action="store_true", help="enable smb (read-only) -- this requires running copyparty as root on linux and macos unless --smb-port")
|
||||
ap2.add_argument("--smbw", action="store_true", help="enable write support (please dont)")
|
||||
ap2.add_argument("--smb1", action="store_true", help="disable SMBv2, only enable SMBv1 (CIFS)")
|
||||
ap2.add_argument("--smb-port", metavar="PORT", type=int, default=445, help="port to listen on -- if you change this value, you must NAT from TCP:445 to this port using iptables or similar")
|
||||
ap2.add_argument("--smb-dbg", action="store_true", help="show debug messages")
|
||||
|
||||
ap2 = ap.add_argument_group('opt-outs')
|
||||
ap2.add_argument("-nw", action="store_true", help="never write anything to disk (debug/benchmark)")
|
||||
ap2.add_argument("--keep-qem", action="store_true", help="do not disable quick-edit-mode on windows (it is disabled to avoid accidental text selection which will deadlock copyparty)")
|
||||
|
@ -949,6 +956,10 @@ def main(argv: Optional[list[str]] = None) -> None:
|
|||
+ " (if you crash with codec errors then that is why)"
|
||||
)
|
||||
|
||||
if PY2 and al.smb:
|
||||
print("error: python2 cannot --smb")
|
||||
return
|
||||
|
||||
if sys.version_info < (3, 6):
|
||||
al.no_scandir = True
|
||||
|
||||
|
|
|
@ -592,7 +592,7 @@ class VFS(object):
|
|||
# if single folder: the folder itself is the top-level item
|
||||
folder = "" if flt or not wrap else (vrem.split("/")[-1] or "top")
|
||||
|
||||
g = self.walk(folder, vrem, [], uname, [[True]], dots, scandir, False)
|
||||
g = self.walk(folder, vrem, [], uname, [[True, False]], dots, scandir, False)
|
||||
for _, _, vpath, apath, files, rd, vd in g:
|
||||
if flt:
|
||||
files = [x for x in files if x[0] in flt]
|
||||
|
@ -1370,7 +1370,7 @@ class AuthSrv(object):
|
|||
"",
|
||||
[],
|
||||
u,
|
||||
[[True]],
|
||||
[[True, False]],
|
||||
True,
|
||||
not self.args.no_scandir,
|
||||
False,
|
||||
|
|
|
@ -7,7 +7,7 @@ from ..util import SYMTIME, fsdec, fsenc
|
|||
from . import path
|
||||
|
||||
try:
|
||||
from typing import Optional
|
||||
from typing import Any, Optional
|
||||
except:
|
||||
pass
|
||||
|
||||
|
@ -38,6 +38,10 @@ def mkdir(p: str, mode: int = 0o755) -> None:
|
|||
return os.mkdir(fsenc(p), mode)
|
||||
|
||||
|
||||
def open(p: str, *a, **ka) -> Any:
|
||||
return os.open(fsenc(p), *a, **ka)
|
||||
|
||||
|
||||
def rename(src: str, dst: str) -> None:
|
||||
return os.rename(fsenc(src), fsenc(dst))
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import sys
|
||||
import importlib
|
||||
import sys
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from .__init__ import PY2
|
||||
|
||||
|
||||
try:
|
||||
from typing import Any, Optional
|
||||
except:
|
||||
|
|
|
@ -175,7 +175,10 @@ class FtpFs(AbstractedFS):
|
|||
vfs, rem = self.hub.asrv.vfs.get(vpath, self.uname, True, False)
|
||||
|
||||
fsroot, vfs_ls1, vfs_virt = vfs.ls(
|
||||
rem, self.uname, not self.args.no_scandir, [[True], [False, True]]
|
||||
rem,
|
||||
self.uname,
|
||||
not self.args.no_scandir,
|
||||
[[True, False], [False, True]],
|
||||
)
|
||||
vfs_ls = [x[0] for x in vfs_ls1]
|
||||
vfs_ls.extend(vfs_virt.keys())
|
||||
|
|
|
@ -754,7 +754,7 @@ class HttpCli(object):
|
|||
|
||||
elif depth == "1":
|
||||
_, vfs_ls, vfs_virt = vn.ls(
|
||||
rem, self.uname, not self.args.no_scandir, [[True]]
|
||||
rem, self.uname, not self.args.no_scandir, [[True, False]]
|
||||
)
|
||||
zi = int(time.time())
|
||||
zsr = os.stat_result((16877, -1, -1, 1, 1000, 1000, 8, zi, zi, zi))
|
||||
|
@ -844,8 +844,8 @@ class HttpCli(object):
|
|||
self.log("{} tried to proppatch [{}]".format(self.uname, self.vpath))
|
||||
raise Pebkac(401, "authenticate")
|
||||
|
||||
from .dxml import parse_xml, mkenod, mktnod
|
||||
from xml.etree import ElementTree as ET
|
||||
from .dxml import mkenod, mktnod, parse_xml
|
||||
|
||||
vn, rem = self.asrv.vfs.get(self.vpath, self.uname, False, False)
|
||||
# abspath = vn.dcanonical(rem)
|
||||
|
@ -901,8 +901,8 @@ class HttpCli(object):
|
|||
self.log("{} tried to lock [{}]".format(self.uname, self.vpath))
|
||||
raise Pebkac(401, "authenticate")
|
||||
|
||||
from .dxml import parse_xml, mkenod, mktnod
|
||||
from xml.etree import ElementTree as ET
|
||||
from .dxml import mkenod, mktnod, parse_xml
|
||||
|
||||
vn, rem = self.asrv.vfs.get(self.vpath, self.uname, False, False)
|
||||
abspath = vn.dcanonical(rem)
|
||||
|
@ -2694,7 +2694,10 @@ class HttpCli(object):
|
|||
try:
|
||||
vn, rem = self.asrv.vfs.get(top, self.uname, True, False)
|
||||
fsroot, vfs_ls, vfs_virt = vn.ls(
|
||||
rem, self.uname, not self.args.no_scandir, [[True], [False, True]]
|
||||
rem,
|
||||
self.uname,
|
||||
not self.args.no_scandir,
|
||||
[[True, False], [False, True]],
|
||||
)
|
||||
except:
|
||||
vfs_ls = []
|
||||
|
@ -3103,7 +3106,7 @@ class HttpCli(object):
|
|||
return self.tx_zip(k, v, vn, rem, [], self.args.ed)
|
||||
|
||||
fsroot, vfs_ls, vfs_virt = vn.ls(
|
||||
rem, self.uname, not self.args.no_scandir, [[True], [False, True]]
|
||||
rem, self.uname, not self.args.no_scandir, [[True, False], [False, True]]
|
||||
)
|
||||
stats = {k: v for k, v in vfs_ls}
|
||||
ls_names = [x[0] for x in vfs_ls]
|
||||
|
|
|
@ -32,11 +32,11 @@ from .__init__ import MACOS, TYPE_CHECKING, EnvParams
|
|||
from .bos import bos
|
||||
from .httpconn import HttpConn
|
||||
from .util import (
|
||||
E_SCK,
|
||||
FHC,
|
||||
Daemon,
|
||||
Garda,
|
||||
Magician,
|
||||
E_SCK,
|
||||
min_ex,
|
||||
shut_socket,
|
||||
spack,
|
||||
|
|
215
copyparty/smbd.py
Normal file
215
copyparty/smbd.py
Normal file
|
@ -0,0 +1,215 @@
|
|||
# coding: utf-8
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import inspect
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
import stat
|
||||
import sys
|
||||
import time
|
||||
from types import SimpleNamespace
|
||||
|
||||
from .__init__ import ANYWIN, TYPE_CHECKING
|
||||
from .authsrv import LEELOO_DALLAS, VFS
|
||||
from .util import Daemon, min_ex
|
||||
from .bos import bos
|
||||
|
||||
try:
|
||||
from typing import Any
|
||||
except:
|
||||
pass
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .svchub import SvcHub
|
||||
|
||||
|
||||
class Standin(object):
|
||||
pass
|
||||
|
||||
|
||||
class HLog(logging.Handler):
|
||||
def __init__(self, log_func: Any) -> None:
|
||||
logging.Handler.__init__(self)
|
||||
self.log_func = log_func
|
||||
|
||||
def __repr__(self) -> str:
|
||||
level = logging.getLevelName(self.level)
|
||||
return "<%s cpp(%s)>" % (self.__class__.__name__, level)
|
||||
|
||||
def flush(self) -> None:
|
||||
pass
|
||||
|
||||
def emit(self, record: logging.LogRecord) -> None:
|
||||
msg = self.format(record)
|
||||
self.log_func("smb", msg)
|
||||
|
||||
|
||||
class SMB(object):
|
||||
def __init__(self, hub: "SvcHub") -> None:
|
||||
self.hub = hub
|
||||
self.args = hub.args
|
||||
self.asrv = hub.asrv
|
||||
self.log_func = hub.log
|
||||
|
||||
handler = HLog(hub.log)
|
||||
lvl = logging.DEBUG if self.args.smb_dbg else logging.INFO
|
||||
logging.getLogger().addHandler(handler)
|
||||
logging.getLogger().setLevel(lvl)
|
||||
|
||||
try:
|
||||
from impacket import smbserver
|
||||
from impacket.ntlm import compute_lmhash, compute_nthash
|
||||
except ImportError:
|
||||
m = "\033[36m\n{}\033[31m\n\nERROR: need 'impacket'; please run this command:\033[33m\n {} -m pip install --user impacket\n\033[0m"
|
||||
print(m.format(min_ex(), sys.executable))
|
||||
sys.exit(1)
|
||||
|
||||
# patch vfs into smbserver.os
|
||||
fos = SimpleNamespace()
|
||||
for k in os.__dict__:
|
||||
try:
|
||||
setattr(fos, k, getattr(os, k))
|
||||
except:
|
||||
pass
|
||||
fos.listdir = self._listdir
|
||||
fos.open = self._open
|
||||
fos.stat = self._stat
|
||||
smbserver.os = fos
|
||||
|
||||
# ...and smbserver.os.path
|
||||
fop = SimpleNamespace()
|
||||
for k in os.path.__dict__:
|
||||
try:
|
||||
setattr(fop, k, getattr(os.path, k))
|
||||
except:
|
||||
pass
|
||||
fop.exists = self._p_exists
|
||||
fop.getsize = self._p_getsize
|
||||
fop.isdir = self._p_isdir
|
||||
smbserver.os.path = fop
|
||||
|
||||
# other patches
|
||||
smbserver.isInFileJail = self._is_in_file_jail
|
||||
self._disarm()
|
||||
|
||||
ip = self.args.i[0]
|
||||
port = int(self.args.smb_port)
|
||||
srv = smbserver.SimpleSMBServer(listenAddress=ip, listenPort=port)
|
||||
|
||||
ro = "no" if self.args.smbw else "yes" # (does nothing)
|
||||
srv.addShare("A", "/", readOnly=ro)
|
||||
srv.setSMB2Support(not self.args.smb1)
|
||||
|
||||
for name, pwd in self.asrv.acct.items():
|
||||
for u, p in ((name, pwd), (pwd, "")):
|
||||
lmhash = compute_lmhash(p)
|
||||
nthash = compute_nthash(p)
|
||||
srv.addCredential(u, 0, lmhash, nthash)
|
||||
|
||||
chi = [random.randint(0, 255) for x in range(8)]
|
||||
cha = "".join(["{:02x}".format(x) for x in chi])
|
||||
srv.setSMBChallenge(cha)
|
||||
|
||||
self.srv = srv
|
||||
self.stop = srv.stop
|
||||
logging.info("listening @ %s:%s", ip, port)
|
||||
|
||||
def start(self) -> None:
|
||||
Daemon(self.srv.start)
|
||||
|
||||
def _v2a(self, caller: str, vpath: str, *a: Any) -> tuple[VFS, str]:
|
||||
vpath = vpath.replace("\\", "/").lstrip("/")
|
||||
# cf = inspect.currentframe().f_back
|
||||
# c1 = cf.f_back.f_code.co_name
|
||||
# c2 = cf.f_code.co_name
|
||||
logging.debug('%s("%s", %s)\033[K\033[0m', caller, vpath, str(a))
|
||||
|
||||
# TODO find a way to grab `identity` in smbComSessionSetupAndX and smb2SessionSetup
|
||||
vfs, rem = self.asrv.vfs.get(vpath, LEELOO_DALLAS, True, True)
|
||||
return vfs, vfs.canonical(rem)
|
||||
|
||||
def _listdir(self, vpath: str, *a: Any, **ka: Any) -> list[str]:
|
||||
vpath = vpath.replace("\\", "/").lstrip("/")
|
||||
# caller = inspect.currentframe().f_back.f_code.co_name
|
||||
logging.info('listdir("%s", %s)\033[K\033[0m', vpath, str(a))
|
||||
vfs, rem = self.asrv.vfs.get(vpath, LEELOO_DALLAS, False, False)
|
||||
_, vfs_ls, vfs_virt = vfs.ls(
|
||||
rem, LEELOO_DALLAS, not self.args.no_scandir, [[False, False]]
|
||||
)
|
||||
ls = [x[0] for x in vfs_ls]
|
||||
ls.extend(vfs_virt.keys())
|
||||
return ls
|
||||
|
||||
def _open(
|
||||
self, vpath: str, flags: int, chmod: int = 0o777, *a: Any, **ka: Any
|
||||
) -> Any:
|
||||
if not self.args.smbw:
|
||||
ok = os.O_RDONLY
|
||||
if ANYWIN:
|
||||
ok |= os.O_BINARY
|
||||
|
||||
if flags != ok:
|
||||
logging.info("blocked write to %s", vpath)
|
||||
raise Exception("read-only")
|
||||
|
||||
return bos.open(self._v2a("open", vpath, *a)[1], flags, chmod, *a, **ka)
|
||||
|
||||
def _stat(self, vpath: str, *a: Any, **ka: Any) -> os.stat_result:
|
||||
return bos.stat(self._v2a("stat", vpath, *a)[1], *a, **ka)
|
||||
|
||||
def _p_exists(self, vpath: str) -> bool:
|
||||
try:
|
||||
bos.stat(self._v2a("p.exists", vpath)[1])
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def _p_getsize(self, vpath: str) -> int:
|
||||
st = bos.stat(self._v2a("p.getsize", vpath)[1])
|
||||
return st.st_size
|
||||
|
||||
def _p_isdir(self, vpath: str) -> bool:
|
||||
try:
|
||||
st = bos.stat(self._v2a("p.isdir", vpath)[1])
|
||||
return stat.S_ISDIR(st.st_mode)
|
||||
except:
|
||||
return False
|
||||
|
||||
def _hook(self, *a: Any, **ka: Any) -> None:
|
||||
src = inspect.currentframe().f_back.f_code.co_name
|
||||
logging.error("\033[31m%s:hook(%s)\033[0m", src, a)
|
||||
raise Exception("nope")
|
||||
|
||||
def _disarm(self) -> None:
|
||||
from impacket import smbserver
|
||||
|
||||
smbserver.os.chmod = self._hook
|
||||
smbserver.os.chown = self._hook
|
||||
smbserver.os.ftruncate = self._hook
|
||||
smbserver.os.lchown = self._hook
|
||||
smbserver.os.link = self._hook
|
||||
smbserver.os.lstat = self._hook
|
||||
smbserver.os.mkdir = self._hook
|
||||
smbserver.os.remove = self._hook
|
||||
smbserver.os.rename = self._hook
|
||||
smbserver.os.replace = self._hook
|
||||
smbserver.os.scandir = self._hook
|
||||
smbserver.os.symlink = self._hook
|
||||
smbserver.os.truncate = self._hook
|
||||
smbserver.os.unlink = self._hook
|
||||
smbserver.os.walk = self._hook
|
||||
|
||||
smbserver.os.path.abspath = self._hook
|
||||
smbserver.os.path.expanduser = self._hook
|
||||
smbserver.os.path.getatime = self._hook
|
||||
smbserver.os.path.getctime = self._hook
|
||||
smbserver.os.path.getmtime = self._hook
|
||||
smbserver.os.path.isabs = self._hook
|
||||
smbserver.os.path.isfile = self._hook
|
||||
smbserver.os.path.islink = self._hook
|
||||
smbserver.os.path.realpath = self._hook
|
||||
|
||||
def _is_in_file_jail(self, *a: Any) -> bool:
|
||||
# handled by vfs
|
||||
return True
|
|
@ -186,6 +186,17 @@ class SvcHub(object):
|
|||
|
||||
self.ftpd = Ftpd(self)
|
||||
|
||||
if args.smb:
|
||||
# impacket.dcerpc is noisy about listen timeouts
|
||||
sto = socket.getdefaulttimeout()
|
||||
socket.setdefaulttimeout(None)
|
||||
|
||||
from .smbd import SMB
|
||||
|
||||
self.smbd = SMB(self)
|
||||
socket.setdefaulttimeout(sto)
|
||||
self.smbd.start()
|
||||
|
||||
# decide which worker impl to use
|
||||
if self.check_mp_enable():
|
||||
from .broker_mp import BrokerMp as Broker
|
||||
|
@ -342,6 +353,17 @@ class SvcHub(object):
|
|||
|
||||
self.shutdown()
|
||||
|
||||
def kill9(self, delay: float = 0.0):
|
||||
if delay > 0.01:
|
||||
time.sleep(delay)
|
||||
print("component stuck; performing sigkill")
|
||||
time.sleep(0.1)
|
||||
|
||||
if ANYWIN:
|
||||
os.system("taskkill /f /pid {}".format(os.getpid()))
|
||||
else:
|
||||
os.kill(os.getpid(), signal.SIGKILL)
|
||||
|
||||
def signal_handler(self, sig: int, frame: Optional[FrameType]) -> None:
|
||||
if self.stopping:
|
||||
if self.nsigs <= 0:
|
||||
|
@ -351,10 +373,7 @@ class SvcHub(object):
|
|||
except:
|
||||
pass
|
||||
|
||||
if ANYWIN:
|
||||
os.system("taskkill /f /pid {}".format(os.getpid()))
|
||||
else:
|
||||
os.kill(os.getpid(), signal.SIGKILL)
|
||||
self.kill9()
|
||||
else:
|
||||
self.nsigs -= 1
|
||||
return
|
||||
|
@ -395,6 +414,10 @@ class SvcHub(object):
|
|||
if n == 3:
|
||||
self.pr("waiting for thumbsrv (10sec)...")
|
||||
|
||||
if hasattr(self, "smbd"):
|
||||
Daemon(self.kill9, a=(1,))
|
||||
self.smbd.stop()
|
||||
|
||||
self.pr("nailed it", end="")
|
||||
ret = self.retcode
|
||||
except:
|
||||
|
|
|
@ -185,6 +185,9 @@ IMPLICATIONS = [
|
|||
["e2vp", "e2v"],
|
||||
["e2v", "e2d"],
|
||||
["daw", "dav"],
|
||||
["smbw", "smb"],
|
||||
["smb1", "smb"],
|
||||
["smb_dbg", "smb"],
|
||||
]
|
||||
|
||||
|
||||
|
@ -1373,7 +1376,7 @@ def gen_filekey_dbg(
|
|||
try:
|
||||
import inspect
|
||||
|
||||
ctx = ",".join(inspect.stack()[n][3] for n in range(2, 5))
|
||||
ctx = ",".join(inspect.stack()[n].function for n in range(2, 5))
|
||||
except:
|
||||
ctx = ""
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ copyparty/mtag.py,
|
|||
copyparty/res,
|
||||
copyparty/res/COPYING.txt,
|
||||
copyparty/res/insecure.pem,
|
||||
copyparty/smbd.py,
|
||||
copyparty/star.py,
|
||||
copyparty/stolen,
|
||||
copyparty/stolen/__init__.py,
|
||||
|
|
|
@ -58,7 +58,7 @@ def uh1(fp):
|
|||
lns = []
|
||||
for ln in cs.split("\n"):
|
||||
m = ptn.match(ln)
|
||||
if m:
|
||||
if m and "SimpleNamespace" not in ln:
|
||||
ln = m.group(1) + "raise Exception()"
|
||||
|
||||
lns.append(ln)
|
||||
|
|
4
setup.py
4
setup.py
|
@ -112,7 +112,10 @@ args = {
|
|||
"Programming Language :: Python :: Implementation :: PyPy",
|
||||
"Environment :: Console",
|
||||
"Environment :: No Input/Output (Daemon)",
|
||||
"Intended Audience :: End Users/Desktop",
|
||||
"Intended Audience :: System Administrators",
|
||||
"Topic :: Communications :: File Sharing",
|
||||
"Topic :: Internet :: File Transfer Protocol (FTP)",
|
||||
"Topic :: Internet :: WWW/HTTP :: HTTP Servers",
|
||||
],
|
||||
"include_package_data": True,
|
||||
|
@ -125,6 +128,7 @@ args = {
|
|||
"audiotags": ["mutagen"],
|
||||
"ftpd": ["pyftpdlib"],
|
||||
"ftps": ["pyftpdlib", "pyopenssl"],
|
||||
"smbd": ["impacket"],
|
||||
},
|
||||
"entry_points": {"console_scripts": ["copyparty = copyparty.__main__:main"]},
|
||||
"scripts": ["bin/copyparty-fuse.py", "bin/up2k.py"],
|
||||
|
|
Loading…
Reference in a new issue