hotspotlogin 0.1.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,844 @@
1
+ /**
2
+ * ChilliLibrary.js
3
+ * V2.0
4
+ *
5
+ * This Javascript library can be used to create HTML/JS browser
6
+ * based smart clients (BBSM) for the CoovaChilli access controller
7
+ * Coova Chilli rev 81 or higher is required
8
+ *
9
+ * This library creates four global objects :
10
+ *
11
+ * - chilliController Expose session/client state and
12
+ * connect()/disconnect() methods the to BBSM.
13
+ *
14
+ * - chilliJSON INTERNAL (should not be called from the BBSM).
15
+ * Issues a command to the chilli daemon by adding a new <SCRIPT>
16
+ * tag to the HTML DOM (this hack enables cross server requests).
17
+ *
18
+ * - chilliClock Can be used by BBSMs to display a count down.
19
+ * Will sync with chilliController for smooth UI display (not yet implemented)
20
+ *
21
+ * - chilliLibrary Expose API and library versions
22
+ *
23
+ * For more information http://www.coova.org/CoovaChilli/JSON
24
+ *
25
+ * TODO :
26
+ * - Fine tune level of debug messages
27
+ * - Define error code when invoking onError
28
+ * - Retry mechanism after a JSON request fails
29
+ * - Delay clock tick when there is already an ongoing request
30
+ * - Use a true JSON parser to validate what we received
31
+ * - Use idleTime and idleTimeout to re-schedule autofresh after
32
+ * a likely idle termination by chilli
33
+ * - check that the library can be compiled as a Flash swf library
34
+ * and used from Flash BBSMs with the same API.
35
+ *
36
+ * Copyright (C) Y.Deltroo 2007
37
+ * Distributed under the BSD License
38
+ *
39
+ * This file also contains third party code :
40
+ * - MD5, distributed under the BSD license
41
+ * http://pajhome.org.uk/crypt/md5
42
+ *
43
+ */
44
+
45
+ var chilliLibrary = { revision:'85' , apiVersion:'2.0' } ;
46
+
47
+
48
+ /**
49
+ * Global chilliController object
50
+ *
51
+ * CONFIGUARION PROPERTIES
52
+ * -----------------------
53
+ * ident (String)
54
+ * Hex encoded string (used for client side CHAP-Password calculations)
55
+ *
56
+ * interval (Number)
57
+ * Poll the gateway every interval, in seconds
58
+ *
59
+ * host (String)
60
+ * IP address of the controller (String)
61
+ *
62
+ * port (Number)
63
+ * UAM port to direct request to on the gateway
64
+ *
65
+ * ssl (Boolean)
66
+ * Shall we use HTTP or HTTPS to communicate with the chilli controller
67
+ *
68
+ * uamService : String
69
+ * !!! EXPERIMENTAL FEATURE !!!
70
+ * URL to external uamService script (used for external MD5 calculation when portal/chilli trust is required)
71
+ * This remote script runs on a SSL enable web server, and knows UAM SECRET.
72
+ * The chilliController javascript object will send the password over SSL (and challenge for CHAP)
73
+ * UAM SERVICE should reply with a JSON response containing
74
+ * - CHAP logon : CHAP-Password X0Red with UAM SECRET
75
+ * - PAP logon : Password XORed with UAM SECRET
76
+ *
77
+ * For more information http://www.coova.org/CoovaChilli/JSON
78
+ *
79
+ */
80
+
81
+ if (!chilliController || !chilliController.host)
82
+ var chilliController = { interval:30 , host:"1.0.0.1" , port:false , ident:'00' , ssl:false , uamService: false };
83
+
84
+ /* Define clientState numerical code constants */
85
+ chilliController.stateCodes = { UNKNOWN:-1 , NOT_AUTH:0 , AUTH:1 , AUTH_PENDING:2 , AUTH_SPLASH:3 } ;
86
+
87
+ /* Initializing session and accounting members, objet properties */
88
+ chilliController.session = {} ;
89
+ chilliController.accounting = {} ;
90
+ chilliController.redir = {} ;
91
+
92
+ chilliController.location = { name: '' } ;
93
+ chilliController.challenge = '' ;
94
+ chilliController.message = '' ;
95
+ chilliController.clientState = chilliController.stateCodes.UNKNOWN ;
96
+ chilliController.command = '' ;
97
+ chilliController.autorefreshTimer = 0 ;
98
+
99
+ /* This method returns the root URL for commands */
100
+ chilliController.urlRoot = function () {
101
+ var protocol = ( chilliController.ssl ) ? "https" : "http" ;
102
+ var urlRoot = protocol + "://" + chilliController.host + (chilliController.port ? ":" + chilliController.port.toString() : "") + "/json/" ;
103
+ return urlRoot;
104
+ };
105
+
106
+ /* Default event handlers */
107
+ chilliController.onUpdate = function ( cmd ) {
108
+ log('>> Default onUpdate handler. <<\n>> You should write your own. <<\n>> cmd = ' + cmd + ' <<' );
109
+ };
110
+
111
+ chilliController.onError = function ( str ) {
112
+ log ( '>> Default Error Handler<<\n>> You should write your own <<\n>> ' + str + ' <<' );
113
+ };
114
+
115
+
116
+ chilliController.formatTime = function ( t , zeroReturn ) {
117
+
118
+ if ( typeof(t) == 'undefined' ) {
119
+ return "Not available";
120
+ }
121
+
122
+ t = parseInt ( t , 10 ) ;
123
+ if ( (typeof (zeroReturn) !='undefined') && ( t === 0 ) ) {
124
+ return zeroReturn;
125
+ }
126
+
127
+ var h = Math.floor( t/3600 ) ;
128
+ var m = Math.floor( (t - 3600*h)/60 ) ;
129
+ var s = t % 60 ;
130
+
131
+ var s_str = s.toString();
132
+ if (s < 10 ) { s_str = '0' + s_str; }
133
+
134
+ var m_str = m.toString();
135
+ if (m < 10 ) { m_str= '0' + m_str; }
136
+
137
+ var h_str = h.toString();
138
+ if (h < 10 ) { h_str= '0' + h_str; }
139
+
140
+
141
+ if ( t < 60 ) { return s_str + 's' ; }
142
+ else if ( t < 3600 ) { return m_str + 'm' + s_str + 's' ; }
143
+ else { return h_str + 'h' + m_str + 'm' + s_str + 's'; }
144
+
145
+ };
146
+
147
+ chilliController.formatBytes = function ( b , zeroReturn ) {
148
+
149
+ if ( typeof(b) == 'undefined' ) {
150
+ b = 0;
151
+ } else {
152
+ b = parseInt ( b , 10 ) ;
153
+ }
154
+
155
+ if ( (typeof (zeroReturn) !='undefined') && ( b === 0 ) ) {
156
+ return zeroReturn;
157
+ }
158
+
159
+ var kb = Math.round(b / 10) / 100;
160
+ if (kb < 1) return b + ' Bytes';
161
+
162
+ var mb = Math.round(kb / 10) / 100;
163
+ if (mb < 1) return kb + ' Kilobytes';
164
+
165
+ var gb = Math.round(mb / 10) / 100;
166
+ if (gb < 1) return mb + ' Megabytes';
167
+
168
+ return gb + ' Gigabytes';
169
+ };
170
+
171
+
172
+ /**
173
+ * Global chilliController object
174
+ *
175
+ * PUBLIC METHODS
176
+ * --------------
177
+ * logon ( username, password ) :
178
+ * Attempt a CHAP logon with username/password
179
+ * issues a /logon command to chilli daemon
180
+ *
181
+ * logon2 ( username, response ) :
182
+ * Attempt a CHAP logon with username/response
183
+ * issues a /logon command to chilli daemon
184
+ *
185
+ * logoff () :
186
+ * Disconnect the current user by issuing a
187
+ * /logoff command to the chilli daemon
188
+ *
189
+ * refresh () :
190
+ * Issues a /status command to chilli daemon to refresh
191
+ * the local chilliController object state/session data
192
+ *
193
+ */
194
+
195
+ chilliController.logon = function ( username , password ) {
196
+
197
+ if ( typeof(username) !== 'string') {
198
+ chilliController.onError( 1 , "username missing (or incorrect type)" ) ;
199
+ }
200
+
201
+ if ( typeof(password) !== 'string') {
202
+ chilliController.onError( 2 , "password missing (or incorrect type)" ) ;
203
+ }
204
+
205
+ log ( 'chilliController.logon( "' + username + '" , "' + password + ' " )' );
206
+
207
+ chilliController.temp = { 'username': username , 'password': password };
208
+ chilliController.command = 'logon';
209
+
210
+ log ('chilliController.logon: asking for a new challenge ' );
211
+ chilliJSON.onError = chilliController.onError ;
212
+ chilliJSON.onJSONReady = chilliController.logonStep2 ;
213
+ chilliController.clientState = chilliController.AUTH_PENDING ;
214
+ chilliJSON.get( chilliController.urlRoot() + 'status' ) ;
215
+ };
216
+
217
+ chilliController.logon2 = function ( username , response ) {
218
+
219
+ if ( typeof(username) !== 'string') {
220
+ chilliController.onError( 1 , "username missing (or incorrect type)" ) ;
221
+ }
222
+
223
+ if ( typeof(response) !== 'string') {
224
+ chilliController.onError( 2 , "response missing (or incorrect type)" ) ;
225
+ }
226
+
227
+ log ( 'chilliController.logon2( "' + username + '" , "' + response + ' " )' );
228
+
229
+ chilliController.temp = { 'username': username , 'response': response };
230
+ chilliController.command = 'logon2';
231
+
232
+ log ('chilliController.logon2: asking for a new challenge ' );
233
+ chilliJSON.onError = chilliController.onError ;
234
+ chilliJSON.onJSONReady = chilliController.logonStep2 ;
235
+ chilliController.clientState = chilliController.AUTH_PENDING ;
236
+ chilliJSON.get( chilliController.urlRoot() + 'status' ) ;
237
+ };
238
+
239
+
240
+ /**
241
+ * Second part of the logon process invoked after
242
+ * the just requested challenge has been received
243
+ */
244
+ chilliController.logonStep2 = function ( resp ) {
245
+
246
+ log('Entering logonStep 2');
247
+
248
+ if ( typeof (resp.challenge) != 'string' ) {
249
+ log('logonStep2: cannot find a challenge. Aborting.');
250
+ return chilliController.onError('Cannot get challenge');
251
+ }
252
+
253
+ if ( resp.clientSate === chilliController.stateCodes.AUTH ) {
254
+ log('logonStep2: Already connected. Aborting.');
255
+ return chilliController.onError('Already connected.');
256
+ }
257
+
258
+ var challenge = resp.challenge;
259
+
260
+ var username = chilliController.temp.username ;
261
+ var password = chilliController.temp.password ;
262
+ var response = chilliController.temp.response ;
263
+
264
+ log ('chilliController.logonStep2: Got challenge = ' + challenge );
265
+
266
+ if ( chilliController.uamService ) { /* MD5 CHAP will be calculated by uamService */
267
+
268
+ log ('chilliController.logonStep2: Logon using uamService (external MD5 CHAP)');
269
+
270
+ var c ;
271
+ if ( chilliController.uamService.indexOf('?') === -1 ) {
272
+ c = '?' ;
273
+ }
274
+ else {
275
+ c = '&' ;
276
+ }
277
+
278
+ // Build command URL
279
+ var url = chilliController.uamService + c + 'username=' + escape(username) +'&password=' + escape(password) +'&challenge=' + challenge ;
280
+
281
+ if (chilliController.queryObj && chilliController.queryObj['userurl'] ) {
282
+ url += '&userurl='+chilliController.queryObj['userurl'] ;
283
+ }
284
+
285
+ // Make uamService request
286
+ chilliJSON.onError = chilliController.onError ;
287
+ chilliJSON.onJSONReady = chilliController.logonStep3 ;
288
+
289
+ chilliController.clientState = chilliController.AUTH_PENDING ;
290
+ chilliJSON.get( url ) ;
291
+ }
292
+ else {
293
+ /* TODO: Should check if challenge has expired and possibly get a new one */
294
+ /* OR always call status first to get a fresh challenge */
295
+
296
+ if (!response || response == '') {
297
+ /* Calculate MD5 CHAP at the client side */
298
+ var myMD5 = new ChilliMD5();
299
+ response = myMD5.chap ( chilliController.ident , password , challenge );
300
+ log ( 'chilliController.logonStep2: Calculating CHAP-Password = ' + response );
301
+ }
302
+
303
+ /* Prepare chilliJSON for logon request */
304
+ chilliJSON.onError = chilliController.onError ;
305
+ chilliJSON.onJSONReady = chilliController.processReply ;
306
+ chilliController.clientState = chilliController.stateCodes.AUTH_PENDING ;
307
+
308
+ /* Build /logon command URL */
309
+ var logonUrl = chilliController.urlRoot() + 'logon?username=' + escape(username) + '&response=' + response;
310
+ if (chilliController.queryObj && chilliController.queryObj['userurl'] ) {
311
+ logonUrl += '&userurl='+chilliController.queryObj['userurl'] ;
312
+ }
313
+ chilliJSON.get ( logonUrl ) ;
314
+ }
315
+
316
+ };
317
+
318
+ /**
319
+ * Third part of the logon process invoked after
320
+ * getting a uamService response
321
+ */
322
+ chilliController.logonStep3 = function ( resp ) {
323
+ log('Entering logonStep 3');
324
+
325
+ var username = chilliController.temp.username ;
326
+
327
+ if ( typeof (resp.response) == 'string' ) {
328
+ chilliJSON.onError = chilliController.onError ;
329
+ chilliJSON.onJSONReady = chilliController.processReply ;
330
+ chilliController.clientState = chilliController.stateCodes.AUTH_PENDING ;
331
+
332
+ /* Build /logon command URL */
333
+ var logonUrl = chilliController.urlRoot() + 'logon?username=' + escape(username) + '&response=' + resp.response;
334
+ if (chilliController.queryObj && chilliController.queryObj['userurl'] ) {
335
+ logonUrl += '&userurl='+chilliController.queryObj['userurl'] ;
336
+ }
337
+ chilliJSON.get ( logonUrl ) ;
338
+ }
339
+ }
340
+
341
+ chilliController.refresh = function ( ) {
342
+
343
+ if ( chilliController.autorefreshTimer ) {
344
+ chilliController.command = 'autorefresh' ;
345
+ }
346
+ else {
347
+ chilliController.command = 'refresh' ;
348
+ }
349
+
350
+ chilliJSON.onError = chilliController.onError ;
351
+ chilliJSON.onJSONReady = chilliController.processReply ;
352
+ chilliJSON.get( chilliController.urlRoot() + 'status' ) ;
353
+ };
354
+
355
+ chilliController.logoff = function () {
356
+
357
+ chilliController.command = 'logoff' ;
358
+ chilliJSON.onError = chilliController.onError ;
359
+ chilliJSON.onJSONReady = chilliController.processReply ;
360
+ chilliJSON.get( chilliController.urlRoot() + 'logoff' );
361
+ };
362
+
363
+ /* *
364
+ *
365
+ * This functions does some check/type processing on the JSON resp
366
+ * and updates the corresponding chilliController members
367
+ *
368
+ */
369
+ chilliController.processReply = function ( resp ) {
370
+
371
+ if ( typeof (resp.message) == 'string' ) {
372
+
373
+ /* The following trick will replace HTML entities with the corresponding
374
+ * character. This will not work in Flash (no innerHTML)
375
+ */
376
+
377
+ var fakediv = document.createElement('div');
378
+ fakediv.innerHTML = resp.message ;
379
+ chilliController.message = fakediv.innerHTML ;
380
+ }
381
+
382
+ if ( typeof (resp.challenge) == 'string' ) {
383
+ chilliController.challenge = resp.challenge ;
384
+ }
385
+
386
+ if ( typeof ( resp.location ) == 'object' ) {
387
+ chilliController.location = resp.location ;
388
+ }
389
+
390
+ if ( typeof ( resp.accounting ) == 'object' ) {
391
+ chilliController.accounting = resp.accounting ;
392
+ }
393
+
394
+ if ( (typeof ( resp.redir ) == 'object') ) {
395
+ chilliController.redir = resp.redir ;
396
+ }
397
+
398
+ /* Update the session member only the first time after AUTH */
399
+ if ( (typeof ( resp.session ) == 'object') &&
400
+ ( chilliController.session==null || (
401
+ ( chilliController.clientState !== chilliController.stateCodes.AUTH ) &&
402
+ ( resp.clientState === chilliController.stateCodes.AUTH )))) {
403
+
404
+ chilliController.session = resp.session ;
405
+
406
+ if ( resp.session.startTime ) {
407
+ chilliController.session.startTime = new Date();
408
+ chilliController.session.startTime.setTime(resp.session.startTime);
409
+ }
410
+ }
411
+
412
+ /* Update clientState */
413
+ if ( ( resp.clientState === chilliController.stateCodes.NOT_AUTH ) ||
414
+ ( resp.clientState === chilliController.stateCodes.AUTH ) ||
415
+ ( resp.clientState === chilliController.stateCodes.AUTH_SPLASH ) ||
416
+ ( resp.clientState === chilliController.stateCodes.AUTH_PENDING ) ) {
417
+
418
+ chilliController.clientState = resp.clientState ;
419
+ }
420
+ else {
421
+ chilliController.onError("Unknown clientState found in JSON reply");
422
+ }
423
+
424
+
425
+ /* Launch or stop the autorefresh timer if required */
426
+ if ( chilliController.clientState === chilliController.stateCodes.AUTH ) {
427
+
428
+ if ( !chilliController.autorefreshTimer ) {
429
+ chilliController.autorefreshTimer = setInterval ('chilliController.refresh()' , 1000*chilliController.interval);
430
+ }
431
+ }
432
+ else if ( chilliController.clientState === chilliController.stateCodes.NOT_AUTH ) {
433
+ clearInterval ( chilliController.autorefreshTimer ) ;
434
+ chilliController.autorefreshTimer = 0 ;
435
+ }
436
+
437
+ /* Lastly... call the event handler */
438
+ log ('chilliController.processReply: Calling onUpdate. clienState = ' + chilliController.clientState);
439
+ chilliController.onUpdate( chilliController.command );
440
+ };
441
+
442
+
443
+
444
+ /**
445
+ * chilliJSON object
446
+ *
447
+ * This private objet implements the cross domain hack
448
+ * If no answer is received before timeout, then an error is raised.
449
+ *
450
+ */
451
+
452
+ var chilliJSON = { timeout:25000 , timer:0 , node:0 , timestamp:0 };
453
+
454
+ chilliJSON.expired = function () {
455
+
456
+ if ( chilliJSON.node.text ) {
457
+ log ('chilliJSON: reply content \n' + chilliJSON.node.text );
458
+ }
459
+ else {
460
+ log ('chilliJSON: request timed out (or reply is not valid JS)');
461
+ }
462
+
463
+ clearInterval ( chilliJSON.timer ) ;
464
+ chilliJSON.timer = 0 ;
465
+
466
+ /* remove the <SCRIPT> tag node that we have created */
467
+ if ( typeof (chilliJSON.node) !== 'number' ) {
468
+ document.getElementsByTagName('head')[0].removeChild ( chilliJSON.node );
469
+ }
470
+ chilliJSON.node = 0;
471
+
472
+ /* TODO: Implement some kind of retry mechanism here ... */
473
+
474
+ chilliJSON.onError('JSON request timed out (or reply is not valid)');
475
+ };
476
+
477
+ chilliJSON.reply = function ( raw ) {
478
+
479
+ clearInterval ( chilliJSON.timer ) ;
480
+ chilliJSON.timer = 0 ;
481
+
482
+ var now = new Date() ;
483
+ var end = now.getTime() ;
484
+
485
+ if ( chilliJSON.timestamp ) {
486
+ log ( 'chilliJSON: JSON reply received in ' + ( end - chilliJSON.timestamp ) + ' ms\n' + dumpObject(raw) );
487
+ }
488
+
489
+ if ( typeof (chilliJSON.node) !== 'number' ) {
490
+ document.getElementsByTagName('head')[0].removeChild ( chilliJSON.node );
491
+ }
492
+ chilliJSON.node = 0;
493
+
494
+ /* TODO: We should parse raw JSON as an extra security measure */
495
+
496
+ chilliJSON.onJSONReady( raw ) ;
497
+ } ;
498
+
499
+ chilliJSON.get = function ( gUrl ) {
500
+
501
+ if ( typeof(gUrl) == "string" ) {
502
+ chilliJSON.url = gUrl ;
503
+ }
504
+ else {
505
+ log ( "chilliJSON:error:Incorrect url passed to chilliJSON.get():" + gUrl );
506
+ chilliJSON.onError ( "Incorrect url passed to chilliJSON.get() " );
507
+ return ;
508
+ }
509
+
510
+ if ( chilliJSON.timer ) {
511
+ log('logon: There is already a request running. Return without launching a new request.');
512
+ return ;
513
+ }
514
+
515
+
516
+ var scriptElement = document.createElement('script');
517
+ scriptElement.type = 'text/javascript';
518
+
519
+ var c ;
520
+ if ( this.url.indexOf('?') === -1 ) {
521
+ c = '?' ;
522
+ }
523
+ else {
524
+ c = '&' ;
525
+ }
526
+
527
+ scriptElement.src = chilliJSON.url + c + 'callback=chilliJSON.reply' ;
528
+ scriptElement.src += '&'+Math.random(); // prevent caching in Safari
529
+
530
+ /* Adding the node that will trigger the HTTP request to the DOM tree */
531
+ chilliJSON.node = document.getElementsByTagName('head')[0].appendChild(scriptElement);
532
+
533
+ /* Using interval instead of timeout to support Flash 5,6,7 */
534
+ chilliJSON.timer = setInterval ( 'chilliJSON.expired()' , chilliJSON.timeout ) ;
535
+ var now = new Date();
536
+ chilliJSON.timestamp = now.getTime() ;
537
+
538
+ log ('chilliJSON: getting ' + chilliJSON.url + ' . Waiting for reply ...');
539
+
540
+ }; // end chilliJSON.get = function ( url )
541
+
542
+
543
+ /**
544
+ * chilliClock object
545
+ *
546
+ * Can be used by BBSMs to display a count down.
547
+ *
548
+ * Will sync with chilliController and modulate the delay to call onTick
549
+ * This will avoid ugly sequence of short updates in the IO
550
+ * (not yet implemented)
551
+ *
552
+ */
553
+
554
+ var chilliClock = { isStarted : 0 };
555
+
556
+ chilliClock.onTick = function () {
557
+ log ("You should define your own onTick() handler on this clock object. Clock value = " + this.value );
558
+ };
559
+
560
+ chilliClock.increment = function () {
561
+
562
+ chilliClock.value = chilliClock.value + 1 ;
563
+ chilliClock.onTick( chilliClock.value ) ;
564
+ };
565
+
566
+ chilliClock.resync = function ( newval ) {
567
+ clearInterval ( chilliClock.isStarted ) ;
568
+ chilliClock.value = parseInt( newval , 10 ) ;
569
+ chilliClock.isStarted = setInterval ( 'chilliClock.increment()' , 1000 );
570
+ };
571
+
572
+ chilliClock.start = function ( newval ) {
573
+
574
+ if ( typeof (newval) !== 'Number' ) {
575
+ chilliClock.resync ( 0 ) ;
576
+ }
577
+ else {
578
+ chilliClock.resync ( newval ) ;
579
+ }
580
+ };
581
+
582
+ chilliClock.stop = function () {
583
+ clearInterval ( chilliClock.isStarted ) ;
584
+ chilliClock.isStarted = 0 ;
585
+ };
586
+
587
+
588
+ function getel(e) {
589
+ if (document.getElementById) {
590
+ return document.getElementById(e);
591
+ } else if (document.all){
592
+ return document.all[e];
593
+ }
594
+ }
595
+
596
+ function log( msg , messageLevel ) {
597
+ if (!chilliController.debug) return;
598
+ if ( typeof(trace)=="function") {
599
+ // ActionScript trace
600
+ trace ( msg );
601
+ }
602
+ else if ( typeof(console)=="object") {
603
+ // FireBug console
604
+ console.debug ( msg );
605
+ }
606
+
607
+ if ( getel('debugarea') ) {
608
+ var e = getel('debugarea') ;
609
+ e.value = e.value + '\n' + msg;
610
+ e.scrollTop = e.scrollHeight - e.clientHeight;
611
+ }
612
+ }
613
+
614
+ /* Transform an object to a text representation */
615
+ function dumpObject ( obj ) {
616
+
617
+ var str = '' ;
618
+
619
+ for (var key in obj ) {
620
+ str = str + " " + key + " = " + obj[key] + "\n" ;
621
+ if ( typeof ( obj[key] ) == "object" ) {
622
+ for ( var key2 in obj[key] ) {
623
+ str = str + " " + key2 + " = " + obj[key][key2] + "\n" ;
624
+ }
625
+ }
626
+ }
627
+
628
+ return str;
629
+ }
630
+
631
+ /*
632
+ * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
633
+ * Digest Algorithm, as defined in RFC 1321.
634
+ * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
635
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
636
+ * Distributed under the BSD License
637
+ * See http://pajhome.org.uk/crypt/md5 for more info.
638
+ *
639
+ * added by Y.DELTROO
640
+ * - new functions: chap(), hex2binl() and str2hex()
641
+ * - modifications to comply with the jslint test, http://www.jslint.com/
642
+ *
643
+ * Copyright (c) 2007
644
+ * Distributed under the BSD License
645
+ *
646
+ */
647
+
648
+
649
+ function ChilliMD5() {
650
+
651
+ var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
652
+ var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
653
+ var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
654
+
655
+ this.hex_md5 = function (s){
656
+ return binl2hex(core_md5(str2binl(s), s.length * chrsz));
657
+ };
658
+
659
+ this.chap = function ( hex_ident , str_password , hex_chal ) {
660
+
661
+ // Convert everything to hex encoded strings
662
+ var hex_password = str2hex ( str_password );
663
+
664
+ // concatenate hex encoded strings
665
+ var hex = hex_ident + hex_password + hex_chal;
666
+
667
+ // Convert concatenated hex encoded string to its binary representation
668
+ var bin = hex2binl ( hex ) ;
669
+
670
+ // Calculate MD5 on binary representation
671
+ var md5 = core_md5( bin , hex.length * 4 ) ;
672
+
673
+ return binl2hex( md5 );
674
+ };
675
+
676
+ function core_md5(x, len) {
677
+ x[len >> 5] |= 0x80 << ((len) % 32);
678
+ x[(((len + 64) >>> 9) << 4) + 14] = len;
679
+
680
+ var a = 1732584193;
681
+ var b = -271733879;
682
+ var c = -1732584194;
683
+ var d = 271733878;
684
+
685
+ for(var i = 0; i < x.length; i += 16) {
686
+ var olda = a;
687
+ var oldb = b;
688
+ var oldc = c;
689
+ var oldd = d;
690
+
691
+ a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
692
+ d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
693
+ c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
694
+ b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
695
+ a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
696
+ d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
697
+ c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
698
+ b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
699
+ a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
700
+ d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
701
+ c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
702
+ b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
703
+ a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
704
+ d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
705
+ c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
706
+ b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);
707
+
708
+ a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
709
+ d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
710
+ c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
711
+ b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
712
+ a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
713
+ d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
714
+ c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
715
+ b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
716
+ a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
717
+ d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
718
+ c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
719
+ b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
720
+ a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
721
+ d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
722
+ c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
723
+ b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
724
+
725
+ a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
726
+ d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
727
+ c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
728
+ b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
729
+ a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
730
+ d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
731
+ c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
732
+ b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
733
+ a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
734
+ d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
735
+ c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
736
+ b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
737
+ a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
738
+ d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
739
+ c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
740
+ b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
741
+
742
+ a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
743
+ d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
744
+ c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
745
+ b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
746
+ a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
747
+ d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
748
+ c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
749
+ b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
750
+ a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
751
+ d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
752
+ c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
753
+ b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
754
+ a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
755
+ d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
756
+ c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
757
+ b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
758
+
759
+ a = safe_add(a, olda);
760
+ b = safe_add(b, oldb);
761
+ c = safe_add(c, oldc);
762
+ d = safe_add(d, oldd);
763
+ }
764
+ return [ a, b, c, d ];
765
+
766
+ }
767
+
768
+ function md5_cmn(q, a, b, x, s, t) {
769
+ return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
770
+ }
771
+
772
+ function md5_ff(a, b, c, d, x, s, t) {
773
+ return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
774
+ }
775
+
776
+ function md5_gg(a, b, c, d, x, s, t) {
777
+ return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
778
+ }
779
+
780
+ function md5_hh(a, b, c, d, x, s, t) {
781
+ return md5_cmn(b ^ c ^ d, a, b, x, s, t);
782
+ }
783
+
784
+ function md5_ii(a, b, c, d, x, s, t) {
785
+ return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
786
+ }
787
+
788
+ function safe_add(x, y) {
789
+ var lsw = (x & 0xFFFF) + (y & 0xFFFF);
790
+ var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
791
+ return (msw << 16) | (lsw & 0xFFFF);
792
+ }
793
+ function bit_rol(num, cnt) {
794
+ return (num << cnt) | (num >>> (32 - cnt));
795
+ }
796
+
797
+ function str2binl(str) {
798
+ var bin = [] ;
799
+ var mask = (1 << chrsz) - 1;
800
+ for (var i = 0; i < str.length * chrsz; i += chrsz) {
801
+ bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
802
+ }
803
+ return bin;
804
+ }
805
+
806
+ function binl2hex(binarray) {
807
+ var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
808
+ var str = "";
809
+ for (var i = 0; i < binarray.length * 4; i++) {
810
+ str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
811
+ hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF);
812
+ }
813
+ return str;
814
+ }
815
+
816
+ function str2hex ( str ) {
817
+ var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
818
+ var hex = '';
819
+ var val ;
820
+ for ( var i=0 ; i<str.length ; i++) {
821
+ /* TODO: adapt this if chrz=16 */
822
+ val = str.charCodeAt(i);
823
+ hex = hex + hex_tab.charAt( val/16 );
824
+ hex = hex + hex_tab.charAt( val%16 );
825
+ }
826
+ return hex;
827
+ }
828
+
829
+ function hex2binl ( hex ) {
830
+ /* Clean-up hex encoded input string */
831
+ hex = hex.toLowerCase() ;
832
+ hex = hex.replace( / /g , "");
833
+
834
+ var bin =[] ;
835
+
836
+ /* Transfrom to array of integers (binary representation) */
837
+ for ( i=0 ; i < hex.length*4 ; i=i+8 ) {
838
+ octet = parseInt( hex.substr( i/4 , 2) , 16) ;
839
+ bin[i>>5] |= ( octet & 255 ) << (i%32);
840
+ }
841
+ return bin;
842
+ }
843
+
844
+ } // end of ChilliMD5 constructor