make blocking/send-file work on py3
* declare transit records and handshake keys are bytes, not str * declare transit connection hints to be str * use six.moves.socketserver, six.moves.input for Verifier query * argparse "--version" writes to stderr on py2, stdout on py3 * avoid xrange(), use subprocess.Popen(universal_newlines=True)
This commit is contained in:
parent
8fe41e135d
commit
b5d470fcda
|
@ -219,15 +219,16 @@ python2, "bytes" in python3):
|
|||
|
||||
* application identifier
|
||||
* verifier string
|
||||
* data in
|
||||
* data out
|
||||
* data in/out
|
||||
* derived-key "purpose" string
|
||||
* transit records in/out
|
||||
|
||||
Some human-readable parameters are passed as strings: "str" in python2, "str"
|
||||
(i.e. unicode) in python3:
|
||||
|
||||
* wormhole code
|
||||
* relay/transit URLs
|
||||
* transit connection hints (e.g. "host:port")
|
||||
|
||||
## Detailed Example
|
||||
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
from __future__ import print_function
|
||||
import re, time, threading, socket, SocketServer
|
||||
import re, time, threading, socket
|
||||
from six.moves import socketserver
|
||||
from binascii import hexlify, unhexlify
|
||||
from nacl.secret import SecretBox
|
||||
from ..util import ipaddrs
|
||||
from ..util.hkdf import HKDF
|
||||
from ..errors import UsageError
|
||||
|
||||
class TransitError(Exception):
|
||||
pass
|
||||
|
@ -40,15 +42,15 @@ class TransitError(Exception):
|
|||
|
||||
def build_receiver_handshake(key):
|
||||
hexid = HKDF(key, 32, CTXinfo=b"transit_receiver")
|
||||
return "transit receiver %s ready\n\n" % hexlify(hexid)
|
||||
return b"transit receiver %s ready\n\n" % hexlify(hexid)
|
||||
|
||||
def build_sender_handshake(key):
|
||||
hexid = HKDF(key, 32, CTXinfo=b"transit_sender")
|
||||
return "transit sender %s ready\n\n" % hexlify(hexid)
|
||||
return b"transit sender %s ready\n\n" % hexlify(hexid)
|
||||
|
||||
def build_relay_handshake(key):
|
||||
token = HKDF(key, 32, CTXinfo=b"transit_relay_token")
|
||||
return "please relay %s\n" % hexlify(token)
|
||||
return b"please relay %s\n" % hexlify(token)
|
||||
|
||||
TIMEOUT=15
|
||||
|
||||
|
@ -62,11 +64,6 @@ TIMEOUT=15
|
|||
class BadHandshake(Exception):
|
||||
pass
|
||||
|
||||
def force_ascii(s):
|
||||
if isinstance(s, type(u"")):
|
||||
return s.encode("ascii")
|
||||
return s
|
||||
|
||||
def send_to(skt, data):
|
||||
sent = 0
|
||||
while sent < len(data):
|
||||
|
@ -87,6 +84,7 @@ def wait_for(skt, expected, description):
|
|||
# publisher wants anonymity, their only hint's ADDR will end in .onion .
|
||||
|
||||
def parse_hint_tcp(hint):
|
||||
assert isinstance(hint, str)
|
||||
# return tuple or None for an unparseable hint
|
||||
mo = re.search(r'^([a-zA-Z0-9]+):(.*)$', hint)
|
||||
if not mo:
|
||||
|
@ -187,7 +185,7 @@ def handle(skt, client_address, owner, description,
|
|||
# owner is now responsible for the socket
|
||||
owner._negotiation_finished(skt, description) # note thread
|
||||
|
||||
class MyTCPServer(SocketServer.TCPServer):
|
||||
class MyTCPServer(socketserver.TCPServer):
|
||||
allow_reuse_address = True
|
||||
|
||||
def process_request(self, request, client_address):
|
||||
|
@ -243,6 +241,7 @@ class RecordPipe:
|
|||
self.next_receive_nonce = 0
|
||||
|
||||
def send_record(self, record):
|
||||
if not isinstance(record, type(b"")): raise UsageError
|
||||
assert SecretBox.NONCE_SIZE == 24
|
||||
assert self.send_nonce < 2**(8*24)
|
||||
assert len(record) < 2**(8*4)
|
||||
|
@ -294,9 +293,9 @@ class Common:
|
|||
return [self._transit_relay]
|
||||
|
||||
def add_their_direct_hints(self, hints):
|
||||
self._their_direct_hints = [force_ascii(h) for h in hints]
|
||||
self._their_direct_hints = [str(h) for h in hints]
|
||||
def add_their_relay_hints(self, hints):
|
||||
self._their_relay_hints = [force_ascii(h) for h in hints]
|
||||
self._their_relay_hints = [str(h) for h in hints]
|
||||
|
||||
def _send_this(self):
|
||||
if self.is_sender:
|
||||
|
@ -308,7 +307,7 @@ class Common:
|
|||
if self.is_sender:
|
||||
return build_receiver_handshake(self._transit_key)
|
||||
else:
|
||||
return build_sender_handshake(self._transit_key) + "go\n"
|
||||
return build_sender_handshake(self._transit_key) + b"go\n"
|
||||
|
||||
def _sender_record_key(self):
|
||||
if self.is_sender:
|
||||
|
@ -407,11 +406,11 @@ class Common:
|
|||
|
||||
if is_winner:
|
||||
if self.is_sender:
|
||||
send_to(skt, "go\n")
|
||||
send_to(skt, b"go\n")
|
||||
self.winning.set()
|
||||
else:
|
||||
if self.is_sender:
|
||||
send_to(skt, "nevermind\n")
|
||||
send_to(skt, b"nevermind\n")
|
||||
skt.close()
|
||||
|
||||
def connect(self):
|
||||
|
|
|
@ -68,12 +68,12 @@ def receive_file(args):
|
|||
if os.path.dirname(target) != here:
|
||||
print("Error: suggested filename (%s) would be outside current directory"
|
||||
% (filename,))
|
||||
record_pipe.send_record("bad filename\n")
|
||||
record_pipe.send_record(b"bad filename\n")
|
||||
record_pipe.close()
|
||||
return 1
|
||||
if os.path.exists(target) and not args.overwrite:
|
||||
print("Error: refusing to overwrite existing file %s" % (filename,))
|
||||
record_pipe.send_record("file already exists\n")
|
||||
record_pipe.send_record(b"file already exists\n")
|
||||
record_pipe.close()
|
||||
return 1
|
||||
tmp = target + ".tmp"
|
||||
|
@ -98,6 +98,6 @@ def receive_file(args):
|
|||
os.rename(tmp, target)
|
||||
|
||||
print("Received file written to %s" % target)
|
||||
record_pipe.send_record("ok\n")
|
||||
record_pipe.send_record(b"ok\n")
|
||||
record_pipe.close()
|
||||
return 0
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from __future__ import print_function
|
||||
import os, sys, json, binascii
|
||||
import os, sys, json, binascii, six
|
||||
from ..errors import handle_server_error
|
||||
|
||||
APPID = b"lothar.com/wormhole/file-xfer"
|
||||
|
@ -37,7 +37,7 @@ def send_file(args):
|
|||
if args.verify:
|
||||
verifier = binascii.hexlify(w.get_verifier())
|
||||
while True:
|
||||
ok = raw_input("Verifier %s. ok? (yes/no): " % verifier)
|
||||
ok = six.moves.input("Verifier %s. ok? (yes/no): " % verifier)
|
||||
if ok.lower() == "yes":
|
||||
break
|
||||
if ok.lower() == "no":
|
||||
|
@ -91,7 +91,7 @@ def send_file(args):
|
|||
|
||||
print("File sent.. waiting for confirmation")
|
||||
ack = record_pipe.receive_record()
|
||||
if ack == "ok\n":
|
||||
if ack == b"ok\n":
|
||||
print("Confirmation received. Transfer complete.")
|
||||
return 0
|
||||
else:
|
||||
|
|
|
@ -55,12 +55,21 @@ class ScriptVersion(ServerBase, ScriptsBase, unittest.TestCase):
|
|||
d = getProcessOutputAndValue(wormhole, ["--version"])
|
||||
def _check(res):
|
||||
out, err, rc = res
|
||||
self.failUnlessEqual(out, "")
|
||||
# argparse on py2 sends --version to stderr
|
||||
# argparse on py3 sends --version to stdout
|
||||
# aargh
|
||||
out = out.decode("utf-8")
|
||||
err = err.decode("utf-8")
|
||||
if "DistributionNotFound" in err:
|
||||
log.msg("stderr was %s" % err)
|
||||
last = err.strip().split("\n")[-1]
|
||||
self.fail("wormhole not runnable: %s" % last)
|
||||
self.failUnlessEqual(err, "magic-wormhole %s\n" % __version__)
|
||||
if sys.version_info[0] == 2:
|
||||
self.failUnlessEqual(out, "")
|
||||
self.failUnlessEqual(err, "magic-wormhole %s\n" % __version__)
|
||||
else:
|
||||
self.failUnlessEqual(err, "")
|
||||
self.failUnlessEqual(out, "magic-wormhole %s\n" % __version__)
|
||||
self.failUnlessEqual(rc, 0)
|
||||
d.addCallback(_check)
|
||||
return d
|
||||
|
@ -92,6 +101,8 @@ class Scripts(ServerBase, ScriptsBase, unittest.TestCase):
|
|||
d2 = getProcessOutputAndValue(wormhole, receive_args)
|
||||
def _check_sender(res):
|
||||
out, err, rc = res
|
||||
out = out.decode("utf-8")
|
||||
err = err.decode("utf-8")
|
||||
self.failUnlessEqual(out,
|
||||
"On the other computer, please run: "
|
||||
"wormhole receive-text\n"
|
||||
|
@ -104,6 +115,8 @@ class Scripts(ServerBase, ScriptsBase, unittest.TestCase):
|
|||
d1.addCallback(_check_sender)
|
||||
def _check_receiver(res):
|
||||
out, err, rc = res
|
||||
out = out.decode("utf-8")
|
||||
err = err.decode("utf-8")
|
||||
self.failUnlessEqual(out, message+"\n")
|
||||
self.failUnlessEqual(err, "")
|
||||
self.failUnlessEqual(rc, 0)
|
||||
|
@ -137,6 +150,8 @@ class Scripts(ServerBase, ScriptsBase, unittest.TestCase):
|
|||
d2 = getProcessOutputAndValue(wormhole, receive_args, path=receive_dir)
|
||||
def _check_sender(res):
|
||||
out, err, rc = res
|
||||
out = out.decode("utf-8")
|
||||
err = err.decode("utf-8")
|
||||
self.failUnlessIn("On the other computer, please run: "
|
||||
"wormhole receive-file\n"
|
||||
"Wormhole code is '%s'\n\n" % code,
|
||||
|
@ -150,6 +165,8 @@ class Scripts(ServerBase, ScriptsBase, unittest.TestCase):
|
|||
d1.addCallback(_check_sender)
|
||||
def _check_receiver(res):
|
||||
out, err, rc = res
|
||||
out = out.decode("utf-8")
|
||||
err = err.decode("utf-8")
|
||||
self.failUnlessIn("Receiving %d bytes for 'testfile'" % len(message),
|
||||
out)
|
||||
self.failUnlessIn("Received file written to ", out)
|
||||
|
|
|
@ -32,7 +32,7 @@ def find_addresses():
|
|||
commands = _unix_commands
|
||||
|
||||
for (pathtotool, args, regex) in commands:
|
||||
assert os.path.isabs(pathtotool)
|
||||
assert os.path.isabs(pathtotool), pathtotool
|
||||
if not os.path.isfile(pathtotool):
|
||||
continue
|
||||
try:
|
||||
|
@ -46,12 +46,13 @@ def find_addresses():
|
|||
def _query(path, args, regex):
|
||||
env = {'LANG': 'en_US.UTF-8'}
|
||||
TRIES = 5
|
||||
for trial in xrange(TRIES):
|
||||
for trial in range(TRIES):
|
||||
try:
|
||||
p = subprocess.Popen([path] + list(args),
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
env=env)
|
||||
env=env,
|
||||
universal_newlines=True)
|
||||
(output, err) = p.communicate()
|
||||
break
|
||||
except OSError as e:
|
||||
|
|
Loading…
Reference in New Issue
Block a user