15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/usr/bin/env python 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved. 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file. 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 64e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)# A simple native messaging host. Shows a Tkinter dialog with incoming messages 74e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)# that also allows to send message back to the webapp. 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import struct 104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)import sys 114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)import threading 124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)import Queue 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)try: 154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) import Tkinter 164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) import tkMessageBox 174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)except ImportError: 184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) Tkinter = None 194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)# Helper function that sends a message to the webapp. 214e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)def send_message(message): 224e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) # Write message size. 234e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) sys.stdout.write(struct.pack('I', len(message))) 244e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) # Write the message itself. 254e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) sys.stdout.write(message) 264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) sys.stdout.flush() 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)# Thread that reads messages from the webapp. 294e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)def read_thread_func(queue): 304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) message_number = 0 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while 1: 325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) # Read the message length (first 4 bytes). 332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) text_length_bytes = sys.stdin.read(4) 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if len(text_length_bytes) == 0: 364e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if queue: 374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) queue.put(None) 384e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) sys.exit(0) 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) # Unpack message length as 4 byte integer. 412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) text_length = struct.unpack('i', text_length_bytes)[0] 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Read the text (JSON object) of the message. 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) text = sys.stdin.read(text_length).decode('utf-8') 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if queue: 474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) queue.put(text) 484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) else: 494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) # In headless mode just send an echo message back. 504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) send_message('{"echo": %s}' % text) 514e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)if Tkinter: 534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) class NativeMessagingWindow(Tkinter.Frame): 544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) def __init__(self, queue): 554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self.queue = queue 564e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) Tkinter.Frame.__init__(self) 584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self.pack() 594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 604e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self.text = Tkinter.Text(self) 614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self.text.grid(row=0, column=0, padx=10, pady=10, columnspan=2) 624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self.text.config(state=Tkinter.DISABLED, height=10, width=40) 634e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 644e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self.messageContent = Tkinter.StringVar() 654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self.sendEntry = Tkinter.Entry(self, textvariable=self.messageContent) 664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self.sendEntry.grid(row=1, column=0, padx=10, pady=10) 674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self.sendButton = Tkinter.Button(self, text="Send", command=self.onSend) 694e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self.sendButton.grid(row=1, column=1, padx=10, pady=10) 704e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 714e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self.after(100, self.processMessages) 724e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 734e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) def processMessages(self): 744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) while not self.queue.empty(): 754e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) message = self.queue.get_nowait() 764e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if message == None: 774e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self.quit() 784e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) return 794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self.log("Received %s" % message) 804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 814e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self.after(100, self.processMessages) 824e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 834e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) def onSend(self): 844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) text = '{"text": "' + self.messageContent.get() + '"}' 854e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self.log('Sending %s' % text) 864e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) try: 874e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) send_message(text) 884e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) except IOError: 894e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) tkMessageBox.showinfo('Native Messaging Example', 904e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 'Failed to send message.') 914e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) sys.exit(1) 924e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) def log(self, message): 944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self.text.config(state=Tkinter.NORMAL) 954e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self.text.insert(Tkinter.END, message + "\n") 964e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self.text.config(state=Tkinter.DISABLED) 974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 994e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)def Main(): 1004e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if not Tkinter: 1014e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) send_message('"Tkinter python module wasn\'t found. Running in headless ' + 1024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 'mode. Please consider installing Tkinter."') 1034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) read_thread_func(None) 1044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) sys.exit(0) 1054e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 1064e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) queue = Queue.Queue() 1074e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 1084e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) main_window = NativeMessagingWindow(queue) 1094e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) main_window.master.title('Native Messaging Example') 1104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 1114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) thread = threading.Thread(target=read_thread_func, args=(queue,)) 1124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) thread.daemon = True 1134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) thread.start() 1144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 1154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) main_window.mainloop() 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) sys.exit(0) 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if __name__ == '__main__': 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Main() 122