Annotation of embedaddon/coova-chilli/www/ChilliLibrary.js, revision 1.1.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>