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