Chrome Native messaging with PHP
Asked Answered
A

1

5

I am trying to build a PHP class that can communicate with a Chrome Extention through Native Messaging.

I can connect to my code, but at initiation Chrome sends

chrome-extension://lkjcciocnocjjgpacggbaikjehbfedbl/ --parent-window=1837060

To my PHP console app (The Host). What do I reply to make the connection working? Below my PHP code. Yes its dirty because its a POC project and I am very new to Chrome Extensions especially with the current updates.

function out($data = ""){
    $fp = fopen("php://stdout", "w");
    if($fp){
        $response = array("text" => "Ok");
        $message = json_encode($response);
        fwrite($fp, $message);
        fflush($fp);
        slog("[OUTPUT] " . json_encode($response));
        fclose($fp);
        exit(0);
    }else{
        slog("Can't open output stream.");
        exit(1);
    }
}

function err($data){
    $fp = fopen("php://stderr", "w");
    if($fp){
        fwrite($fp, $data);
        fflush($fp);
        fclose($fp);
    }
    return;
}

function in(){
    $data = "";
    $fp = fopen("php://stdin", "r");
    if($fp){
        $data = fgets($fp);
        fclose($fp);       
    }else{
        slog("Can't open input stream.");
        exit(1);
    }
    slog("[INPUT]" . $data);
    return $data;
}

function slog($data){
    if($data != ""){
        file_put_contents("./log.txt", date("r").": {$data}\r\n", FILE_APPEND);
    }
}

slog("Entering");
while(true){
    if(($l = in()) !== ""){
        out($l);
    }else{
        exit(0);
    }
}
exit(0);

My background.js code. (the extention)

var port = null;
var hostName = "com.google.chrome.poc-extension";

function appendMessage(text) {
  document.getElementById('response').innerHTML += "<p>" + text + "</p>";
}

function updateUiState() {
  if (port) {
      document.getElementById('connect-button').style.display = 'none';
  }else{
      document.getElementById('connect-button').style.display = 'block';
  }
}

function sendNativeMessage() {
  port = chrome.runtime.connectNative(hostName);
  port.onMessage.addListener(onNativeMessage);

  message = {"text": document.getElementById('input-text').value};
  port.postMessage(message);
  appendMessage("Sent message: <b>" + JSON.stringify(message) + "</b>");
}
function onNativeMessage(message) {
  alert(message);
  appendMessage("Received message: <b>" + JSON.stringify(message) + "</b>");
}
function onDisconnected() {
  appendMessage("Failed to connect: " + chrome.runtime.lastError.message);
  console.log(chrome.runtime.lastError);
  port = null;
  updateUiState();
}
function connect() {  
  appendMessage("Connecting to native messaging host <b>" + hostName + "</b>")
  port = chrome.runtime.connectNative(hostName);
  port.onMessage.addListener(onNativeMessage);
  port.onDisconnect.addListener(onDisconnected);
  updateUiState();
}
document.addEventListener('DOMContentLoaded', function (){    
    document.getElementById('connect-button').addEventListener('click', connect);
    document.getElementById('send-message-button').addEventListener('click', sendNativeMessage);
    updateUiState();
});

There is this Python example app but I don't really get what it does exactly. Besides that it also uses the Tkinter plugin which I don't want. I want a clean, plain and simpel extension.

Antimere answered 13/11, 2017 at 16:50 Comment(8)
The first quote isn't sent to stdin, those are command line parameters. If you see it in your php code then something is wrong. As for the python example, the meaningful part is send_message and read_thread_func. The protocol is simple and described in the documentation: 4 bytes length, JSON-ified message.Psycho
Sounds plausible indeed. Gonna check and reply soon.Antimere
@wOxxOm chrome doesn't seem to pipe stdin correctly. I use a windows .bat file, like the example Python app. echo %* | php -f "%~dp0/native_host.php"Antimere
echo %* prints your command line into stdin, which is hardly what you want. The documentation's bat file doesn't do that.Psycho
I tried only with php -f "%~dp0/native_host.php" but that doesn't work either.......kind stuck hereAntimere
Well, the posted code doesn't read/write the message length so of course it shouldn't work.Psycho
@wOxxOm How to read/write the message length using php?Sabinasabine
I don't know PHP but I think it should have a way to read stdin in binary mode. You need to read 4 bytes and unpack them to a long integer - it will be the length, then read this amount of bytes to a string.Psycho
B
11

Native Messaging use structured data (length-formatted) to read and write. in browser (JavaScript), that structure has been handled by browser. If you want to communicate with Native Messaging, so you need to follow that structure. Read refference here

Each message is serialized using JSON, UTF-8 encoded and is preceded with 32-bit message length in native byte order.

So you need to send your message as: len(message) + [your message]

len(message) must be packed following the protocol.

Example function to send output:

function out($data = ""){
    $fp = fopen("php://stdout", "w");
    if($fp){
        $response = array("text" => "Ok");
        $message = json_encode($response);
        //Send the length of data
        fwrite($fp, pack('L', strlen($message)));
        fwrite($fp, $message);
        fflush($fp);
        slog("[OUTPUT] " . json_encode($response));
        fclose($fp);
        exit(0);
    }else{
        slog("Can't open output stream.");
        exit(1);
    }
}

Read Input:

function in(){
    $data = "";
    $fp = fopen("php://stdin", "r");
    if($fp){
        //Read first 4 bytes as unsigned integer
        $len    = current( unpack('L', fread($fp, 4) ) );
        $data   = fread($fp, $len);
        fclose($fp);
    }else{
        slog("Can't open input stream.");
        exit(1);
    }
    slog("[INPUT]" . $data);
    return $data;
}
Bangka answered 25/1, 2018 at 12:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.