Merge PR161: add --ignore-unsendable-files flag for 'wormhole send'
closes #161 closes #112
This commit is contained in:
commit
10d2bea203
|
@ -10,7 +10,7 @@ from . import public_relay
|
||||||
from .. import __version__
|
from .. import __version__
|
||||||
from ..timing import DebugTiming
|
from ..timing import DebugTiming
|
||||||
from ..errors import (WrongPasswordError, WelcomeError, KeyFormatError,
|
from ..errors import (WrongPasswordError, WelcomeError, KeyFormatError,
|
||||||
TransferError, NoTorError)
|
TransferError, NoTorError, UnsendableFileError)
|
||||||
from twisted.internet.defer import inlineCallbacks, maybeDeferred
|
from twisted.internet.defer import inlineCallbacks, maybeDeferred
|
||||||
from twisted.python.failure import Failure
|
from twisted.python.failure import Failure
|
||||||
from twisted.internet.task import react
|
from twisted.internet.task import react
|
||||||
|
@ -109,7 +109,7 @@ def _dispatch_command(reactor, cfg, command):
|
||||||
msg = fill("ERROR: " + dedent(e.__doc__))
|
msg = fill("ERROR: " + dedent(e.__doc__))
|
||||||
print(msg, file=cfg.stderr)
|
print(msg, file=cfg.stderr)
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
except WelcomeError as e:
|
except (WelcomeError, UnsendableFileError) as e:
|
||||||
msg = fill("ERROR: " + dedent(e.__doc__))
|
msg = fill("ERROR: " + dedent(e.__doc__))
|
||||||
print(msg, file=cfg.stderr)
|
print(msg, file=cfg.stderr)
|
||||||
print(six.u(""), file=cfg.stderr)
|
print(six.u(""), file=cfg.stderr)
|
||||||
|
@ -173,6 +173,10 @@ TorArgs = _compose(
|
||||||
"--text", default=None, metavar="MESSAGE",
|
"--text", default=None, metavar="MESSAGE",
|
||||||
help="text message to send, instead of a file. Use '-' to read from stdin.",
|
help="text message to send, instead of a file. Use '-' to read from stdin.",
|
||||||
)
|
)
|
||||||
|
@click.option(
|
||||||
|
"--ignore-unsendable-files", default=False, is_flag=True,
|
||||||
|
help="Don't raise an error if a file can't be read."
|
||||||
|
)
|
||||||
@click.argument("what", required=False)
|
@click.argument("what", required=False)
|
||||||
@click.pass_obj
|
@click.pass_obj
|
||||||
def send(cfg, **kwargs):
|
def send(cfg, **kwargs):
|
||||||
|
|
|
@ -6,7 +6,8 @@ from twisted.python import log
|
||||||
from twisted.protocols import basic
|
from twisted.protocols import basic
|
||||||
from twisted.internet import reactor
|
from twisted.internet import reactor
|
||||||
from twisted.internet.defer import inlineCallbacks, returnValue
|
from twisted.internet.defer import inlineCallbacks, returnValue
|
||||||
from ..errors import TransferError, WormholeClosedError, NoTorError
|
from ..errors import (TransferError, WormholeClosedError, NoTorError,
|
||||||
|
UnsendableFileError)
|
||||||
from wormhole import create, __version__
|
from wormhole import create, __version__
|
||||||
from ..transit import TransitSender
|
from ..transit import TransitSender
|
||||||
from ..util import dict_to_bytes, bytes_to_dict, bytes_to_hexstr
|
from ..util import dict_to_bytes, bytes_to_dict, bytes_to_hexstr
|
||||||
|
@ -274,9 +275,17 @@ class Sender:
|
||||||
for fn in files:
|
for fn in files:
|
||||||
archivename = os.path.join(*tuple(localpath+[fn]))
|
archivename = os.path.join(*tuple(localpath+[fn]))
|
||||||
localfilename = os.path.join(path, fn)
|
localfilename = os.path.join(path, fn)
|
||||||
zf.write(localfilename, archivename)
|
try:
|
||||||
num_bytes += os.stat(localfilename).st_size
|
zf.write(localfilename, archivename)
|
||||||
num_files += 1
|
num_bytes += os.stat(localfilename).st_size
|
||||||
|
num_files += 1
|
||||||
|
except OSError as e:
|
||||||
|
errmsg = u"{}: {}".format(fn, e.strerror)
|
||||||
|
if self._args.ignore_unsendable_files:
|
||||||
|
print(u"{} (ignoring error)".format(errmsg),
|
||||||
|
file=args.stderr)
|
||||||
|
else:
|
||||||
|
raise UnsendableFileError(errmsg)
|
||||||
fd_to_send.seek(0,2)
|
fd_to_send.seek(0,2)
|
||||||
filesize = fd_to_send.tell()
|
filesize = fd_to_send.tell()
|
||||||
fd_to_send.seek(0,0)
|
fd_to_send.seek(0,0)
|
||||||
|
|
|
@ -3,6 +3,16 @@ from __future__ import unicode_literals
|
||||||
class WormholeError(Exception):
|
class WormholeError(Exception):
|
||||||
"""Parent class for all wormhole-related errors"""
|
"""Parent class for all wormhole-related errors"""
|
||||||
|
|
||||||
|
class UnsendableFileError(Exception):
|
||||||
|
"""
|
||||||
|
A file you wanted to send couldn't be read, maybe because it's not
|
||||||
|
a file, or because it's a symlink that points to something
|
||||||
|
that doesn't exist.
|
||||||
|
|
||||||
|
To ignore this kind of error, you can run wormhole with the
|
||||||
|
--ignore-unsendable-files flag.
|
||||||
|
"""
|
||||||
|
|
||||||
class ServerError(WormholeError):
|
class ServerError(WormholeError):
|
||||||
"""The relay server complained about something we did."""
|
"""The relay server complained about something we did."""
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,8 @@ from twisted.internet.defer import gatherResults, inlineCallbacks, returnValue
|
||||||
from .. import __version__
|
from .. import __version__
|
||||||
from .common import ServerBase, config
|
from .common import ServerBase, config
|
||||||
from ..cli import cmd_send, cmd_receive, welcome, cli
|
from ..cli import cmd_send, cmd_receive, welcome, cli
|
||||||
from ..errors import TransferError, WrongPasswordError, WelcomeError
|
from ..errors import (TransferError, WrongPasswordError, WelcomeError,
|
||||||
|
UnsendableFileError)
|
||||||
from wormhole.server.cmd_server import MyPlugin
|
from wormhole.server.cmd_server import MyPlugin
|
||||||
from wormhole.server.cli import server
|
from wormhole.server.cli import server
|
||||||
|
|
||||||
|
@ -65,6 +66,40 @@ class OfferData(unittest.TestCase):
|
||||||
self.assertEqual(fd_to_send.tell(), 0)
|
self.assertEqual(fd_to_send.tell(), 0)
|
||||||
self.assertEqual(fd_to_send.read(), message)
|
self.assertEqual(fd_to_send.read(), message)
|
||||||
|
|
||||||
|
def _create_broken_symlink(self):
|
||||||
|
if not hasattr(os, 'symlink'):
|
||||||
|
raise unittest.SkipTest("host OS does not support symlinks")
|
||||||
|
|
||||||
|
parent_dir = self.mktemp()
|
||||||
|
os.mkdir(parent_dir)
|
||||||
|
send_dir = "dirname"
|
||||||
|
os.mkdir(os.path.join(parent_dir, send_dir))
|
||||||
|
os.symlink('/non/existent/file',
|
||||||
|
os.path.join(parent_dir, send_dir, 'linky'))
|
||||||
|
|
||||||
|
send_dir_arg = send_dir
|
||||||
|
self.cfg.what = send_dir_arg
|
||||||
|
self.cfg.cwd = parent_dir
|
||||||
|
|
||||||
|
def test_broken_symlink_raises_err(self):
|
||||||
|
self._create_broken_symlink()
|
||||||
|
self.cfg.ignore_unsendable_files = False
|
||||||
|
e = self.assertRaises(UnsendableFileError, build_offer, self.cfg)
|
||||||
|
|
||||||
|
# On english distributions of Linux, this will be
|
||||||
|
# "linky: No such file or directory", but the error may be
|
||||||
|
# different on Windows and other locales and/or Unix variants, so
|
||||||
|
# we'll just assert the part we know about.
|
||||||
|
self.assertIn("linky: ", str(e))
|
||||||
|
|
||||||
|
def test_broken_symlink_is_ignored(self):
|
||||||
|
self._create_broken_symlink()
|
||||||
|
self.cfg.ignore_unsendable_files = True
|
||||||
|
d, fd_to_send = build_offer(self.cfg)
|
||||||
|
self.assertIn('(ignoring error)', self.cfg.stderr.getvalue())
|
||||||
|
self.assertEqual(d['directory']['numfiles'], 0)
|
||||||
|
self.assertEqual(d['directory']['numbytes'], 0)
|
||||||
|
|
||||||
def test_missing_file(self):
|
def test_missing_file(self):
|
||||||
self.cfg.what = filename = "missing"
|
self.cfg.what = filename = "missing"
|
||||||
send_dir = self.mktemp()
|
send_dir = self.mktemp()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user