Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements #4

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@ Based on [Native Messaging on MDN](https://developer.mozilla.org/en-US/docs/Mozi
If [`runtime.connectNative`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/connectNative) is used, `get_message()` must be called repeatedly in a loop to poll for messages.
If [`runtime.sendNativeMessage`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/sendNativeMessage) is used, `get_message()` only needs to be called once.

## `encode_message( message_content )`
`nativemessaging.encode_message()` takes one argument, a message to be encoded.
Returns an encoded version of a message to be returned to the browser. Use with `send_message()`.
## `send_message( message )`
`nativemessaging.send_message()` takes one argument, a message to be returned to the browser.

## `send_message( encoded_message )`
`nativemessaging.send_message()` takes one argument, an encoded message from `encode_message()`. Returns a message to the browser.
## `install( browsers, manifest_file )`
`nativemessaging.install()` takes two arguments, a list of browsers to install the manifest and a manifest. Supported browsers are 'chrome' and 'firefox' and the manifest must be the contents of a valid manifest file.

## Sample
Browser side:
Expand All @@ -42,7 +41,7 @@ import nativemessaging
while True:
message = nativemessaging.get_message()
if message == "hello":
nativemessaging.send_message(nativemessaging.encode_message("world"))
nativemessaging.send_message("world")
```

## nativemessaging-install
Expand Down
97 changes: 0 additions & 97 deletions bin/nativemessaging-install.py

This file was deleted.

30 changes: 3 additions & 27 deletions nativemessaging/__init__.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,4 @@
#!/usr/bin/env python
import json
import sys
import struct
# -*- coding: utf-8 -*-


# from https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging
def get_message():
raw_length = sys.stdin.buffer.read(4)
if len(raw_length) == 0:
sys.exit(0)
message_length = struct.unpack("@I", raw_length)[0]
message = sys.stdin.buffer.read(message_length).decode("utf-8")
return json.loads(message)


# Encode a message for transmission, given its content.
def encode_message(message_content):
encoded_content = json.dumps(message_content).encode("utf-8")
encoded_length = struct.pack("@I", len(encoded_content))
return {"length": encoded_length, "content": encoded_content}


# Send an encoded message to stdout.
def send_message(encoded_message):
sys.stdout.buffer.write(encoded_message["length"])
sys.stdout.buffer.write(encoded_message["content"])
sys.stdout.buffer.flush()
from .nativemessaging import *
from .install import install
5 changes: 5 additions & 0 deletions nativemessaging/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-

from .install import main

main()
114 changes: 114 additions & 0 deletions nativemessaging/install.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# -*- coding: utf-8 -*-

import argparse
import os
import sys
import json
if sys.platform == "win32":
import winreg

# locations for storing manifests
browser_info = {
"chrome": {
"registry": "Software\\Google\\Chrome\\NativeMessagingHosts",
"linux": os.path.join(os.path.expandvars("$HOME"), ".config/google-chrome/NativeMessagingHosts"),
"darwin": os.path.join(os.path.expandvars("$HOME"), "Library/Application Support/Google/Chrome/NativeMessagingHosts")
},
"firefox": {
"registry": "Software\\Mozilla\\NativeMessagingHosts",
"linux": os.path.join(os.path.expandvars("$HOME"), ".mozilla/native-messaging-hosts"),
"darwin": os.path.join(os.path.expandvars("$HOME"), "Library/Application Support/Mozilla/NativeMessagingHosts")
}
}

def read_file(filename):
with open(filename, "r") as f:
return f.read()


def write_file(filename, contents):
with open(filename, "w") as f:
f.write(contents)


def write_manifest(browser, path, manifest):
if browser == "firefox":
manifest.pop("allowed_origins", None)
elif browser == "chrome":
manifest.pop("allowed_extensions", None)
write_file(path, json.dumps(manifest))
#print("Saved manifest file to " + path)


def create_reg_key(path, value):
winreg.CreateKey(winreg.HKEY_CURRENT_USER, path)
registry_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, path, 0, winreg.KEY_WRITE)
winreg.SetValue(registry_key, "", winreg.REG_SZ, value)
winreg.CloseKey(registry_key)
print("Created registry key at HKEY_CURRENT_USER\\" + path)


def install_windows(browsers, manifest):
install_dir = os.path.dirname(manifest["path"])
if manifest["path"].endswith(".py"):
# create batch file for python apps in windows
batch_path = os.path.join(install_dir, manifest["name"] + ".bat")
write_file(batch_path, "@echo off\npython -u \"{0}\"".format(manifest["path"]))
manifest["path"] = batch_path
#print("Batch file created at: " + manifest["path"])
# write registry key on windows
for browser in browsers:
manifest_path = os.path.join(install_dir, "{0}_{1}.json".format(manifest["name"], browser))
write_manifest(browser, manifest_path, manifest)
create_reg_key(os.path.join(browser_info[browser]["registry"], manifest["name"]),
manifest_path)


def install_unix(browsers, manifest):
for browser in browsers:
manifest_path_folder = browser_info[browser][sys.platform]
if not os.path.exists(manifest_path_folder):
os.mkdir(manifest_path_folder)
manifest_path = os.path.join(manifest_path_folder, manifest["name"] + ".json")
write_manifest(browser, manifest_path, manifest)


def install(browsers, manifest):
# ensure path is absolute
manifest["path"] = os.path.abspath(manifest["path"])
#print("Absolute path: " + manifest["path"])

if sys.platform == "win32":
install_windows(browsers, manifest)
elif sys.platform in ["linux", "darwin"]:
install_unix(browsers, manifest)


def options():
ap = argparse.ArgumentParser()
ap.add_argument("browser", choices=["chrome", "firefox"], nargs="+")
ap.add_argument("--manifest")
return vars(ap.parse_args())


def main():
opts = options()

manifest_file = "native-manifest.json"
if opts["manifest"] is not None and os.path.isfile(opts["manifest"]):
# read from arguments if available
manifest_file = opts["manifest"]
elif os.path.isfile("native-manifest.json"):
# fall back to native-manifest.json
pass
else:
raise Exception("No manifest found. Supply a manifest in the arguments, or create a manifest named native-manifest.json")

# read contents of manifest file
print("Reading manifest: " + manifest_file)
manifest_contents = read_file(manifest_file)
manifest = json.loads(manifest_contents)

install(opts["browser"], manifest)

input("Done! Press enter or close the window.")
35 changes: 35 additions & 0 deletions nativemessaging/nativemessaging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-

import json
import sys
import struct

def log_browser_console(message):
'''
Log a message in the browser console
'''
sys.stderr.write(message + '\n')


# from https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging
def get_message():
'''
Receive a native message from the browser
'''
raw_length = sys.stdin.buffer.read(4)
if len(raw_length) == 0:
raise Exception("Message is empty")
message_length = struct.unpack("@I", raw_length)[0]
message = sys.stdin.buffer.read(message_length).decode("utf-8")
return json.loads(message)


def send_message(message):
'''
Send a native message to the browser
'''
encoded_content = json.dumps(message).encode("utf-8")
encoded_length = struct.pack("@I", len(encoded_content))
sys.stdout.buffer.write(encoded_length)
sys.stdout.buffer.write(encoded_content)
sys.stdout.buffer.flush()
4 changes: 3 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
author_email="[email protected]",
license="MPL 2.0",
packages=["nativemessaging"],
scripts=["bin/nativemessaging-install.py"],
entry_points={
"console_scripts": ["nativemessaging-install = nativemessaging.install:main"]
},
include_package_data=True,
package_data={"": ["README.md"]},
classifiers=[
Expand Down
Binary file added test/example_message
Binary file not shown.
11 changes: 11 additions & 0 deletions test/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env python

# -*- coding: utf-8 -*-

import nativemessaging

msg = nativemessaging.get_message()
nativemessaging.log_browser_console("received msg: %s" % str(msg))
nativemessaging.send_message("test")

print()
3 changes: 3 additions & 0 deletions test/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

PYTHONPATH=.. python test.py < example_message