2016-02-28 10:14:21 +00:00
|
|
|
from __future__ import print_function
|
|
|
|
import io, json
|
2016-03-02 08:51:21 +00:00
|
|
|
from twisted.internet import reactor, defer
|
2016-02-28 10:14:21 +00:00
|
|
|
from twisted.internet.defer import inlineCallbacks, returnValue
|
|
|
|
from ..twisted.transcribe import Wormhole, WrongPasswordError
|
2016-03-02 08:41:33 +00:00
|
|
|
from ..twisted.transit import TransitReceiver
|
2016-02-28 10:14:21 +00:00
|
|
|
from .cmd_receive_blocking import BlockingReceiver, RespondError, APPID
|
|
|
|
from ..errors import TransferError
|
|
|
|
from .progress import ProgressPrinter
|
|
|
|
|
2016-03-02 08:51:21 +00:00
|
|
|
def receive_twisted_sync(args):
|
|
|
|
# try to use twisted.internet.task.react(f) here (but it calls sys.exit
|
|
|
|
# directly)
|
|
|
|
d = defer.Deferred()
|
|
|
|
# don't call receive_twisted() until after the reactor is running, so
|
|
|
|
# that if it raises an exception synchronously, we won't stop the reactor
|
|
|
|
# before it starts
|
|
|
|
reactor.callLater(0, d.callback, None)
|
|
|
|
d.addCallback(lambda _: receive_twisted(args))
|
|
|
|
rc = []
|
|
|
|
def _done(res):
|
|
|
|
rc.extend([True, res])
|
|
|
|
reactor.stop()
|
|
|
|
def _err(f):
|
2016-03-03 20:22:25 +00:00
|
|
|
rc.extend([False, f])
|
2016-03-02 08:51:21 +00:00
|
|
|
reactor.stop()
|
|
|
|
d.addCallbacks(_done, _err)
|
|
|
|
reactor.run()
|
|
|
|
if rc[0]:
|
|
|
|
return rc[1]
|
2016-03-03 20:22:25 +00:00
|
|
|
print(str(rc[1]))
|
|
|
|
rc[1].raiseException()
|
2016-03-02 08:51:21 +00:00
|
|
|
|
2016-02-28 10:14:21 +00:00
|
|
|
def receive_twisted(args):
|
|
|
|
return TwistedReceiver(args).go()
|
|
|
|
|
|
|
|
class TwistedReceiver(BlockingReceiver):
|
|
|
|
|
|
|
|
# TODO: @handle_server_error
|
|
|
|
@inlineCallbacks
|
|
|
|
def go(self):
|
2016-01-29 21:40:41 +00:00
|
|
|
tor_manager = None
|
|
|
|
if self.args.tor:
|
2016-03-03 23:07:05 +00:00
|
|
|
_start = self.args.timing.add_event("import TorManager")
|
2016-01-29 21:40:41 +00:00
|
|
|
from ..twisted.tor_manager import TorManager
|
2016-03-03 23:07:05 +00:00
|
|
|
self.args.timing.finish_event(_start)
|
2016-03-03 22:31:13 +00:00
|
|
|
tor_manager = TorManager(reactor, timing=self.args.timing)
|
2016-01-29 21:40:41 +00:00
|
|
|
# For now, block everything until Tor has started. Soon: launch
|
|
|
|
# tor in parallel with everything else, make sure the TorManager
|
|
|
|
# can lazy-provide an endpoint, and overlap the startup process
|
|
|
|
# with the user handing off the wormhole code
|
|
|
|
yield tor_manager.start()
|
2016-02-28 10:14:21 +00:00
|
|
|
|
2016-01-29 21:40:41 +00:00
|
|
|
w = Wormhole(APPID, self.args.relay_url, tor_manager,
|
|
|
|
timing=self.args.timing)
|
|
|
|
|
|
|
|
rc = yield self._go(w, tor_manager)
|
2016-02-28 10:14:21 +00:00
|
|
|
yield w.close()
|
|
|
|
returnValue(rc)
|
|
|
|
|
|
|
|
@inlineCallbacks
|
2016-01-29 21:40:41 +00:00
|
|
|
def _go(self, w, tor_manager):
|
2016-02-28 10:14:21 +00:00
|
|
|
self.handle_code(w)
|
|
|
|
verifier = yield w.get_verifier()
|
|
|
|
self.show_verifier(verifier)
|
|
|
|
them_d = yield self.get_data(w)
|
|
|
|
try:
|
|
|
|
if "message" in them_d:
|
|
|
|
yield self.handle_text(them_d, w)
|
|
|
|
returnValue(0)
|
|
|
|
if "file" in them_d:
|
|
|
|
f = self.handle_file(them_d)
|
2016-01-29 21:40:41 +00:00
|
|
|
rp = yield self.establish_transit(w, them_d, tor_manager)
|
2016-02-28 10:14:21 +00:00
|
|
|
yield self.transfer_data(rp, f)
|
|
|
|
self.write_file(f)
|
|
|
|
yield self.close_transit(rp)
|
|
|
|
elif "directory" in them_d:
|
|
|
|
f = self.handle_directory(them_d)
|
2016-01-29 21:40:41 +00:00
|
|
|
rp = yield self.establish_transit(w, them_d, tor_manager)
|
2016-02-28 10:14:21 +00:00
|
|
|
yield self.transfer_data(rp, f)
|
|
|
|
self.write_directory(f)
|
|
|
|
yield self.close_transit(rp)
|
|
|
|
else:
|
|
|
|
self.msg(u"I don't know what they're offering\n")
|
|
|
|
self.msg(u"Offer details:", them_d)
|
|
|
|
raise RespondError({"error": "unknown offer type"})
|
|
|
|
except RespondError as r:
|
|
|
|
data = json.dumps(r.response).encode("utf-8")
|
|
|
|
yield w.send_data(data)
|
|
|
|
returnValue(1)
|
|
|
|
returnValue(0)
|
|
|
|
|
|
|
|
@inlineCallbacks
|
|
|
|
def get_data(self, w):
|
|
|
|
try:
|
|
|
|
them_bytes = yield w.get_data()
|
|
|
|
except WrongPasswordError as e:
|
|
|
|
raise TransferError(u"ERROR: " + e.explain())
|
|
|
|
them_d = json.loads(them_bytes.decode("utf-8"))
|
|
|
|
if "error" in them_d:
|
|
|
|
raise TransferError(u"ERROR: " + them_d["error"])
|
|
|
|
returnValue(them_d)
|
|
|
|
|
|
|
|
@inlineCallbacks
|
|
|
|
def handle_text(self, them_d, w):
|
|
|
|
# we're receiving a text message
|
|
|
|
self.msg(them_d["message"])
|
|
|
|
data = json.dumps({"message_ack": "ok"}).encode("utf-8")
|
|
|
|
yield w.send_data(data)
|
|
|
|
|
|
|
|
@inlineCallbacks
|
2016-01-29 21:40:41 +00:00
|
|
|
def establish_transit(self, w, them_d, tor_manager):
|
2016-02-28 10:14:21 +00:00
|
|
|
transit_key = w.derive_key(APPID+u"/transit-key")
|
2016-03-02 01:13:33 +00:00
|
|
|
transit_receiver = TransitReceiver(self.args.transit_helper,
|
2016-03-03 21:02:17 +00:00
|
|
|
no_listen=self.args.no_listen,
|
2016-01-29 21:40:41 +00:00
|
|
|
tor_manager=tor_manager,
|
2016-03-02 01:13:33 +00:00
|
|
|
timing=self.args.timing)
|
2016-02-28 10:14:21 +00:00
|
|
|
transit_receiver.set_transit_key(transit_key)
|
|
|
|
direct_hints = yield transit_receiver.get_direct_hints()
|
|
|
|
relay_hints = yield transit_receiver.get_relay_hints()
|
|
|
|
data = json.dumps({
|
|
|
|
"file_ack": "ok",
|
|
|
|
"transit": {
|
|
|
|
"direct_connection_hints": direct_hints,
|
|
|
|
"relay_connection_hints": relay_hints,
|
|
|
|
},
|
|
|
|
}).encode("utf-8")
|
|
|
|
yield w.send_data(data)
|
|
|
|
|
|
|
|
# now receive the rest of the owl
|
|
|
|
tdata = them_d["transit"]
|
|
|
|
transit_receiver.add_their_direct_hints(tdata["direct_connection_hints"])
|
|
|
|
transit_receiver.add_their_relay_hints(tdata["relay_connection_hints"])
|
|
|
|
record_pipe = yield transit_receiver.connect()
|
|
|
|
returnValue(record_pipe)
|
|
|
|
|
|
|
|
@inlineCallbacks
|
|
|
|
def transfer_data(self, record_pipe, f):
|
|
|
|
self.msg(u"Receiving (%s).." % record_pipe.describe())
|
|
|
|
|
2016-03-02 01:13:33 +00:00
|
|
|
_start = self.args.timing.add_event("rx file")
|
2016-02-28 10:14:21 +00:00
|
|
|
progress_stdout = self.args.stdout
|
|
|
|
if self.args.hide_progress:
|
|
|
|
progress_stdout = io.StringIO()
|
2016-03-02 08:41:33 +00:00
|
|
|
progress = ProgressPrinter(self.xfersize, progress_stdout)
|
|
|
|
|
|
|
|
progress.start()
|
|
|
|
received = yield record_pipe.writeToFile(f, self.xfersize,
|
|
|
|
progress.update)
|
|
|
|
progress.finish()
|
2016-03-02 01:13:33 +00:00
|
|
|
self.args.timing.finish_event(_start)
|
2016-03-02 08:41:33 +00:00
|
|
|
|
2016-02-28 10:14:21 +00:00
|
|
|
# except TransitError
|
|
|
|
if received < self.xfersize:
|
|
|
|
self.msg()
|
|
|
|
self.msg(u"Connection dropped before full file received")
|
|
|
|
self.msg(u"got %d bytes, wanted %d" % (received, self.xfersize))
|
|
|
|
returnValue(1) # TODO: exit properly
|
|
|
|
assert received == self.xfersize
|
|
|
|
|
|
|
|
@inlineCallbacks
|
|
|
|
def close_transit(self, record_pipe):
|
2016-03-02 01:13:33 +00:00
|
|
|
_start = self.args.timing.add_event("ack")
|
2016-02-28 10:14:21 +00:00
|
|
|
yield record_pipe.send_record(b"ok\n")
|
|
|
|
yield record_pipe.close()
|
2016-03-02 01:13:33 +00:00
|
|
|
self.args.timing.finish_event(_start)
|