native: feed stdin of subprocess for "run"

There's currently no way to tell the native messenger to
provide a particular stdin to a "run" command. This leads to
awkward uses of here-docs, which in turn require hacks like
the one in 4a5dcd7557.

Instead, let's take the "content" field from the message
body and feed that to the command's stdin, which gives us a
more robust channel for passing arbitrary bytes.

Note that we have to switch to using subprocess.Popen's
communicate() method, which will make sure we don't deadlock
if the input or output exceeds a pipe buffer.

Note also that when no stdin is provided by the caller
(i.e., all current cases), we'll pass an empty string. This
actually fixes a minor bug. Because we didn't override the
command's stdin argument, it's hooked to the messenger's
stdin, which is the pipe coming from firefox. If the command
tries to read, it (and the messenger) will hang forever,
since firefox is waiting for the messenger to respond before
writing anything else.

I bumped the native messenger version. This is mostly
backwards compatible (existing callers just don't send any
stdin content at all). But a caller that wants to send stdin
should check to make sure we have at least 0.1.7.
This commit is contained in:
Jeff King 2018-07-18 01:08:35 -04:00
parent a9fb9b65e9
commit fb0bfc0807

View file

@ -14,7 +14,7 @@ import time
import unicodedata
DEBUG = False
VERSION = "0.1.6"
VERSION = "0.1.7"
class NoConnectionError(Exception):
@ -413,15 +413,14 @@ def handleMessage(message):
elif cmd == "run":
commands = message["command"]
stdin = message.get("content", "").encode("utf-8")
try:
p = subprocess.check_output(commands, shell=True)
reply["content"] = p.decode("utf-8")
reply["code"] = 0
p = subprocess.Popen(commands, shell=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
except subprocess.CalledProcessError as process:
reply["code"] = process.returncode
reply["content"] = process.output.decode("utf-8")
reply["content"] = p.communicate(stdin)[0].decode("utf-8")
reply["code"] = p.returncode
elif cmd == "eval":
output = eval(message["command"])