author | Dan |
Tue, 14 Apr 2009 21:02:13 -0400 | |
changeset 909 | 94c1ff984286 |
parent 907 | 44851d7e9bda |
child 1001 | 79770fb4132a |
permissions | -rw-r--r-- |
/* * AJAX-based intelligent login interface */ /* * FRONTEND */ /** * Performs a logon as a regular member. */ window.ajaxLogonToMember = function() { // IE <6 pseudo-compatibility if ( KILL_SWITCH ) return true; if ( auth_level >= USER_LEVEL_MEMBER ) return true; ajaxLoginInit(function(k) { if ( on_main_page ) { window.location = makeUrl(main_page_members); } else { window.location.reload(); } }, USER_LEVEL_MEMBER); } /** * Authenticates to the highest level the current user is allowed to go to. */ window.ajaxLogonToElev = function() { if ( auth_level == user_level ) return true; ajaxLoginInit(function(k) { ENANO_SID = k; var url = String(' ' + window.location).substr(1); url = append_sid(url); window.location = url; }, user_level); } /* * BACKEND */ /** * Holding object for various AJAX authentication information. * @var object */ var logindata = {}; /** * Path to the image used to indicate loading progress * @var string */ if ( !ajax_login_loadimg_path ) var ajax_login_loadimg_path = false; if ( !ajax_login_successimg_path ) var ajax_login_successimg_path = false; if ( !ajax_login_lockimg_path ) var ajax_login_lockimg_path = false; /** * Status variables * @var int */ var AJAX_STATUS_LOADING_KEY = 1; var AJAX_STATUS_GENERATING_KEY = 2; var AJAX_STATUS_LOGGING_IN = 3; var AJAX_STATUS_SUCCESS = 4; var AJAX_STATUS_ERROR = 5; var AJAX_STATUS_DESTROY = 65535; /** * State constants * @var int */ var AJAX_STATE_EARLY_INIT = 1; var AJAX_STATE_LOADING_KEY = 2; /** * Switch to decide if DiffieHellman shows a "browser incompatible" error * @var bool */ var ajax_login_prevent_dh = IE || is_iPhone; /** * Performs the AJAX request to get an encryption key and from there spawns the login form. * @param function The function that will be called once authentication completes successfully. * @param int The security level to authenticate at - see http://docs.enanocms.org/Help:Appendix_B */ window.ajaxLoginInit = function(call_on_finish, user_level) { load_component(['messagebox', 'flyin', 'fadefilter', 'jquery', 'jquery-ui', 'l10n', 'crypto']); logindata = {}; var title = ( user_level > USER_LEVEL_MEMBER ) ? $lang.get('user_login_ajax_prompt_title_elev') : $lang.get('user_login_ajax_prompt_title'); logindata.mb_object = new MessageBox(MB_OKCANCEL | MB_ICONLOCK, title, ''); logindata.mb_object.onclick['Cancel'] = function() { // Hide the error message and captcha if ( document.getElementById('ajax_login_error_box') ) { document.getElementById('ajax_login_error_box').parentNode.removeChild(document.getElementById('ajax_login_error_box')); } if ( document.getElementById('autoCaptcha') ) { var to = fly_out_top(document.getElementById('autoCaptcha'), false, true); setTimeout(function() { var d = document.getElementById('autoCaptcha'); d.parentNode.removeChild(d); }, to); } // Ask the server to clean our key ajaxLoginPerformRequest({ mode: 'clean_key', key_aes: logindata.key_aes, key_dh: logindata.key_dh }); }; logindata.mb_object.onbeforeclick['OK'] = function() { ajaxLoginSubmitForm(); return true; } // Fetch the inner content area logindata.mb_inner = document.getElementById('messageBox').getElementsByTagName('div')[0]; // Initialize state logindata.showing_status = false; logindata.user_level = user_level; logindata.successfunc = call_on_finish; // Build the "loading" window ajaxLoginSetStatus(AJAX_STATUS_LOADING_KEY); // Request the key ajaxLoginPerformRequest({ mode: 'getkey' }); } /** * For compatibility only. */ window.ajaxLogonInit = function(call_on_finish, user_level) { return ajaxLoginInit(call_on_finish, user_level); } /** * Sets the contents of the AJAX login window to the appropriate status message. * @param int One of AJAX_STATUS_* */ window.ajaxLoginSetStatus = function(status) { if ( !logindata.mb_inner ) return false; if ( logindata.showing_status ) { var div = document.getElementById('ajax_login_status'); if ( div ) logindata.mb_inner.removeChild(div); } switch(status) { case AJAX_STATUS_LOADING_KEY: // Create the status div var div = document.createElement('div'); div.id = 'ajax_login_status'; div.style.marginTop = '10px'; div.style.textAlign = 'center'; // The circly ball ajaxy image + status message var status_msg = $lang.get('user_login_ajax_fetching_key'); // Insert the status message div.appendChild(document.createTextNode(status_msg)); // Append a br or two to space things properly div.appendChild(document.createElement('br')); div.appendChild(document.createElement('br')); var img = document.createElement('img'); img.src = ( ajax_login_loadimg_path ) ? ajax_login_loadimg_path : scriptPath + '/images/loading-big.gif'; div.appendChild(img); // Another coupla brs div.appendChild(document.createElement('br')); div.appendChild(document.createElement('br')); // The link to the full login form var small = document.createElement('small'); small.innerHTML = $lang.get('user_login_ajax_link_fullform', { link_full_form: makeUrlNS('Special', 'Login/' + title) }); div.appendChild(small); // Insert the entire message into the login window logindata.mb_inner.innerHTML = ''; logindata.mb_inner.appendChild(div); break; case AJAX_STATUS_GENERATING_KEY: // Create the status div var div = document.createElement('div'); div.id = 'ajax_login_status'; div.style.marginTop = '10px'; div.style.textAlign = 'center'; // The circly ball ajaxy image + status message var status_msg = $lang.get('user_login_ajax_generating_key'); // Insert the status message div.appendChild(document.createTextNode(status_msg)); // Append a br or two to space things properly div.appendChild(document.createElement('br')); div.appendChild(document.createElement('br')); var img = document.createElement('img'); img.src = ( ajax_login_lockimg_path ) ? ajax_login_lockimg_path : scriptPath + '/images/lock48.png'; div.appendChild(img); // Another coupla brs div.appendChild(document.createElement('br')); div.appendChild(document.createElement('br')); // The link to the full login form var small = document.createElement('small'); small.innerHTML = $lang.get('user_login_ajax_link_fullform_dh', { link_full_form: makeUrlNS('Special', 'Login/' + title) }); div.appendChild(small); // Insert the entire message into the login window logindata.mb_inner.innerHTML = ''; logindata.mb_inner.appendChild(div); break; case AJAX_STATUS_LOGGING_IN: // Create the status div var div = document.createElement('div'); div.id = 'ajax_login_status'; div.style.marginTop = '10px'; div.style.textAlign = 'center'; // The circly ball ajaxy image + status message var status_msg = $lang.get('user_login_ajax_loggingin'); // Insert the status message div.appendChild(document.createTextNode(status_msg)); // Append a br or two to space things properly div.appendChild(document.createElement('br')); div.appendChild(document.createElement('br')); var img = document.createElement('img'); img.src = ( ajax_login_loadimg_path ) ? ajax_login_loadimg_path : scriptPath + '/images/loading-big.gif'; div.appendChild(img); // Insert the entire message into the login window logindata.mb_inner.innerHTML = ''; logindata.mb_inner.appendChild(div); break; case AJAX_STATUS_SUCCESS: // Create the status div var div = document.createElement('div'); div.id = 'ajax_login_status'; div.style.marginTop = '10px'; div.style.textAlign = 'center'; // The circly ball ajaxy image + status message var status_msg = $lang.get('user_login_success_short'); // Insert the status message div.appendChild(document.createTextNode(status_msg)); // Append a br or two to space things properly div.appendChild(document.createElement('br')); div.appendChild(document.createElement('br')); var img = document.createElement('img'); img.src = ( ajax_login_successimg_path ) ? ajax_login_successimg_path : scriptPath + '/images/check.png'; div.appendChild(img); // Insert the entire message into the login window logindata.mb_inner.innerHTML = ''; logindata.mb_inner.appendChild(div); break; case AJAX_STATUS_ERROR: // Create the status div var div = document.createElement('div'); div.id = 'ajax_login_status'; div.style.marginTop = '10px'; div.style.textAlign = 'center'; // The circly ball ajaxy image + status message var status_msg = $lang.get('user_login_ajax_err_crypto'); // Insert the status message div.appendChild(document.createTextNode(status_msg)); // Append a br or two to space things properly div.appendChild(document.createElement('br')); div.appendChild(document.createElement('br')); var img = document.createElement('img'); img.src = ( ajax_login_successimg_path ) ? ajax_login_successimg_path : scriptPath + '/images/checkbad.png'; div.appendChild(img); // Append a br or two to space things properly div.appendChild(document.createElement('br')); div.appendChild(document.createElement('br')); // The circly ball ajaxy image + status message var detail_msg = $lang.get('user_login_ajax_err_crypto_details'); var full_link = $lang.get('user_login_ajax_err_crypto_link'); var link = document.createElement('a'); link.href = makeUrlNS('Special', 'Login/' + title); link.appendChild(document.createTextNode(full_link)); var span = document.createElement('span'); span.style.fontSize = 'smaller'; // Insert the message span.appendChild(document.createTextNode(detail_msg + ' ')); span.appendChild(link); div.appendChild(span); // Insert the entire message into the login window logindata.mb_inner.innerHTML = ''; logindata.mb_inner.appendChild(div); break; default: eval(setHook('login_set_status')); break; case AJAX_STATUS_DESTROY: case null: case undefined: logindata.showing_status = false; return null; break; } logindata.showing_status = true; } /** * Performs an AJAX logon request to the server and calls ajaxLoginProcessResponse() on the result. * @param object JSON packet to send */ window.ajaxLoginPerformRequest = function(json) { json = toJSONString(json); json = ajaxEscape(json); ajaxPost(makeUrlNS('Special', 'Login/action.json'), 'r=' + json, function(ajax) { if ( ajax.readyState == 4 && ajax.status == 200 ) { // parse response var response = String(ajax.responseText + ''); if ( !check_json_response(response) ) { handle_invalid_json(response); return false; } response = parseJSON(response); ajaxLoginProcessResponse(response); } }, true); } /** * Processes a response from the login server * @param object JSON response */ window.ajaxLoginProcessResponse = function(response) { // Did the server send a plaintext error? if ( response.mode == 'error' ) { if ( logindata.mb_object ) { logindata.mb_object.destroy(); var error_msg = $lang.get('user_' + ( response.error.toLowerCase() )); new MessageBox(MB_ICONSTOP | MB_OK, $lang.get('user_err_login_generic_title'), error_msg); } else { alert(response.error); } return false; } // Main mode switch switch ( response.mode ) { case 'build_box': // Rid ourselves of any loading windows ajaxLoginSetStatus(AJAX_STATUS_DESTROY); // The server wants us to build the login form, all the information is there ajaxLoginBuildForm(response); break; case 'login_success': ajaxLoginSetStatus(AJAX_STATUS_SUCCESS); logindata.successfunc(response.key); break; case 'login_failure': // Rid ourselves of any loading windows ajaxLoginSetStatus(AJAX_STATUS_DESTROY); document.getElementById('messageBox').style.backgroundColor = '#C0C0C0'; var mb_parent = document.getElementById('messageBox').parentNode; $(mb_parent).effect("shake", {}, 200); setTimeout(function() { document.getElementById('messageBox').style.backgroundColor = '#FFF'; ajaxLoginBuildForm(response.respawn_info); ajaxLoginShowFriendlyError(response); }, 2500); break; case 'login_success_reset': var conf = confirm($lang.get('user_login_ajax_msg_used_temp_pass')); if ( conf ) { var url = makeUrlNS('Special', 'PasswordReset/stage2/' + response.user_id + '/' + response.temp_password); window.location = url; } else { // treat as a failure ajaxLoginSetStatus(AJAX_STATUS_DESTROY); document.getElementById('messageBox').style.backgroundColor = '#C0C0C0'; var mb_parent = document.getElementById('messageBox').parentNode; $(mb_parent).effect("shake", {}, 1500); setTimeout(function() { document.getElementById('messageBox').style.backgroundColor = '#FFF'; ajaxLoginBuildForm(response.respawn_info); // don't show an error here, just silently respawn }, 2500); } break; case 'logout_success': if ( ENANO_SID ) { ajaxLoginReplaceSIDInline(false, ENANO_SID, USER_LEVEL_MEMBER); } break; case 'noop': break; } } /* * RESPONSE HANDLERS */ /** * Builds the login form. * @param object Metadata to build off of */ window.ajaxLoginBuildForm = function(data) { // let's hope this effectively preloads the image... var _1 = document.createElement('img'); _1.src = ( ajax_login_successimg_path ) ? ajax_login_successimg_path : scriptPath + '/images/check.png'; var _2 = document.createElement('img'); _2.src = ( ajax_login_lockimg_path ) ? ajax_login_lockimg_path : scriptPath + '/images/lock48.png'; var div = document.createElement('div'); div.id = 'ajax_login_form'; var show_captcha = ( data.locked_out && data.lockout_info.lockout_policy == 'captcha' ) ? data.lockout_info.captcha : false; // text displayed on re-auth if ( logindata.user_level > USER_LEVEL_MEMBER ) { div.innerHTML += $lang.get('user_login_ajax_prompt_body_elev') + '<br /><br />'; } // Create the form var form = document.createElement('form'); form.action = 'javascript:void(ajaxLoginSubmitForm());'; form.onsubmit = function() { ajaxLoginSubmitForm(); return false; } if ( IE ) { form.style.marginTop = '-20px'; } // Using tables to wrap form elements because it results in a // more visually appealing form. Yes, tables suck. I don't really // care - they make forms look good. var table = document.createElement('table'); table.style.margin = '0 auto'; // Field - username var tr1 = document.createElement('tr'); var td1_1 = document.createElement('td'); td1_1.appendChild(document.createTextNode($lang.get('user_login_field_username') + ':')); tr1.appendChild(td1_1); var td1_2 = document.createElement('td'); var f_username = document.createElement('input'); f_username.id = 'ajax_login_field_username'; f_username.name = 'ajax_login_field_username'; f_username.type = 'text'; f_username.size = '25'; if ( data.username ) f_username.value = data.username; td1_2.appendChild(f_username); tr1.appendChild(td1_2); table.appendChild(tr1); // Field - password var tr2 = document.createElement('tr'); var td2_1 = document.createElement('td'); td2_1.appendChild(document.createTextNode($lang.get('user_login_field_password') + ':')); tr2.appendChild(td2_1); var td2_2 = document.createElement('td'); var f_password = document.createElement('input'); f_password.id = 'ajax_login_field_password'; f_password.name = 'ajax_login_field_username'; f_password.type = 'password'; f_password.size = '25'; if ( !show_captcha ) { f_password.onkeyup = function(e) { if ( !e ) e = window.event; if ( !e && IE ) return true; if ( e.keyCode == 13 ) { ajaxLoginSubmitForm(); } } } td2_2.appendChild(f_password); tr2.appendChild(td2_2); table.appendChild(tr2); eval(setHook('login_build_form')); // Field - captcha if ( show_captcha ) { var tr3 = document.createElement('tr'); var td3_1 = document.createElement('td'); td3_1.appendChild(document.createTextNode($lang.get('user_login_field_captcha') + ':')); tr3.appendChild(td3_1); var td3_2 = document.createElement('td'); var f_captcha = document.createElement('input'); f_captcha.id = 'ajax_login_field_captcha'; f_captcha.name = 'ajax_login_field_username'; f_captcha.type = 'text'; f_captcha.size = '25'; f_captcha.onkeyup = function(e) { if ( !e ) e = window.event; if ( !e.keyCode ) return true; if ( e.keyCode == 13 ) { ajaxLoginSubmitForm(); } } td3_2.appendChild(f_captcha); tr3.appendChild(td3_2); table.appendChild(tr3); } // Done building the main part of the form form.appendChild(table); // Field: remember login if ( logindata.user_level <= USER_LEVEL_MEMBER ) { var lbl_remember = document.createElement('label'); lbl_remember.style.fontSize = 'smaller'; lbl_remember.style.display = 'block'; lbl_remember.style.textAlign = 'center'; // figure out what text to put in the "remember me" checkbox // infinite session length? if ( data.extended_time == 0 ) { // yes, infinite var txt_remember = $lang.get('user_login_ajax_check_remember_infinite'); } else { if ( data.extended_time % 7 == 0 ) { // number of days is a multiple of 7 // use weeks as our unit var sess_time = data.extended_time / 7; var unit = 'week'; } else { // use days as our unit var sess_time = data.extended_time; var unit = 'day'; } // more than one week or day? if ( sess_time != 1 ) unit += 's'; // assemble the string var txt_remember = $lang.get('user_login_ajax_check_remember', { session_length: sess_time, length_units: $lang.get('etc_unit_' + unit) }); } var check_remember = document.createElement('input'); check_remember.type = 'checkbox'; // this onclick attribute changes the cookie whenever the checkbox or label is clicked check_remember.setAttribute('onclick', 'var ck = ( this.checked ) ? "enable" : "disable"; createCookie("login_remember", ck, 3650);'); if ( readCookie('login_remember') != 'disable' ) check_remember.setAttribute('checked', 'checked'); check_remember.id = 'ajax_login_field_remember'; lbl_remember.appendChild(check_remember); lbl_remember.innerHTML += ' ' + txt_remember; form.appendChild(lbl_remember); } // Field: enable Diffie Hellman if ( ajax_login_prevent_dh ) { var lbl_dh = document.createElement('span'); lbl_dh.style.fontSize = 'smaller'; lbl_dh.style.display = 'block'; lbl_dh.style.textAlign = 'center'; lbl_dh.innerHTML = $lang.get('user_login_ajax_check_dh_ie'); form.appendChild(lbl_dh); } else if ( !data.allow_diffiehellman ) { // create hidden control - server requested that DiffieHellman be disabled (usually means not supported) var check_dh = document.createElement('input'); check_dh.type = 'hidden'; check_dh.id = 'ajax_login_field_dh'; form.appendChild(check_dh); } else { var lbl_dh = document.createElement('label'); lbl_dh.style.fontSize = 'smaller'; lbl_dh.style.display = 'block'; lbl_dh.style.textAlign = 'center'; var check_dh = document.createElement('input'); check_dh.type = 'checkbox'; // this onclick attribute changes the cookie whenever the checkbox or label is clicked check_dh.setAttribute('onclick', 'var ck = ( this.checked ) ? "enable" : "disable"; createCookie("diffiehellman_login", ck, 3650);'); if ( readCookie('diffiehellman_login') != 'disable' ) check_dh.setAttribute('checked', 'checked'); check_dh.id = 'ajax_login_field_dh'; lbl_dh.appendChild(check_dh); lbl_dh.innerHTML += ' ' + $lang.get('user_login_ajax_check_dh'); form.appendChild(lbl_dh); } if ( IE ) { div.innerHTML += form.outerHTML; } else { div.appendChild(form); } // Diagnostic / help links // (only displayed in login, not in re-auth) if ( logindata.user_level == USER_LEVEL_MEMBER ) { form.style.marginBottom = '10px'; var links = document.createElement('small'); links.style.display = 'block'; links.style.textAlign = 'center'; links.innerHTML = ''; if ( !show_captcha ) links.innerHTML += $lang.get('user_login_ajax_link_fullform', { link_full_form: makeUrlNS('Special', 'Login/' + title) }) + '<br />'; // Always shown links.innerHTML += $lang.get('user_login_ajax_link_forgotpass', { forgotpass_link: makeUrlNS('Special', 'PasswordReset') }) + '<br />'; if ( !show_captcha ) links.innerHTML += $lang.get('user_login_createaccount_blurb', { reg_link: makeUrlNS('Special', 'Register') }); div.appendChild(links); } // Insert the entire form into the login window logindata.mb_inner.innerHTML = ''; logindata.mb_inner.appendChild(div); // Post operations: field focus setTimeout( function() { if ( logindata.loggedin_username ) document.getElementById('ajax_login_field_password').focus(); else document.getElementById('ajax_login_field_username').focus(); }, 750); // Post operations: show captcha window if ( show_captcha ) ajaxShowCaptcha(show_captcha); // Post operations: stash encryption keys and All That Jazz(TM) logindata.key_aes = data.aes_key; logindata.key_dh = data.dh_public_key; logindata.captcha_hash = show_captcha; logindata.loggedin_username = data.username // Are we locked out? If so simulate an error and disable the controls if ( data.lockout_info.lockout_policy == 'lockout' && data.locked_out ) { f_username.setAttribute('disabled', 'disabled'); f_password.setAttribute('disabled', 'disabled'); var fake_packet = { error_code: 'locked_out', respawn_info: data }; ajaxLoginShowFriendlyError(fake_packet); } } window.ajaxLoginSubmitForm = function(real, username, password, captcha, remember) { // Perform AES test to make sure it's all working if ( !aes_self_test() ) { alert('BUG: AES self-test failed'); login_cache.mb_object.destroy(); return false; } // Early submit hook eval(setHook('login_submit_early')); // Hide the error message and captcha if ( document.getElementById('ajax_login_error_box') ) { document.getElementById('ajax_login_error_box').parentNode.removeChild(document.getElementById('ajax_login_error_box')); } if ( document.getElementById('autoCaptcha') ) { var to = fly_out_top(document.getElementById('autoCaptcha'), false, true); setTimeout(function() { var d = document.getElementById('autoCaptcha'); d.parentNode.removeChild(d); }, to); } // "Remember session" switch if ( typeof(remember) == 'boolean' ) { var remember_session = remember; } else { if ( document.getElementById('ajax_login_field_remember') ) { var remember_session = ( document.getElementById('ajax_login_field_remember').checked ) ? true : false; } else { var remember_session = false; } } // Encryption: preprocessor if ( real ) { var do_dh = true; } else if ( document.getElementById('ajax_login_field_dh') ) { var do_dh = document.getElementById('ajax_login_field_dh').checked; } else { if ( ajax_login_prevent_dh ) { // IE/MobileSafari doesn't have this control, continue silently IF the rest // of the login form is there if ( !document.getElementById('ajax_login_field_username') ) { return false; } } else { // The user probably clicked ok when the form wasn't in there. return false; } } if ( typeof(username) != 'string' ) { var username = document.getElementById('ajax_login_field_username').value; } if ( typeof(password) != 'string' ) { var password = document.getElementById('ajax_login_field_password').value; } if ( !captcha && document.getElementById('ajax_login_field_captcha') ) { var captcha = document.getElementById('ajax_login_field_captcha').value; } try { if ( do_dh ) { ajaxLoginSetStatus(AJAX_STATUS_GENERATING_KEY); if ( !real ) { // Wait while the browser updates the login window setTimeout(function() { ajaxLoginSubmitForm(true, username, password, captcha, remember_session); }, 20); return true; } var dh_start = (new Date()).getTime(); // Perform Diffie Hellman stuff var dh_priv = dh_gen_private(); var dh_pub = dh_gen_public(dh_priv); var secret = dh_gen_shared_secret(dh_priv, logindata.key_dh); // secret_hash is used to verify that the server guesses the correct secret var secret_hash = hex_sha1(secret); // crypt_key is the actual AES key var crypt_key = (hex_sha256(secret)).substr(0, (keySizeInBits / 4)); var dh_time = (new Date()).getTime() - dh_start; console.debug("DH: complete, time = %dms", dh_time); } else { var crypt_key = logindata.key_aes; } ajaxLoginSetStatus(AJAX_STATUS_LOGGING_IN); // Encrypt the password and username var userinfo = { username: username, password: password }; eval(setHook('login_build_userinfo')); userinfo = toJSONString(userinfo); var crypt_key_ba = hexToByteArray(crypt_key); userinfo = stringToByteArray(userinfo); userinfo = rijndaelEncrypt(userinfo, crypt_key_ba, 'ECB'); userinfo = byteArrayToHex(userinfo); // Encrypted username and password (serialized with JSON) are now in the userinfo string // Collect other needed information if ( logindata.captcha_hash ) { var captcha_hash = logindata.captcha_hash; var captcha_code = captcha; } else { var captcha_hash = false; var captcha_code = false; } // Ship it across the 'net if ( do_dh ) { var json_packet = { mode: 'login_dh', userinfo: userinfo, captcha_code: captcha_code, captcha_hash: captcha_hash, dh_public_key: logindata.key_dh, dh_client_key: dh_pub, dh_secret_hash: secret_hash, level: logindata.user_level, remember: remember_session } } else { var json_packet = { mode: 'login_aes', userinfo: userinfo, captcha_code: captcha_code, captcha_hash: captcha_hash, key_aes: hex_md5(crypt_key), level: logindata.user_level, remember: remember_session } } } catch(e) { ajaxLoginSetStatus(AJAX_STATUS_ERROR); console.error('Exception caught in login process; backtrace follows'); console.debug(e); return false; } ajaxLoginPerformRequest(json_packet); } window.ajaxLoginShowFriendlyError = function(response) { if ( !response.respawn_info ) return false; if ( !response.error_code ) return false; var text = ajaxLoginGetErrorText(response); if ( document.getElementById('ajax_login_error_box') ) { // console.info('Reusing existing error-box'); document.getElementById('ajax_login_error_box').innerHTML = text; return true; } // console.info('Drawing new error-box'); // calculate position for the top of the box var mb_bottom = $dynano('messageBoxButtons').Top() + $dynano('messageBoxButtons').Height(); // if the box isn't done flying in yet, just estimate if ( mb_bottom < ( getHeight() / 2 ) ) { mb_bottom = ( getHeight() / 2 ) + 120; } var win_bottom = getHeight() + getScrollOffset(); var top = mb_bottom + ( ( win_bottom - mb_bottom ) / 2 ) - 32; // left position = 0.2 * window_width, seeing as the box is 60% width this works hackishly but nice and quick var left = getWidth() * 0.2; // create the div var errbox = document.createElement('div'); errbox.className = 'error-box-mini'; errbox.style.position = 'absolute'; errbox.style.width = '60%'; errbox.style.top = top + 'px'; errbox.style.left = left + 'px'; errbox.style.zIndex = getHighestZ(); errbox.innerHTML = text; errbox.id = 'ajax_login_error_box'; var body = document.getElementsByTagName('body')[0]; body.appendChild(errbox); } window.ajaxLoginGetErrorText = function(response) { if ( !response.error_code.match(/^[a-z0-9]+_[a-z0-9_]+$/) ) { return response.error_code; } switch ( response.error_code ) { default: eval(setHook('ajax_login_process_error')); if ( !ls ) { var ls = $lang.get('user_err_' + response.error_code); if ( ls == 'user_err_' + response.error_code ) // Adding response here allows language strings to utilize additional information passed from the error packet ls = $lang.get(response.error_code, response); } return ls; break; case 'locked_out': if ( response.respawn_info.lockout_info.lockout_policy == 'lockout' ) { return $lang.get('user_err_locked_out', { lockout_threshold: response.respawn_info.lockout_info.lockout_threshold, lockout_duration: response.respawn_info.lockout_info.lockout_duration, time_rem: response.respawn_info.lockout_info.time_rem, plural: ( response.respawn_info.lockout_info.time_rem == 1 ) ? '' : $lang.get('meta_plural'), captcha_blurb: '' }); break; } case 'invalid_credentials': var base = $lang.get('user_err_invalid_credentials'); if ( response.respawn_info.locked_out ) { base += ' '; var captcha_blurb = ''; switch(response.respawn_info.lockout_info.lockout_policy) { case 'captcha': captcha_blurb = $lang.get('user_err_locked_out_captcha_blurb'); break; case 'lockout': break; default: base += 'WTF? Shouldn\'t be locked out with lockout policy set to disable.'; break; } base += $lang.get('user_err_locked_out', { captcha_blurb: captcha_blurb, lockout_threshold: response.respawn_info.lockout_info.lockout_threshold, lockout_duration: response.respawn_info.lockout_info.lockout_duration, time_rem: response.respawn_info.lockout_info.time_rem, plural: ( response.respawn_info.lockout_info.time_rem == 1 ) ? '' : $lang.get('meta_plural') }); } else if ( response.respawn_info.lockout_info.lockout_policy == 'lockout' || response.respawn_info.lockout_info.lockout_policy == 'captcha' ) { // if we have a lockout policy of captcha or lockout, then warn the user switch ( response.respawn_info.lockout_info.lockout_policy ) { case 'captcha': base += $lang.get('user_err_invalid_credentials_lockout_captcha', { fails: response.respawn_info.lockout_info.lockout_fails, lockout_threshold: response.respawn_info.lockout_info.lockout_threshold, lockout_duration: response.respawn_info.lockout_info.lockout_duration }); break; case 'lockout': base += $lang.get('user_err_invalid_credentials_lockout', { fails: response.respawn_info.lockout_info.lockout_fails, lockout_threshold: response.respawn_info.lockout_info.lockout_threshold, lockout_duration: response.respawn_info.lockout_info.lockout_duration }); break; } } return base; break; } } window.ajaxShowCaptcha = function(code) { var mydiv = document.createElement('div'); mydiv.style.backgroundColor = '#FFFFFF'; mydiv.style.padding = '10px'; mydiv.style.position = 'absolute'; mydiv.style.top = '0px'; mydiv.id = 'autoCaptcha'; mydiv.style.zIndex = String( getHighestZ() + 1 ); var img = document.createElement('img'); img.onload = function() { if ( this.loaded ) return true; var mydiv = document.getElementById('autoCaptcha'); var width = getWidth(); var divw = $dynano(mydiv).Width(); var left = ( width / 2 ) - ( divw / 2 ); mydiv.style.left = left + 'px'; fly_in_top(mydiv, false, true); this.loaded = true; }; img.src = makeUrlNS('Special', 'Captcha/' + code); img.onclick = function() { this.src = this.src + '/a'; }; img.style.cursor = 'pointer'; mydiv.appendChild(img); domObjChangeOpac(0, mydiv); var body = document.getElementsByTagName('body')[0]; body.appendChild(mydiv); } window.ajaxInitLogout = function() { load_component(['messagebox', 'l10n', 'flyin', 'fadefilter', 'jquery', 'jquery-ui']); var title = $lang.get('user_logout_confirm_title'); var message = ( auth_level > USER_LEVEL_MEMBER ) ? $lang.get('user_logout_confirm_body_nelev') : $lang.get('user_logout_confirm_body_normal'); var buttons = []; buttons.push({ text: $lang.get('user_logout_confirm_btn_logout'), color: 'red', style: { fontWeight: 'bold' }, onclick: function() { miniPromptDestroy(this); window.location = makeUrlNS('Special', 'Logout/' + csrf_token + '/' + window.title); return false; } }); if ( auth_level > USER_LEVEL_MEMBER ) { buttons.push({ text: $lang.get('user_logout_confirm_btn_deauth'), color: 'blue', onclick: function() { var mp = miniPromptGetParent(this); var whitey = whiteOutMiniPrompt(mp); setTimeout(function() { whiteOutReportSuccess(whitey); setTimeout(function() { miniPromptDestroy(mp); }, 1250); }, 1000); ajaxLoginPerformRequest({ mode: 'logout', level: auth_level, csrf_token: csrf_token }); return false; } }); } buttons.push({ text: $lang.get('etc_cancel'), onclick: function() { miniPromptDestroy(this); return false; } }); miniPromptMessage({ title: title, message: message, buttons: buttons }); } window.mb_logout = function() { ajaxInitLogout(); } window.ajaxStartLogin = function() { ajaxLogonToMember(); } window.ajaxStartAdminLogin = function() { // IE <6 pseudo-compatibility if ( KILL_SWITCH ) return true; if ( auth_level < USER_LEVEL_ADMIN ) { ajaxLoginInit(function(k) { ENANO_SID = k; auth_level = USER_LEVEL_ADMIN; var loc = makeUrlNS('Special', 'Administration'); if ( (ENANO_SID + ' ').length > 1 ) window.location = loc; }, USER_LEVEL_ADMIN); return false; } var loc = makeUrlNS('Special', 'Administration'); window.location = loc; } window.ajaxAdminPage = function() { // IE <6 pseudo-compatibility if ( KILL_SWITCH ) return true; if ( auth_level < USER_LEVEL_ADMIN ) { ajaxPromptAdminAuth(function(k) { ENANO_SID = k; auth_level = USER_LEVEL_ADMIN; var loc = String(window.location + ''); window.location = append_sid(loc); var loc = makeUrlNS('Special', 'Administration', 'module=' + namespace_list['Admin'] + 'PageManager&source=ajax&page_id=' + ajaxEscape(title)); if ( (ENANO_SID + ' ').length > 1 ) window.location = loc; }, 9); return false; } var loc = makeUrlNS('Special', 'Administration', 'module=' + namespace_list['Admin'] + 'PageManager&source=ajax&page_id=' + ajaxEscape(title)); window.location = loc; } var navto_ns; var navto_pg; var navto_ul; window.ajaxLoginNavTo = function(namespace, page_id, min_level) { // IE <6 pseudo-compatibility if ( KILL_SWITCH ) return true; navto_pg = page_id; navto_ns = namespace; navto_ul = min_level; if ( auth_level < min_level ) { ajaxPromptAdminAuth(function(k) { ENANO_SID = k; auth_level = navto_ul; var loc = makeUrlNS(navto_ns, navto_pg); if ( (ENANO_SID + ' ').length > 1 ) window.location = loc; }, min_level); return false; } var loc = makeUrlNS(navto_ns, navto_pg); window.location = loc; } window.ajaxAdminUser = function(username) { // IE <6 pseudo-compatibility if ( KILL_SWITCH ) return true; if ( auth_level < USER_LEVEL_ADMIN ) { ajaxPromptAdminAuth(function(k) { ENANO_SID = k; auth_level = USER_LEVEL_ADMIN; var loc = String(window.location + ''); window.location = append_sid(loc); var loc = makeUrlNS('Special', 'Administration', 'module=' + namespace_list['Admin'] + 'UserManager&src=get&user=' + ajaxEscape(username)); if ( (ENANO_SID + ' ').length > 1 ) window.location = loc; }, 9); return false; } var loc = makeUrlNS('Special', 'Administration', 'module=' + namespace_list['Admin'] + 'UserManager&src=get&user=' + ajaxEscape(username)); window.location = loc; } window.ajaxDynamicReauth = function(adminpage, level) { var old_sid = ENANO_SID; var targetpage = adminpage; if ( !level ) { level = USER_LEVEL_ADMIN; } ajaxLogonInit(function(k) { ajaxLoginReplaceSIDInline(k, old_sid, level); mb_current_obj.destroy(); console.debug(targetpage); if ( typeof(targetpage) == 'string' ) { ajaxPage(targetpage); } else if ( typeof(targetpage) == 'function' ) { targetpage(k); } }, level); if ( typeof(adminpage) == 'string' ) { ajaxLoginShowFriendlyError({ error_code: 'admin_session_timed_out', respawn_info: {} }); } } window.ajaxRenewSession = function() { ajaxDynamicReauth(false); } window.ajaxTrashElevSession = function() { load_component(['messagebox', 'fadefilter', 'l10n', 'flyin', 'jquery', 'jquery-ui']); miniPromptMessage({ title: $lang.get('user_logout_confirm_title_elev'), message: $lang.get('user_logout_confirm_body_elev'), buttons: [ { text: $lang.get('user_logout_confirm_btn_logout'), color: 'red', style: { fontWeight: 'bold' }, onclick: function() { ajaxLoginPerformRequest({ mode: 'logout', level: auth_level, csrf_token: csrf_token }); miniPromptDestroy(this); } }, { text: $lang.get('etc_cancel'), onclick: function() { miniPromptDestroy(this); } } ] }); } /** * Take an SID and patch all internal links on the page. * @param string New key. If false, removes keys from the page. * @param string Old key. If false, only appends the new SID (more work as it uses DOM, use when dynamically going up to elevated) * @param int New level, not a huge deal but sets auth_level. Try to specify it as some functions depend on it. */ window.ajaxLoginReplaceSIDInline = function(key, oldkey, level) { var host = String(window.location.hostname); var exp = new RegExp('^https?://' + host.replace('.', '\.') + contentPath.replace('.', '\.'), 'g'); var rexp = new RegExp('^https?://' + host.replace('.', '\.'), 'g'); if ( key ) { if ( oldkey ) { var body = document.getElementsByTagName('body')[0]; var replace = new RegExp(oldkey, 'g'); body.innerHTML = body.innerHTML.replace(replace, key); ENANO_SID = key; } else { // append SID to all internal links ENANO_SID = key; var links = document.getElementsByTagName('a'); for ( var i = 0; i < links.length; i++ ) { if ( links[i].href.match(exp, links[i]) && links[i].href.indexOf('#') == -1 ) { var newurl = (String(append_sid(links[i].href))).replace(rexp, ''); links[i].href = newurl; } } var forms = document.getElementsByTagName('form'); for ( var i = 0; i < forms.length; i++ ) { if ( forms[i].method.toLowerCase() == 'post' ) { if ( forms[i].action.match(exp, links[i]) ) { var newurl = (String(append_sid(forms[i].action))).replace(rexp, ''); forms[i].action = newurl; } } else { if ( !forms[i].auth ) { var auth = document.createElement('input'); auth.type = 'hidden'; auth.name = 'auth'; auth.value = key; forms[i].appendChild(auth); } else { forms[i].auth.value = key; } } } } if ( level ) { auth_level = level; } window.location.hash = '#auth:' + key; } else { auth_level = USER_LEVEL_MEMBER; ENANO_SID = false; if ( oldkey ) { var links = document.getElementsByTagName('a'); for ( var i = 0; i < links.length; i++ ) { if ( links[i].href.match(exp, links[i]) && links[i].href.indexOf('#') == -1 ) { links[i].href = links[i].href.replace(/\?auth=([a-f0-9]+)(&|#|$)/, '$2').replace(/&auth=([a-f0-9]+)/, '').replace(rexp, ''); } } } window.location.hash = '#auth:false'; } window.stdAjaxPrefix = append_sid(scriptPath + '/ajax.php?title=' + title); }