Mercurial > eldonilo > lightstring
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 }; |