Mercurial > psc
view xmpp.js @ 0:a2dab4544b2d default tip
Initial commit.
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Fri, 28 Jan 2011 02:35:04 +0100 |
parents | |
children |
line wrap: on
line source
#!/usr/bin/env node var nc = require('ncurses') var consts = require('ncconsts') var sys = require('sys'); var fs = require('fs'); var xmpp = require('clxmpp'); var config = require('./config'); var forms = require('./forms'); var NS = require('./namespaces'); var options = { xml: false, command_parsing: false, chr: false } var st = [ 'Error', 'Connecting', 'Connfail', 'Authenticating', 'Authfail', 'Connected', 'Disconnected', 'Disconnecting' ]; var jid = config.jid.split('@'); var user = jid[0]; var server = jid[1]; var history = []; process.addListener('SIGWINCH', function (err) { win.resize(win.lines, win.cols); win.hline(win.height-2, 0, win.width); win.setscrreg(0, win.height-3); win.cursor(win.height-1, 0); win.clearok(true); win.refresh(); appendLine('Window size changed to ' + win.lines + ' x ' + win.cols + '!'); }); process.addListener('uncaughtException', function (err) { appendLine('Uncaught exception (' + err + '), this should never happen:\n' + err.stack + '.'); }); // ============================================================================ // FUNCTIONS // ============================================================================ var getTimestamp = function() { var time = new Date(); var hours = time.getHours(); var mins = time.getMinutes(); return '' + (hours < 10 ? '0' : '') + hours + ':' + (mins < 10 ? '0' : '') + mins; }; var appendLine = function(message) { var curx=win.curx; win.scroll(1); win.cursor(win.height-3, 0); win.print('[' + getTimestamp() + '] '); if (arguments.length > 1 && arguments[1]) { if (win.hasColors) win.attrset(win.colorPair(1)); win.print('== '); } if (arguments.length > 2 && arguments[2] && win.hasColors) win.attrset(arguments[2]); win.print('' + message); if (arguments.length > 2 && arguments[2] && win.hasColors) win.attrset(win.colorPair(0)); if (arguments.length > 3) { win.cursor(win.height-1, 0); win.clrtoeol(); } else win.cursor(win.height-1, curx); win.refresh(); }; var cleanup = function() { win.clear(); win.refresh(); win.close(); process.exit(0); }; // ============================================================================ // HANDLERS // ============================================================================ var on = { configure: function(stanza) { var pubsub = stanza.getChild('pubsub', NS.PUBSUB_OWNER); var configure = pubsub.getChild('configure', NS.PUBSUB_OWNER); var nodeID = configure.getAttribute('node'); if (nodeID) var text = 'Configuration of node “' + nodeID + '”:'; else var text = 'Configuration of a new node:'; var x = configure.getChild('x', 'jabber:x:data'); var form = forms.parse(x); text += forms.toString(form); next.func = function() { var stanza = xmpp.iq({to: config.server, id: conn.getUniqueId(), type: 'set'}) .c('pubsub', {xmlns: NS.PUBSUB_OWNER}) .c('configure', {node: nodeID}); var form = forms.build(); stanza.cnode(form); return stanza; }; appendLine(text); }, default: function(stanza) { var pubsub = stanza.getChild('pubsub', NS.PUBSUB); var def = pubsub.getChild('default', NS.PUBSUB); var nodeID = def.getAttribute('node'); if (nodeID) var text = 'Options of a new subscription on ' + nodeID + ':'; else var text = 'Subscription options on a new node:'; var x = def.getChild('x', 'jabber:x:data'); var form = forms.parse(x); text += '\nOptions:' + forms.toString(form); appendLine(text); }, 'delete': function(stanza) { appendLine('Node correctly deleted.'); }, purge: function(stanza) { appendLine('Node correctly purged.'); }, info: function(stanza) { var query = stanza.getChild('query', NS.DISCO_INFO); var list = query.tags; var nodeID = query.getAttribute('node'); var text = ''; for (var i in list) { if (list[i].name == 'identity') { if (nodeID) text += '"' + nodeID + '"'; else text += '"' + list[i].getAttribute('name') + '"'; text += ', ' + list[i].getAttribute('category') + ' ' + list[i].getAttribute('type') + ', features:'; } else if (list[i].name == 'feature') text += '\n\t' + list[i].getAttribute('var'); } var x = query.getChild('x', 'jabber:x:data'); if (x) { var form = forms.parse(x); text += '\nMetadata:' + forms.toString(form); } appendLine(text); }, list: function(stanza) { var query = stanza.getChild('query', NS.DISCO_ITEMS); var list = query.tags; var nodeID = query.getAttribute('node'); if (nodeID) var text = 'Nodes in ' + nodeID + ':'; else var text = 'Nodes:'; for (var i in list) { var node = list[i].getAttribute('node'); if (node) { text += '\n\t' + list[i].getAttribute('node'); var name = list[i].getAttribute('name'); if (name) text += ' (' + name + ')'; } } if (nodeID) text += '\nItems in ' + nodeID + ':'; else text += '\nItems:'; for (var i in list) { var node = list[i].getAttribute('node'); if (!node) text += '\n\t' + list[i].getAttribute('name'); } appendLine(text); }, manage: function(stanza) { var pubsub = stanza.getChild('pubsub', NS.PUBSUB_OWNER); var tag = pubsub.tags[0]; if (tag.attr.xmlns != NS.PUBSUB_OWNER) return; var action = tag.name; if (action != 'subscriptions' && action != 'affiliations') return; var nodeID = tag.getAttribute('node'); if (!nodeID) return; var text = action[0].toUpperCase() + action.substr(1) + ' in ' + nodeID + ':'; for (var i in tag.tags) { var sub = tag.tags[i]; var jid = sub.getAttribute('jid'); text += '\n\t' + jid; if (action == 'subscriptions') { var subscription = sub.getAttribute('subscription'); var subid = sub.getAttribute('subid'); text += ' (' + subid + '): ' + subscription; } else { var affiliation = sub.getAttribute('affiliation'); text += ': ' + affiliation; } } next.func = function() { var tag = this.tag; var name = tag.substr(0, tag.length-1); var stanza = xmpp.iq({to: config.server, id: conn.getUniqueId(), type: 'set'}) .c('pubsub', {xmlns: NS.PUBSUB_OWNER}) .c(tag, {node: nodeID}); for (var i in this) { if (!(this[i] instanceof Array)) continue; if (this[i].length != 2) continue; var obj = {jid: this[i][0]}; obj[name] = this[i][1]; stanza.c(name, obj).up(); } return stanza; }; next.tag = action; appendLine(text); }, options: function(stanza) { var pubsub = stanza.getChild('pubsub', NS.PUBSUB); var options = pubsub.getChild('options', NS.PUBSUB); var nodeID = options.getAttribute('node'); var jid = options.getAttribute('jid'); var text = 'Options of ' + jid + ' on ' + nodeID + ':'; var x = options.getChild('x', 'jabber:x:data'); var form = forms.parse(x); text += '\nOptions:' + forms.toString(form); appendLine(text); }, error: function(stanza) { var error = stanza.getChild('error'); var type = error.getAttribute('type'); var name = error.tags[0].name; var text = 'Got error of type ' + type + ': ' + name; if (error.tags[1]) { var name2 = error.tags[1].name; text += ', '; var feature = error.tags[1].getAttribute('feature'); if (feature) text += 'feature ' + feature + ' '; text += name2; } text += '.'; appendLine(text, false, win.colorPair(1)); } }; // ============================================================================ // COMMANDS // ============================================================================ var next = {}; var commands = { create: function(args) { var nodeID = args[0]; var stanza = xmpp.iq({to: config.server, id: conn.getUniqueId(), type: 'set'}) .c('pubsub', {xmlns: NS.PUBSUB}) .c('create', {node: nodeID}); conn.sendIQ(stanza, function(stanza) { appendLine('Node correctly created.'); }, on.error); }, configure: function(args) { if (args < 1) return; var nodeID = args[0]; var stanza = xmpp.iq({to: config.server, id: conn.getUniqueId(), type: 'get'}) .c('pubsub', {xmlns: NS.PUBSUB_OWNER}) .c('configure', {node: nodeID}); conn.sendIQ(stanza, on.configure, on.error); }, default: function(args) { var nodeID = args[0]; if (nodeID) var stanza = xmpp.iq({to: config.server, id: conn.getUniqueId(), type: 'get'}) .c('pubsub', {xmlns: NS.PUBSUB}) .c('default', {node: nodeID}); else var stanza = xmpp.iq({to: config.server, id: conn.getUniqueId(), type: 'get'}) .c('pubsub', {xmlns: NS.PUBSUB}) .c('default'); conn.sendIQ(stanza, on.default, on.error); }, 'delete': function(args) { var nodeID = args[0]; var stanza = xmpp.iq({to: config.server, id: conn.getUniqueId(), type: 'set'}) .c('pubsub', {xmlns: NS.PUBSUB_OWNER}) .c('delete', {node: nodeID}); conn.sendIQ(stanza, on.delete, on.error); }, purge: function(args) { var nodeID = args[0]; var stanza = xmpp.iq({to: config.server, id: conn.getUniqueId(), type: 'set'}) .c('pubsub', {xmlns: NS.PUBSUB_OWNER}) .c('purge', {node: nodeID}); conn.sendIQ(stanza, on.purge, on.error); }, info: function(args) { if (args.length) var disco = xmpp.iq({to: config.server, id: conn.getUniqueId(), type: 'get'}) .c('query', {xmlns: NS.DISCO_INFO, node: args[0]}); else var disco = xmpp.iq({to: config.server, id: conn.getUniqueId(), type: 'get'}) .c('query', {xmlns: NS.DISCO_INFO}); conn.sendIQ(disco, on.info, on.error); }, help: function(args) { if (args.length == 0) { var text = 'Available commands:'; for (var i in commands) text += '\n\t' + i; } else { var text = 'Commands:'; for (var i in commands) for (var j in args) if (i == args[j]) text += '\n\t' + i; } appendLine(text); }, list: function(args) { if (args.length) var disco = xmpp.iq({to: config.server, id: conn.getUniqueId(), type: 'get'}) .c('query', {xmlns: NS.DISCO_ITEMS, node: args[0]}); else var disco = xmpp.iq({to: config.server, id: conn.getUniqueId(), type: 'get'}) .c('query', {xmlns: NS.DISCO_ITEMS}); conn.sendIQ(disco, on.list, on.error); }, manage: function(args) { if (args.length < 2) return; var action = args[0]; var nodeID = args[1]; if (!nodeID) return; if (action != 'subscriptions' && action != 'affiliations') return; var stanza = xmpp.iq({to: config.server, id: conn.getUniqueId(), type: 'get'}) .c('pubsub', {xmlns: NS.PUBSUB_OWNER}) .c(action, {node: nodeID}); conn.sendIQ(stanza, on.manage, on.error); }, options: function(args) { if (args.length < 1) return; var nodeID = args[0]; var stanza = xmpp.iq({to: config.server, id: conn.getUniqueId(), type: 'get'}) .c('pubsub', {xmlns: NS.PUBSUB}) .c('options', {node: nodeID, jid: config.jid}); conn.sendIQ(stanza, on.opions, on.error); }, quit: function() { conn.send('</stream:stream>'); cleanup(); }, refresh: function() { win.clearok(true); appendLine('Refresh, done!'); }, retrieve: function(args) { if (args.length < 1) return; var action = args[0]; var nodeID = args[1]; if (action == 'subscriptions') { if (nodeID) var stanza = xmpp.iq({to: config.server, id: conn.getUniqueId(), type: 'get'}) .c('pubsub', {xmlns: NS.PUBSUB}) .c('subscriptions', {node: nodeID}); else var stanza = xmpp.iq({to: config.server, id: conn.getUniqueId(), type: 'get'}) .c('pubsub', {xmlns: NS.PUBSUB}) .c('subscriptions'); conn.sendIQ(stanza, function(stanza) { var pubsub = stanza.getChild('pubsub', NS.PUBSUB); var subscriptions = pubsub.getChild('subscriptions', NS.PUBSUB); var nodeID = subscriptions.getAttribute('node'); if (nodeID) var text = 'Subscriptions in ' + nodeID + ':'; else var text = 'Subscriptions:'; for (var i in subscriptions.tags) { var sub = subscriptions.tags[i]; text += '\n\t' + sub.getAttribute('jid') + ', ' + sub.getAttribute('subscription') + ' to node ' + sub.getAttribute('node') + ' (' + sub.getAttribute('subid') + ')'; } appendLine(text); }, on.error); } else if (action == 'affiliations') { if (nodeID) var stanza = xmpp.iq({to: config.server, id: conn.getUniqueId(), type: 'get'}) .c('pubsub', {xmlns: NS.PUBSUB}) .c('affiliations', {node: nodeID}); else var stanza = xmpp.iq({to: config.server, id: conn.getUniqueId(), type: 'get'}) .c('pubsub', {xmlns: NS.PUBSUB}) .c('affiliations'); conn.sendIQ(stanza, function(stanza) { var pubsub = stanza.getChild('pubsub', NS.PUBSUB); var affiliations = pubsub.getChild('affiliations', NS.PUBSUB); var text = 'Affiliations:'; for (var i in affiliations.tags) { var affil = affiliations.tags[i]; text += '\n\t' + affil.getAttribute('affiliation') + ' of node ' + affil.getAttribute('node') + '.'; } appendLine(text); }, on.error); } }, send: function() { var stanza = next.func(); appendLine(sys.inspect(stanza, false, null)); conn.sendIQ(stanza, function() { next = {}; }, on.error); }, sendFile: function(args) { if (args.length < 1) return; fs.readFile(args[0], function(err, data) { if (err) return; conn.send(data.toString()); }); }, set: function(args) { if (args.length < 1) return; var key = args.shift(); if (!args.length) delete next[key]; else next[key] = args; appendLine(sys.inspect(next)); }, subscribe: function(args) { if (args.length < 1) return; var nodeID = args[0]; var stanza = xmpp.iq({to: config.server, id: conn.getUniqueId(), type: 'set'}) .c('pubsub', {xmlns: NS.PUBSUB}) .c('subscribe', {node: nodeID, jid: config.jid}); conn.sendIQ(stanza, function(stanza) { var pubsub = stanza.getChild('pubsub', NS.PUBSUB); var subscription = pubsub.getChild('subscription', NS.PUBSUB); var text = subscription.getAttribute('jid') + ' ' + subscription.getAttribute('subscription') + ' to ' + subscription.getAttribute('node') + ': ' + subscription.getAttribute('subid') + '.'; var subOpt = subscription.getChild('subscribe-options', NS.PUBSUB); if (subOpt && subOpt.getChild('required', NS.PUBSUB)) text += ' Warning: you should configure your subscription!'; appendLine(text); }, on.error); }, unsubscribe: function(args) { if (args.length < 1) return; var nodeID = args[0]; var stanza = xmpp.iq({to: config.server, id: conn.getUniqueId(), type: 'set'}) .c('pubsub', {xmlns: NS.PUBSUB}) .c('unsubscribe', {node: nodeID, jid: config.jid}); conn.sendIQ(stanza, function(stanza) { appendLine('Node correctly unsubscribed.'); }, on.error); }, activate: function(args) { if (args[0] == 'xml') conn.log = function(_, m) { if (m.indexOf('--> ') == 0) appendLine(m.substr(4), false, COLOR_IN); else if (m.indexOf('<== ') == 0) appendLine(m.substr(4), false, COLOR_OUT); else appendLine(m); }; }, deactivate: function(args) { if (args[0] == 'xml') conn.log = function(_, m) {}; }, toggle: function(args) { if (args[0] == 'xml') { if (options.xml) conn.log = function(_, m) {}; else conn.log = function(_, m) { if (m.indexOf('--> ') == 0) appendLine(m.substr(4), false, COLOR_IN); else if (m.indexOf('<== ') == 0) appendLine(m.substr(4), false, COLOR_OUT); else appendLine(m); }; options.xml = !options.xml; } if (args[0] in options) options[args[0]] = !options[args[0]]; appendLine(args[0] + ' ' + options[args[0]]); }, }; // ============================================================================ parseCommand = function(line) { var all = line.split(' '); var command = all.shift().substr(1); var args = []; var multi = ''; for (var i in all) { if (all[i][0] == '"') multi += all[i].substr(1)+' '; else { if (multi != '') { multi += all[i]; if (all[i][all[i].length-1] == '"') { multi = multi.substr(0, multi.length-1); args.push(multi); multi = ''; } } else args.push(all[i]); } } return {command: command, args: args}; } // ============================================================================ var win = new nc.ncWindow(); var inputLine = ''; var hline = 0; var pos = 0; win.addListener('inputChar', function (chr, intval) { if (options.chr) appendLine(chr + ' ' + intval); if (intval == consts.keys['BACKSPACE'] || intval == 7 || intval == 127) { win.delch(); if (inputLine.length) { inputLine = inputLine.substr(0, inputLine.length-1); if (pos > 0) pos--; } } else if (chr == consts.DOWN || intval == 2) { if (!history.length) return; if (hline > history.length-1); else if (hline == history.length-1) { inputLine = ''; hline++; } else inputLine = history[++hline]; } else if (chr == consts.UP || intval == 3) { if (!history.length) return; if (hline > 0) inputLine = history[--hline]; } else if (chr == consts.LEFT || intval == 4) { if (inputLine.length && pos > 0) pos--; } else if (chr == consts.RIGHT || intval == 5) { if (inputLine.length && pos < inputLine.length) pos++; } else if (chr == '\n') { if (inputLine[0] == '/') { var command = parseCommand(inputLine); if (options.command_parsing) appendLine(sys.inspect(command)); var notcommand = true; for (var i in commands) { if (i == command.command) { commands[i](command.args); notcommand = false; break; } } if (notcommand) appendLine('Command unknown.'); } else if (inputLine[0] == '<') { conn.send(inputLine); } else if (!inputLine.length); else appendLine('Only commands and XML are allowed.'); history.push(inputLine); hline = history.length; inputLine = ''; pos = 0; } else { if (inputLine.length == pos) inputLine += chr; else { var tmp = inputLine.substr(0, pos); tmp += chr; tmp += inputLine.substr(pos, inputLine.length); inputLine = tmp; } pos++; } win.deleteln(); win.cursor(win.height-1, 0); win.print(inputLine); win.cursor(win.height-1, pos); win.refresh(); }); // Setup colors var COLOR_IN = win.colorPair(2); var COLOR_OUT = win.colorPair(3); var COLOR_QUIT = win.colorPair(6); var COLOR_ACTION = win.colorPair(5); // Setup window win.clear(); win.echo = false; win.scrollok(true); win.hline(win.height-2, 0, win.width); win.setscrreg(0, win.height-3); // Leave one line at the top for the channel name and optional topic win.cursor(win.height-1, 0); win.refresh(); var conn = new xmpp.Connection(server); conn.log = function(_, m) { if (m.indexOf('--> ') == 0) appendLine(m.substr(4), false, COLOR_IN); else if (m.indexOf('<== ') == 0) appendLine(m.substr(4), false, COLOR_OUT); else appendLine(m); }; conn.connect(user, server, config.password, function (status, condition) { appendLine(st[status] + (condition?(' ('+condition+')'):''), true, COLOR_ACTION); });