Merge PR161: add --ignore-unsendable-files flag for 'wormhole send'

closes #161
closes #112
This commit is contained in:
Brian Warner 2017-05-24 12:12:12 -07:00
commit 10d2bea203
4 changed files with 65 additions and 7 deletions

View File

@ -10,7 +10,7 @@ from . import public_relay
from .. import __version__
from ..timing import DebugTiming
from ..errors import (WrongPasswordError, WelcomeError, KeyFormatError,
TransferError, NoTorError)
TransferError, NoTorError, UnsendableFileError)
from twisted.internet.defer import inlineCallbacks, maybeDeferred
from twisted.python.failure import Failure
from twisted.internet.task import react
@ -109,7 +109,7 @@ def _dispatch_command(reactor, cfg, command):
msg = fill("ERROR: " + dedent(e.__doc__))
print(msg, file=cfg.stderr)
raise SystemExit(1)
except WelcomeError as e:
except (WelcomeError, UnsendableFileError) as e:
msg = fill("ERROR: " + dedent(e.__doc__))
print(msg, file=cfg.stderr)
print(six.u(""), file=cfg.stderr)
@ -173,6 +173,10 @@ TorArgs = _compose(
"--text", default=None, metavar="MESSAGE",
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.pass_obj
def send(cfg, **kwargs):

View File

@ -6,7 +6,8 @@ from twisted.python import log
from twisted.protocols import basic
from twisted.internet import reactor
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 ..transit import TransitSender
from ..util import dict_to_bytes, bytes_to_dict, bytes_to_hexstr
@ -274,9 +275,17 @@ class Sender:
for fn in files:
archivename = os.path.join(*tuple(localpath+[fn]))
localfilename = os.path.join(path, fn)
zf.write(localfilename, archivename)
num_bytes += os.stat(localfilename).st_size
num_files += 1
try:
zf.write(localfilename, archivename)
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)
filesize = fd_to_send.tell()
fd_to_send.seek(0,0)

View File

@ -3,6 +3,16 @@ from __future__ import unicode_literals
class WormholeError(Exception):
"""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):
"""The relay server complained about something we did."""

View File

@ -12,7 +12,8 @@ from twisted.internet.defer import gatherResults, inlineCallbacks, returnValue
from .. import __version__
from .common import ServerBase, config
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.cli import server
@ -65,6 +66,40 @@ class OfferData(unittest.TestCase):
self.assertEqual(fd_to_send.tell(), 0)
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):
self.cfg.what = filename = "missing"
send_dir = self.mktemp()