From 096cb3932096e05a126012e07efa3ef83a9cf78e Mon Sep 17 00:00:00 2001 From: Rodolphe Breard Date: Wed, 21 Nov 2012 21:13:18 +0100 Subject: [PATCH] messages reception support --- README.md | 33 +++------- chromesoul.css | 8 +++ lib/ns_client.js | 158 ++++++++++++++++++++++++++++------------------- lib/nsui.js | 75 ++++++++++++++-------- lib/options.js | 63 +++++++++---------- lib/tab.nsui.js | 62 +++++++++++-------- manifest.json | 2 +- start.js | 25 ++++---- 8 files changed, 242 insertions(+), 184 deletions(-) diff --git a/README.md b/README.md index 3e27c38..07a669a 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ ## What is chromesoul? -Chromesoul is a minimalist NetSoul client for Google Chrome. It aim to connect to the NetSoul server and stay active, but does not provide all the functionalities you can expect from a real client. +Chromesoul is a minimalist NetSoul client for Google Chrome. If you don't know what NetSoul is, you probably don't need it. If you're neither a student nor an employee of any [IONIS](http://www.ionis-group.com/) school (eg: Epitech, Epita, …), all you have to know is that NetSoul is an internal messaging protocol for thoses schools. ## Why? -When you're on the PIE, you're stuck in it unless you are connected to netsoul, and with multiple boot and mobile devices it's a real pain to configure many NetSoul clients. That's why chromesoul is awesome: not only it's a cross-platform NS client, but with the Chrome synchronisation it's automaticaly installed and configured. +When you're on the PIE, you're stuck in it unless you are connected to netsoul. With multiple operating systems it's a real pain to configure a NetSoul client for each of them. That's why chromesoul is awesome: not only it's a cross-platform NS client, but the Chrome synchronisation automaticaly install and configure it everywhere you uses chrome. ## Requirements @@ -19,20 +19,20 @@ Sockets have been introduced in Chromium 24. Therefore, it is required to have a Storing your socks password encrypted is a priority but is not available yet. Yes, the chromesoul dev version is unsafe; so is the official NetSoul server which doesn't store a hash but the password itself (it's required by the NetSoul protocol). ### Messages -Because the only purpose of this app is to provide an access to internet when you're on the PIE, there is no plan to support messages at this time. Thoses stupid guys who have fun spamming everyone by broadcasting messages are the second reasons why you won't see any message using chromesoul. Maybe one day I'll write a chat interface. Since version 0.2, chromesoul have a dedicated window and is more likely to integrate a chat interface. +At first, chromesoul's was only intended to provide an access to internet from the PIE and didn't supported messages. However, I changed my mind and decided to include this feature. Don't panic if you don't want to be disturbed, it's possible to turn it off. All you have to do is to go in your settings and uncheck the little box. -### Contacts -If you cannot send and receive messages, you don't need a contact list. +### Contact list +It's on it's way. ### State change -Same as contacts. Why changing your state if you're not gonna talk? +Same as the contact list. ## FAQ ### How do I install this app? Because it's not packaged yet, you have to: -* Download it +* Download it (aka: clone the git repository) * Use the developer mode * Click on "Load unpackaged extension..." and select the chromesoul directory @@ -45,26 +45,11 @@ Just like any other app: * click on the "apps" tab (have a look at the bottom of the page) * click on "chromesoul" -### Could not load extension from '/path/to/chromesoul'. Invalid value for 'permissions[2]'. -It seems your browser doesn't support sockets. Have you checked the requirements? - -### Do I really have to compile the version from dev channel? +### Do I really have to compile the version from beta channel? No, you can wait until it comes to stable. -### Where do I send and receive messages? -Please have a look at the features lists. - -### Will you ever support sending and receiving messages? -Maybe. If someone pays me, I will; otherwise I'll do it if have time to invest in this project. Notice: I accept beer as payment. - -### I don't want chromesoul to support sending and receiving messages, will you do something about it? -Yes, if it comes, you'll be able to shut it down very easily. So, keep calm and carry on. - -### I talked to you on netsoul but you never reply! -I started this project for my personal usage, which means I use it. Now look at the features list and the previous questions. - ### I found a bug, what should I do? -Hum, please [report it](https://github.com/TychoBrahe/chromesoul/issues) with as much details as you can. Thanks in advance. +Please [report it](https://github.com/TychoBrahe/chromesoul/issues) with as much details as you can. Thanks in advance. ### I hate chromesoul! It's your opinion and I don't care about it. diff --git a/chromesoul.css b/chromesoul.css index df19eae..1535a61 100644 --- a/chromesoul.css +++ b/chromesoul.css @@ -79,3 +79,11 @@ #tab-config > span { display: none; } + +.spk-oth { + color: red; +} + +.spk-me { + color: blue; +} diff --git a/lib/ns_client.js b/lib/ns_client.js index b4d4a2a..803186b 100644 --- a/lib/ns_client.js +++ b/lib/ns_client.js @@ -20,48 +20,84 @@ var NsClient = function() { this.allowed_statuses = ["actif", "away", "idle", "lock"]; this.is_connected = false; this.socket = null; + this.actions = {}; + + this.actions.ping = {}; + this.actions.ping.is = function(msg) { + return msg.substr(0, 5) === "ping "; + }; + this.actions.ping.act = (function(elem) { + return function(msg) { + chrome.socket.write(elem.socket, str2ab(msg), function(w_inf) {}); + }; + })(this); + + this.actions.msg = {}; + this.actions.msg.is = function(msg) { + var exp = /user_cmd (\d+):user:.*?:(.*?)@(.*?):.*?:(.*?):(.*?) \| msg ([^ ]*)/; + return exp.exec(msg) !== null; + }; + this.actions.msg.act = (function(elem) { + return function(msg) { + elem.storage.get('enable_msg', function(infos) { + if (infos.enable_msg) { + var mo = {}, exp = /user_cmd (\d+):user:.*?:(.*?)@(.*?):.*?:(.*?):(.*?) \| msg ([^ ]*)/; + + mch = exp.exec(msg); + if (mch !== null && typeof elem.msgHandler !== "undefined") { + mo.socket = mch[1]; + mo.login = mch[2]; + mo.host = mch[3]; + mo.location = decodeURI(mch[4]); + mo.group = mch[5]; + mo.message = decodeURI(mch[6]); + + elem.msgHandler(mo); + } + } + }); + }; + })(this); }; NsClient.prototype.connect = function() { - var cnt = function(elem) { - return function(infos) { - if (typeof infos.login !== "undefined" && typeof infos.pwd_socks !== "undefined") { - chrome.socket.create('tcp', {}, function(sock_inf) { - elem.socket = sock_inf.socketId; - chrome.socket.connect(elem.socket, "ns-server.epita.fr", 4242, function(res) { - chrome.socket.read(elem.socket, null, function(rd_inf) { - if (rd_inf.resultCode > 0) { - var data = ab2str(rd_inf.data).split(' '), - auth = "ext_user_log "; - auth += infos.login + " "; - auth += hex_md5(data[2] + "-" + data[3] + "/" + data[4] + infos.pwd_socks) + " "; - auth += "chromesoul chromesoul\n"; - chrome.socket.write(elem.socket, str2ab("auth_ag ext_user none none\n"), function(w_inf) { - chrome.socket.read(elem.socket, null, function(rd_inf) { - if (rd_inf.resultCode > 0) { - chrome.socket.write(elem.socket, str2ab(auth), function(w_inf) { - chrome.socket.read(elem.socket, null, function(rd_inf) { - if (rd_inf.resultCode > 0) { - elem.is_connected = true; - console.info("connected to the netsoul server"); - elem.updateStatus(); - elem.daemonize(); - } - }); - }); - } - }); - }); - } - }); - }); - }); - } - }; - }; - if (!this.is_connected) { - this.storage.get(null, cnt(this)); + this.storage.get(null, (function(elem) { + return function(infos) { + if (typeof infos.login !== "undefined" && typeof infos.pwd_socks !== "undefined") { + chrome.socket.create('tcp', {}, function(sock_inf) { + elem.socket = sock_inf.socketId; + chrome.socket.connect(elem.socket, "ns-server.epita.fr", 4242, function(res) { + chrome.socket.read(elem.socket, null, function(rd_inf) { + if (rd_inf.resultCode > 0) { + var data = ab2str(rd_inf.data).split(' '), + auth = "ext_user_log "; + auth += infos.login + " "; + auth += hex_md5(data[2] + "-" + data[3] + "/" + data[4] + infos.pwd_socks) + " "; + auth += "chromesoul chromesoul\n"; + chrome.socket.write(elem.socket, str2ab("auth_ag ext_user none none\n"), function(w_inf) { + chrome.socket.read(elem.socket, null, function(rd_inf) { + if (rd_inf.resultCode > 0) { + chrome.socket.write(elem.socket, str2ab(auth), function(w_inf) { + chrome.socket.read(elem.socket, null, function(rd_inf) { + if (rd_inf.resultCode > 0) { + elem.is_connected = true; + console.info("connected to the netsoul server"); + elem.updateStatus(); + elem.daemonize(); + } + }); + }); + } + }); + }); + } + }); + }); + }); + } + }; + })(this)); } }; @@ -75,31 +111,29 @@ NsClient.prototype.disconnect = function() { }; NsClient.prototype.daemonize = function() { - var dm = function(elem) { - return function(rd_inf) { - if (rd_inf.resultCode > 0) { - var data = ab2str(rd_inf.data); - - if (data.substr(0, 5) === "ping ") { - chrome.socket.write(elem.socket, rd_inf.data, function(w_inf) { - elem.daemonize(); - }); - } else { - elem.daemonize(); - } - } else { - /* - * A read error is the only way to know if the remote peer has disconnected. - * See for more informations. - */ - elem.disconnect(); - console.info('connection lost, reconnecting...'); - } - }; - }; - if (this.is_connected) { - chrome.socket.read(this.socket, dm(this)); + chrome.socket.read(this.socket, (function(elem) { + return function(rd_inf) { + if (rd_inf.resultCode > 0) { + var at, data = ab2str(rd_inf.data); + + for (at in elem.actions) { + if (elem.actions[at].is(data)) { + elem.actions[at].act(data); + break ; + } + } + elem.daemonize(); + } else { + /* + * A read error is the only way to know if the remote peer has disconnected. + * See for more informations. + */ + elem.disconnect(); + console.info('connection lost, reconnecting...'); + } + }; + })(this)); } else { console.error("unable to daemonize: not connected"); } diff --git a/lib/nsui.js b/lib/nsui.js index 04c76fc..e3a3392 100644 --- a/lib/nsui.js +++ b/lib/nsui.js @@ -19,7 +19,7 @@ var Nsui = function() { }; Nsui.prototype.setReconnect = function(func) { - document.getElementById('reconnect').addEventListener('click', func, false); + document.getElementById("reconnect").addEventListener("click", func, false); }; Nsui.prototype.createTab = function(name) { @@ -40,12 +40,12 @@ Nsui.prototype.deleteTab = function(tab) { if (new_tab !== null) { new_tab.show(); } else { - document.getElementById('configuration').style.display = 'block'; + document.getElementById("configuration").style.display = "block"; } }; Nsui.prototype.hideAllTabs = function() { - document.getElementById('configuration').style.display = 'none'; + document.getElementById("configuration").style.display = "none"; for (i = this.tab_lst.length - 1; i >= 0; --i) { this.tab_lst[i].hide(); } @@ -69,7 +69,7 @@ Nsui.prototype.getNextTab = function(current_name) { for (i = this.tab_lst.length - 1; i >= 0; --i) { if (this.tab_lst[i].name === current_name) { - if (prev === null && typeof this.tab_lst[i - 1] !== 'undefined') { + if (prev === null && typeof this.tab_lst[i - 1] !== "undefined") { prev = this.tab_lst[i - 1]; } break ; @@ -82,35 +82,60 @@ Nsui.prototype.getNextTab = function(current_name) { Nsui.prototype.addContentToTab = function(tab_name, content) { var tab = this.getTabByName(tab_name); + if (tab === null) { tab = this.createTab(tab_name); } - tab.setActive(); - tab.appendText(content); + if (!tab.isCurrent()) { + tab.setActive(); + } + tab.appendText(this.formatMessage(content)); +}; + +Nsui.prototype.formatMessage = function(msg) { + var fmt = ""; + + if (msg.login !== null) { + fmt += '' + msg.login + ': '; + } else { + fmt += '' + document.getElementById('login').value + ': '; + } + + msg.message = msg.message.replace("<", "<"); + msg.message = msg.message.replace(">", ">"); + fmt += msg.message; + + fmt += '
'; + + return fmt; }; Nsui.prototype.init = function() { - var sh = function(elem) { + document.getElementById("tab-config").addEventListener("click", (function(elem) { return function() { - for (var i = elem.tab_lst.length - 1; i >= 0; --i) { + for (var i = elem.tab_lst.length - 1; i >= 0; --i) { elem.tab_lst[i].hide(); - } - this.classList.add('tab-current'); - document.getElementById('configuration').style.display = 'block'; + } + this.classList.add("tab-current"); + document.getElementById("configuration").style.display = "block"; }; - }, - tch = function(elem) { - return function() { - elem.deleteTab(this); - }; - }, - tsh = function(elem) { - return function() { - elem.hideAllTabs(); - }; - }; + })(this), false); - document.getElementById('tab-config').addEventListener('click', sh(this), false); - Tab.prototype.closeHandler = tch(this); - Tab.prototype.showHandler = tsh(this); + Tab.prototype.closeHandler = (function(elem) { + return function() { + elem.deleteTab(this); + }; + })(this); + + Tab.prototype.showHandler = (function(elem) { + return function() { + elem.hideAllTabs(); + }; + })(this); + + NsClient.prototype.msgHandler = (function(elem) { + return function(msg) { + elem.addContentToTab(msg.login, msg); + }; + })(this); }; diff --git a/lib/options.js b/lib/options.js index 61c7981..8e0ea31 100644 --- a/lib/options.js +++ b/lib/options.js @@ -51,12 +51,6 @@ OptionsManager.prototype.types = { }; OptionsManager.prototype.init = function() { - var saveOpts = function(elem) { - return function() { - elem.save(); - }; - }; - this.status = document.getElementById("status"); this.save_btn = document.getElementById("save"); this.opts = document.getElementsByClassName("opt"); @@ -64,52 +58,51 @@ OptionsManager.prototype.init = function() { this.restore(); if (this.save !== null) { - this.save_btn.addEventListener("click", saveOpts(this)); + this.save_btn.addEventListener("click", (function(elem) { + return function() { + elem.save(); + }; + })(this)); } }; OptionsManager.prototype.save = function() { - var i = 0, - data = {}, - notif = function(elem) { - return function () { - if (elem !== null) { - elem.innerHTML = "Options saved."; - setTimeout(function() { - elem.innerHTML = ""; - }, 3000); - } - }; - }; + var i = 0, data = {}; if (this.opts !== null) { for (i = this.opts.length - 1; i >= 0; --i) { data[this.opts[i].id] = this.getElemValue(this.opts[i]); } - this.storage.set(data, notif(this.status)); + this.storage.set(data, (function(elem) { + return function () { + if (elem !== null) { + elem.innerHTML = "Options saved."; + setTimeout(function() { + elem.innerHTML = ""; + }, 3000); + } + }; + })(this.status)); } }; OptionsManager.prototype.restore = function() { - var i, - el, - rst = function(elem) { - return function(items) { - for (i in items) { - el = document.getElementById(i); - if (el !== null) { - elem.setElemValue(el, items[i]); - } - } - }; - }; + var i, el; - this.storage.get(null, rst(this)); + this.storage.get(null, (function(elem) { + return function(items) { + for (i in items) { + el = document.getElementById(i); + if (el !== null) { + elem.setElemValue(el, items[i]); + } + } + }; + })(this)); }; OptionsManager.prototype.getElemValue = function(elem) { - var val = null, - type = elem.getAttribute('type'); + var val = null, type = elem.getAttribute('type'); if (typeof this.types.get[type] !== "undefined") { val = this.types.get[type](elem); diff --git a/lib/tab.nsui.js b/lib/tab.nsui.js index 58534ff..d7bf950 100644 --- a/lib/tab.nsui.js +++ b/lib/tab.nsui.js @@ -16,8 +16,8 @@ var Tab = function(name) { this.name = this.filterName(name); - this.wr_lst = document.getElementById('tab-lst'); - this.wr_body = document.getElementById('tab-body-wrapper'); + this.wr_lst = document.getElementById("tab-lst"); + this.wr_body = document.getElementById("tab-body-wrapper"); this.initListElement(); this.initBodyElement(); }; @@ -28,7 +28,7 @@ Tab.prototype.filterName = function(name) { }; Tab.prototype.initListElement = function() { - var inner = document.createElement('span'), + var inner = document.createElement("span"), evt_click = function(elem) { return function() { elem.show(); @@ -41,27 +41,35 @@ Tab.prototype.initListElement = function() { }; inner.innerHTML = this.name; - this.el_lst = document.createElement('li'); - this.el_lst.addEventListener('click', evt_click(this), false); - this.el_lst.addEventListener('dblclick', evt_dblclick(this), false); + this.el_lst = document.createElement("li"); + this.el_lst.addEventListener("click", (function(elem) { + return function() { + elem.show(); + }; + })(this), false); + this.el_lst.addEventListener("dblclick", (function(elem) { + return function() { + elem.close(); + }; + })(this), false); this.el_lst.appendChild(inner); this.wr_lst.appendChild(this.el_lst); }; Tab.prototype.initBodyElement = function() { - var chat_input = document.createElement('input'), - chat_input_w = document.createElement('div'); + var chat_input = document.createElement("input"), + chat_input_w = document.createElement("div"); - this.chat_log = document.createElement('p'); - this.chat_log.classList.add('chat-log'); - this.el_body = document.createElement('div'); - this.el_body.classList.add('tab-body'); + this.chat_log = document.createElement("pre"); + this.chat_log.classList.add("chat-log"); + this.el_body = document.createElement("div"); + this.el_body.classList.add("tab-body"); this.el_body.id = this.name; - this.el_body.style.display = 'none'; - chat_input_w.classList.add('chat-input-wrapper'); - chat_input.classList.add('chat-input'); - chat_input.setAttribute('type', 'text'); + this.el_body.style.display = "none"; + chat_input_w.classList.add("chat-input-wrapper"); + chat_input.classList.add("chat-input"); + chat_input.setAttribute("type", "text"); chat_input_w.appendChild(chat_input); this.el_body.appendChild(this.chat_log); @@ -70,7 +78,7 @@ Tab.prototype.initBodyElement = function() { }; Tab.prototype.close = function() { - if (typeof this.closeHandler !== 'undefined') { + if (typeof this.closeHandler !== "undefined") { this.closeHandler(); } this.wr_body.removeChild(this.el_body); @@ -78,25 +86,29 @@ Tab.prototype.close = function() { }; Tab.prototype.hide = function() { - this.el_lst.classList.remove('tab-current'); - this.el_body.style.display = 'none'; + this.el_lst.classList.remove("tab-current"); + this.el_body.style.display = "none"; }; Tab.prototype.show = function() { - if (typeof this.showHandler !== 'undefined') { + if (typeof this.showHandler !== "undefined") { this.showHandler(); } - this.el_lst.classList.remove('tab-active'); - this.el_lst.classList.add('tab-current'); - this.el_body.style.display = 'block'; + this.el_lst.classList.remove("tab-active"); + this.el_lst.classList.add("tab-current"); + this.el_body.style.display = "block"; this.chat_log.scrollTop = 42000; }; +Tab.prototype.isCurrent = function() { + return this.el_lst.classList.contains("tab-current"); +}; + Tab.prototype.setActive = function() { - this.el_lst.classList.add('tab-active'); + this.el_lst.classList.add("tab-active"); } Tab.prototype.appendText = function(text) { // TODO: flush text - this.chat_log.innerHTML += text + '
'; + this.chat_log.innerHTML += text; }; diff --git a/manifest.json b/manifest.json index 148bb7f..5d1e4c0 100644 --- a/manifest.json +++ b/manifest.json @@ -1,6 +1,6 @@ { "name": "chromesoul", - "version": "0.2.1", + "version": "0.3.0", "minimum_chrome_version": "24", "manifest_version": 2, "offline_enabled": false, diff --git a/start.js b/start.js index 417eaae..fc4295c 100644 --- a/start.js +++ b/start.js @@ -1,16 +1,17 @@ -var om, ui, cs; +(function() { + var om, ui, cs; + om = new OptionsManager(); + om.init(); -om = new OptionsManager(); -om.init(); + ui = new Nsui(); + ui.init(); -ui = new Nsui(); -ui.init(); + cs = new NsClient(); + cs.init(); -cs = new NsClient(); -cs.init(); - -ui.setReconnect(function() { - cs.disconnect(); - cs.connect(); -}); + ui.setReconnect(function() { + cs.disconnect(); + cs.connect(); + }); +})();