Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use a shell-script as Chrome Native Messaging host application

How do you process a Chrome Native Messaging API-call with a bash script?

I succeeded in doing it with python with this example

Sure I can call bash from the python code with subprocess, but is it possible to skip python and process the message in bash directly?

The problematic part is reading the JSON serialized message into a variable. The message is serialized using JSON, UTF-8 encoded and is preceded with 32-bit message length in native byte order through stdin.

echo $* only outputs: chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/

Also something like

read
echo $REPLY

doesn't output anything. No sign of the JSON message. Python uses struct.unpack for this. Can that be done in bash?

like image 354
Janghou Avatar asked Jul 15 '14 17:07

Janghou


People also ask

What is Native messaging host?

The native app host sends and receives messages with extensions using standard input and standard output. Extensions that use native messaging are installed in Microsoft Edge similar to any other extension. However, native apps aren't installed or managed by Microsoft Edge.


1 Answers

I suggest to not use (bash) shell scripts as a native messaging host, because bash is too limited to be useful.

read without any parameters reads a whole line before terminating, while the native messaging protocol specifies that the first four bytes specify the length of the following message (in native byte order).

Bash is a terrible tool for processing binary data. An improved version of your read command would specify the -n N parameter to stop reading after N characters (note: not bytes) and -r to remove some processing. E.g. the following would store the first four characters in a variable called var_prefix:

IFS= read -rn 4 var_prefix

Even if you assume that this stores the first four bytes in the variable (it does not!), then you have to convert the bytes to an integer. Did I already mention that bash automatically drops all NUL bytes? This characteristics makes Bash utterly worthless for being a fully capable native messaging host.

You could cope with this shortcoming by ignoring the first few bytes, and start parsing the result when you spot a { character, the beginning of the JSON-formatted request. After this, you have to read all input until the end of the input is found. You need a JSON parser that stops reading input when it encounters the end of the JSON string. Good luck with writing that.

Generating output is a easier, just use echo -n or printf.

Here is a minimal example that assumes that the input ends with a }, reads it (without processing) and replies with a result. Although this demo works, I strongly recommend to not use bash, but a richer (scripting) language such as Python or C++.

#!/bin/bash
# Loop forever, to deal with chrome.runtime.connectNative
while IFS= read -r -n1 c; do
    # Read the first message
    # Assuming that the message ALWAYS ends with a },
    # with no }s in the string. Adopt this piece of code if needed.
    if [ "$c" != '}' ] ; then
        continue
    fi

    message='{"message": "Hello world!"}'
    # Calculate the byte size of the string.
    # NOTE: This assumes that byte length is identical to the string length!
    # Do not use multibyte (unicode) characters, escape them instead, e.g.
    # message='"Some unicode character:\u1234"'
    messagelen=${#message}

    # Convert to an integer in native byte order.
    # If you see an error message in Chrome's stdout with
    # "Native Messaging host tried sending a message that is ... bytes long.",
    # then just swap the order, i.e. messagelen1 <-> messagelen4 and
    # messagelen2 <-> messagelen3
    messagelen1=$(( ($messagelen      ) & 0xFF ))               
    messagelen2=$(( ($messagelen >>  8) & 0xFF ))               
    messagelen3=$(( ($messagelen >> 16) & 0xFF ))               
    messagelen4=$(( ($messagelen >> 24) & 0xFF ))               

    # Print the message byte length followed by the actual message.
    printf "$(printf '\\x%x\\x%x\\x%x\\x%x' \
        $messagelen1 $messagelen2 $messagelen3 $messagelen4)%s" "$message"

done
like image 185
Rob W Avatar answered Oct 16 '22 16:10

Rob W