comparison lightstring.js @ 2:f31a75c3b6c8

code cleaning
author Sonny Piers <sonny.piers@gmail.com>
date Sun, 18 Dec 2011 22:57:47 +0100
parents 96087680669f
children 029c12b8f048
comparison
equal deleted inserted replaced
1:96087680669f 2:f31a75c3b6c8
1 'use strict'; 1 'use strict';
2
3 /**
4 Copyright (c) 2011, Sonny Piers <sonny at fastmail dot net>
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
2 18
3 var Lightstring = { 19 var Lightstring = {
4 NS: {}, 20 NS: {},
5 stanza: {}, 21 stanza: {},
6 }; 22 };
7 23
8 Lightstring.Connection = function (aURL) { 24 Lightstring.Connection = function (aService) {
9 var parser = new DOMParser(); 25 var parser = new DOMParser();
10 var serializer = new XMLSerializer(); 26 var serializer = new XMLSerializer();
27 this.service = aService;
11 this.handlers = {}; 28 this.handlers = {};
12 this.iqid = 1024; 29 this.iqid = 1024;
13 this.getNewId = function() { 30 this.getNewId = function() {
14 this.iqid++; 31 this.iqid++;
15 return 'sendiq:'+this.iqid; 32 return 'sendiq:'+this.iqid;
18 return parser.parseFromString(str, 'text/xml').documentElement; 35 return parser.parseFromString(str, 'text/xml').documentElement;
19 }; 36 };
20 this.serialize = function(elm) { 37 this.serialize = function(elm) {
21 return serializer.serializeToString(elm); 38 return serializer.serializeToString(elm);
22 }; 39 };
23 this.connect = function(jid, password) { 40 this.connect = function(aJid, aPassword) {
24 this.domain = jid.split('@')[1]; 41 if(aJid)
25 this.node = jid.split('@')[0]; 42 this.jid = aJid;
26 this.jid = jid; 43 if(this.jid) {
27 this.password = password; 44 this.domain = this.jid.split('@')[1];
28 if(typeof WebSocket === 'undefined') 45 this.node = this.jid.split('@')[0];
29 this.socket = new MozWebSocket(aURL); 46 this.resource = this.jid.split('/')[1];
47 }
48 if(aPassword)
49 this.password = aPassword;
50
51 if(!this.jid)
52 throw "Lightstring: Connection.jid is undefined.";
53 if(!this.password)
54 throw "Lightstring: Connection.password is undefined.";
55 if(!this.service)
56 throw "Lightstring: Connection.service is undefined.";
57
58 //"Bug 695635 - tracking bug: unprefix WebSockets" https://bugzil.la/695635
59 if(MozWebSocket)
60 this.socket = new MozWebSocket(this.service);
61 else if(WebSocket)
62 this.socket = new WebSocket(this.service);
30 else 63 else
31 this.socket = new WebSocket(aURL); 64 this.emit('error', 'No WebSocket support.');
32 65
33 var that = this; 66 var that = this;
34 this.socket.addEventListener('open', function() { 67 this.socket.addEventListener('open', function() {
35 that.emit('open'); 68 that.emit('connecting');
36 that.send("<stream:stream to='"+that.domain+"' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' />"); 69 //FIXME there shouldn't be an ending "/"
37 }); 70 that.send(
38 this.socket.addEventListener('error', function(err) { 71 "<stream:stream to='"+that.domain+"'\
39 that.emit('error'); 72 xmlns='jabber:client'\
40 }); 73 xmlns:stream='http://etherx.jabber.org/streams'\
41 this.socket.addEventListener('close', function(close) { 74 version='1.0'/>"
42 that.emit('close'); 75 );
43 that.emit('disconnected'); 76 });
77 this.socket.addEventListener('error', function(e) {
78 that.emit('error', e.data);
79 });
80 this.socket.addEventListener('close', function(e) {
81 that.emit('disconnected', e.data);
44 }); 82 });
45 this.socket.addEventListener('message', function(e) { 83 this.socket.addEventListener('message', function(e) {
46 that.emit('XMLInput', e.data); 84 that.emit('XMLInput', e.data);
47 var elm = that.parse(e.data); 85 var elm = that.parse(e.data);
48 that.emit('DOMInput', elm); 86 that.emit('DOMInput', elm);
49 that.emit(elm.tagName, elm); 87 that.emit(elm.tagName, elm);
50 88
51 if((elm.tagName === 'iq')) 89 if(elm.tagName === 'iq')
52 that.emit(elm.getAttribute('id'), elm); 90 that.emit(elm.getAttribute('id'), elm);
53 }); 91 });
54 }; 92 };
55 93 this.send = function(aStanza, aCallback) {
56 94 if(typeof aStanza === 'string') {
57 95 var str = aStanza;
58 this.send = function(stanza, callback) { 96 var elm = this.parse(str);
59 //FIXME support for E4X 97 }
60 //~ if(typeof stanza === 'xml') { 98 else if(aStanza instanceof Element) {
61 //~ stanza = stanza.toXMLString(); 99 var elm = aStanza;
62 //~ } 100 var str = this.serialize(elm);
63 if(stanza.cnode) {
64 stanza = stanza.toString();
65 //~ console.log(typeof stanza);
66 }
67 if(typeof stanza === 'string') {
68 var str = stanza;
69 var elm = this.parse(stanza);
70 } 101 }
71 else { 102 else {
72 var elm = stanza; 103 that.emit('error', 'Unsupported data type.');
73 var str = this.serialize(stanza);
74 } 104 }
75 105
76 106
77 if(elm.tagName === 'iq') { 107 if(elm.tagName === 'iq') {
78 var id = elm.getAttribute('id'); 108 var id = elm.getAttribute('id');
79 if(!id) { 109 if(!id) {
80 elm.setAttribute('id', this.getNewId()) 110 elm.setAttribute('id', this.getNewId())
81 str = this.serialize(elm) 111 str = this.serialize(elm)
82 } 112 }
83 if(callback) this.on(elm.getAttribute('id'), callback); 113 if(aCallback)
114 this.on(elm.getAttribute('id'), aCallback);
115 }
116 else if(aCallback) {
117 that.emit('warning', 'Callback can\'t be called with non-iq stanza.');
84 } 118 }
85 119
86 120
87 this.socket.send(str); 121 this.socket.send(str);
88 this.emit('XMLOutput', str); 122 this.emit('XMLOutput', str);
89 this.emit('DOMOutput', elm); 123 this.emit('DOMOutput', elm);
90 }; 124 };
91 this.disconnect = function() { 125 this.disconnect = function() {
126 this.emit('disconnecting');
92 this.send('</stream:stream>'); 127 this.send('</stream:stream>');
93 this.emit('disconnected');
94 this.socket.close(); 128 this.socket.close();
129 this.emit('disconnected');
95 }; 130 };
96 //FIXME Callbacks sucks, better idea?
97 this.emit = function(name, data) { 131 this.emit = function(name, data) {
98 var handlers = this.handlers[name]; 132 var handlers = this.handlers[name];
99 if(!handlers) 133 if(!handlers)
100 return; 134 return;
101 135
128 mechanisms[nodes[i].textContent] = true; 162 mechanisms[nodes[i].textContent] = true;
129 163
130 164
131 //FIXME support SCRAM-SHA1 && allow specify method preferences 165 //FIXME support SCRAM-SHA1 && allow specify method preferences
132 if('DIGEST-MD5' in mechanisms) 166 if('DIGEST-MD5' in mechanisms)
133 that.send("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>"); 167 that.send(
168 "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl'\
169 mechanism='DIGEST-MD5'/>"
170 );
134 else if('PLAIN' in mechanisms) { 171 else if('PLAIN' in mechanisms) {
135 var token = btoa(that.jid + "\u0000" + that.jid.split('@')[0] + "\u0000" + that.password); 172 var token = btoa(
136 that.send("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>"+token+"</auth>"); 173 that.jid +
174 "\u0000" +
175 that.jid.node +
176 "\u0000" +
177 that.password
178 );
179 that.send(
180 "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl'\
181 mechanism='PLAIN'>"+token+"</auth>"
182 );
137 } 183 }
138 } 184 }
139 //XMPP features 185 //XMPP features
140 else { 186 else {
141 that.emit('features', stanza); 187 that.emit('features', stanza);
142 //Bind http://xmpp.org/rfcs/rfc3920.html#bind 188 //Bind http://xmpp.org/rfcs/rfc3920.html#bind
143 that.send("<iq type='set' xmlns='jabber:client'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/></iq>", function() { 189 that.send(
190 "<iq type='set' xmlns='jabber:client'>\
191 <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>\
192 </iq>",
193 function() {
144 //Session http://xmpp.org/rfcs/rfc3921.html#session 194 //Session http://xmpp.org/rfcs/rfc3921.html#session
145 that.send("<iq type='set' xmlns='jabber:client'><session xmlns='urn:ietf:params:xml:ns:xmpp-session'/></iq>", function() { 195 that.send(
146 that.emit('connected'); 196 "<iq type='set' xmlns='jabber:client'>\
147 }); 197 <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>\
198 </iq>",
199 function() {
200 that.emit('connected');
201 }
202 );
148 }); 203 });
149 } 204 }
150 }); 205 });
151 //Internal 206 //Internal
152 this.on('success', function(stanza, that) { 207 this.on('success', function(stanza, that) {
153 that.send("<stream:stream to='"+that.domain+"' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' />"); 208 that.send(
209 "<stream:stream to='"+that.domain+"'\
210 xmlns='jabber:client'\
211 xmlns:stream='http://etherx.jabber.org/streams'\
212 version='1.0' />"
213 );
154 }); 214 });
155 //Internal 215 //Internal
156 this.on('challenge', function(stanza, that) { 216 this.on('challenge', function(stanza, that) {
157 //FIXME this is mostly Strophe code 217 //FIXME this is mostly Strophe code
158 218
193 253
194 var digest_uri = "xmpp/" + that.domain; 254 var digest_uri = "xmpp/" + that.domain;
195 if (host !== null) { 255 if (host !== null) {
196 digest_uri = digest_uri + "/" + host; 256 digest_uri = digest_uri + "/" + host;
197 } 257 }
198
199 var A1 = MD5.hash(that.node + 258 var A1 = MD5.hash(that.node +
200 ":" + realm + ":" + that.password) + 259 ":" + realm + ":" + that.password) +
201 ":" + nonce + ":" + cnonce; 260 ":" + nonce + ":" + cnonce;
202 var A2 = 'AUTHENTICATE:' + digest_uri; 261 var A2 = 'AUTHENTICATE:' + digest_uri;
203 262
213 MD5.hexdigest(MD5.hexdigest(A1) + ":" + 272 MD5.hexdigest(MD5.hexdigest(A1) + ":" +
214 nonce + ":00000001:" + 273 nonce + ":00000001:" +
215 cnonce + ":auth:" + 274 cnonce + ":auth:" +
216 MD5.hexdigest(A2))) + ','; 275 MD5.hexdigest(A2))) + ',';
217 responseText += 'charset="utf-8"'; 276 responseText += 'charset="utf-8"';
218 277 that.send(
219 that.send("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"+btoa(responseText)+"</response>"); 278 "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"
279 +btoa(responseText)+
280 "</response>");
220 }); 281 });
221 }; 282 };