this.handle_blist_message(obj.method, obj.params);
} else if (obj.sender == "conversations") {
this.handle_conversations_message(obj.method, obj.params);
+ } else if (obj.sender == "session") {
+ this.handle_session_message(obj.method, obj.params);
}
},
handle_blist_message: function(method, params) {
if (method == "buddy_status_changed") {
window.blist[bid].status = params.status;
} else if (method == "buddy_icon_changed") {
- window.blist[bid].bicon_url = params.url;
+ var url = "/icons/" + window.session_id + "/" + params.url;
+ console.log(url);
+ window.blist[bid].bicon_url = url;
}
window.buddy_list.onChange(window.blist);
},
window.conversations[bid].type_state = params.state;
}
// TODO: repaint conversations window
+ },
+ handle_session_message: function(method, params) {
+ console.log("session:", method, params);
+ if (method == "set_session") {
+ window.session_id = params["session_id"];
+ }
}
};
import json
import logging
import os
+import shutil
import socket
import struct
import subprocess
pwd = os.path.dirname(os.path.abspath(__file__))
+_SESSION_BYTES = 8
+
+# path to the ribbon executable
+ribbon_binary = os.path.join(os.path.dirname(pwd), "ribbon", "ribbon")
+
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s : %(levelname)s : %(name)s : %(message)s',
self.dispatch_backlog()
def open(self):
- # Create a new connection to ribbon.
+ # Create a new ribbon subprocess, then connect to it.
+ self.sess_id = os.urandom(_SESSION_BYTES).encode('hex')
+ self.state_dir = os.path.join(pwd, "state", self.sess_id)
+ self.ribbon_subprocess = subprocess.Popen( [ribbon_binary, "--state-dir", self.state_dir], close_fds=True )
+
+ # TODO: find a better way to make this work, like exponential backoff
+ # polling for the socket that's supposed to appear
+ # For now, a one-second delay gives ribbon enough time to start up
+ import time
+ time.sleep(1.0)
+
self.ribbon_ready = False
self.backlog = [] # A list of bytestrings queued to be sent on the socket.
self.ribbon_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0)
self.ribbon_stream = tornado.iostream.IOStream(self.ribbon_socket)
- self.ribbon_stream.connect( os.path.join(pwd, "state", "socket"), self.on_ribbon_ready)
+ self.ribbon_stream.connect(os.path.join(self.state_dir, "socket"), self.on_ribbon_ready)
log.info('new connection')
- self.write_message('{"key":"value"}')
+ m = { "sender": "session",
+ "method": "set_session",
+ "params": {"session_id": self.sess_id },
+ }
+ self.write_message(json.dumps(m))
def on_message(self, message):
log.info('message received: %r', message)
data = json.loads(message)
self.queue_message(message.encode("latin1"))
def on_close(self):
- log.info('connection closed')
-
+ log.info('connection closed: {}'.format(self.session_id))
+ # kill child ribbon
+ self.ribbon_subprocess.kill()
+ self.ribbon_subprocess.wait()
+ # clean up that ribbon instance's work directory
+ shutil.rmtree(self.state_dir)
application = tornado.web.Application([
(r'/(index.html)', tornado.web.StaticFileHandler, {'path': pwd}),
(r'/([a-z]*\.js)', tornado.web.StaticFileHandler, {'path': pwd}),
(r'/([a-z]*\.css)', tornado.web.StaticFileHandler, {'path': pwd}),
- (r'/icons/(.*)', tornado.web.StaticFileHandler, {'path': os.path.join(pwd, 'state', 'icons')}),
+ # mildly janky url mapping
+ (r'/icons/([0-9a-f]{16}/icons/.*)', tornado.web.StaticFileHandler, {'path': os.path.join(pwd, 'state')}),
(r'/ws', WSHandler),
])
if __name__ == "__main__":
- # Spawn the ribbon subprocess
- ribbon_binary = os.path.join(os.path.dirname(pwd), "ribbon", "ribbon")
- ribbon = subprocess.Popen( [ribbon_binary, "--state-dir", os.path.join(pwd, "state")], close_fds=True )
# Spawn the http server
http_server = tornado.httpserver.HTTPServer(application)