Fully implemented password complexity enforcement; added encryption for passwords on registration form; some baby steps taken towards supporting international usernames - this is not working very well, we might need a hackish fix; TODO: implement password strength meter into installer UI and get international usernames 100% working
authorDan
Tue, 18 Sep 2007 00:30:43 -0400
changeset 133 af0f6ec48de3
parent 132 0ae1b281a884
child 134 175776498ef1
Fully implemented password complexity enforcement; added encryption for passwords on registration form; some baby steps taken towards supporting international usernames - this is not working very well, we might need a hackish fix; TODO: implement password strength meter into installer UI and get international usernames 100% working
includes/clientside/static/ajax.js
includes/clientside/static/enano-lib-basic.js
includes/clientside/static/misc.js
includes/render.php
includes/sessions.php
includes/template.php
plugins/SpecialAdmin.php
plugins/SpecialUserFuncs.php
plugins/SpecialUserPrefs.php
--- a/includes/clientside/static/ajax.js	Mon Sep 17 11:52:58 2007 -0400
+++ b/includes/clientside/static/ajax.js	Tue Sep 18 00:30:43 2007 -0400
@@ -44,6 +44,16 @@
 
 function ajaxEscape(text)
 {
+  /*
+  text = escape(text);
+  text = text.replace(/\+/g, '%2B', text);
+  */
+  text = window.encodeURIComponent(text);
+  return text;
+}
+
+function ajaxAltEscape(text)
+{
   text = escape(text);
   text = text.replace(/\+/g, '%2B', text);
   return text;
--- a/includes/clientside/static/enano-lib-basic.js	Mon Sep 17 11:52:58 2007 -0400
+++ b/includes/clientside/static/enano-lib-basic.js	Tue Sep 18 00:30:43 2007 -0400
@@ -282,6 +282,7 @@
   'dynano.js',
   'flyin.js',
   'paginate.js',
+  'pwstrength.js',
   'loader.js'
 ];
 
--- a/includes/clientside/static/misc.js	Mon Sep 17 11:52:58 2007 -0400
+++ b/includes/clientside/static/misc.js	Tue Sep 18 00:30:43 2007 -0400
@@ -446,8 +446,11 @@
     'level' : ajax_auth_level_cache
   };
   
+  window.console.debug(json_data);
   json_data = toJSONString(json_data);
-  json_data = ajaxEscape(json_data);
+  window.console.debug(json_data);
+  json_data = ajaxAltEscape(json_data);
+  window.console.debug(json_data);
   
   var loading_win = '<div align="center" style="text-align: center;"> \
       <p>Logging in...</p> \
--- a/includes/render.php	Mon Sep 17 11:52:58 2007 -0400
+++ b/includes/render.php	Tue Sep 18 00:30:43 2007 -0400
@@ -72,7 +72,7 @@
     $chartag = $row['char_tag'];
     unset($row); // Free some memory
     
-    if ( preg_match('#^\#redirect \[\[(.+?)\]\]#', $message, $m) && $redir && !isset($_GET['redirect']) || ( isset($_GET['redirect']) && $_GET['redirect'] != 'no' ) )
+    if ( preg_match("#^\#redirect \[\[([^\]\r\n\a\t]+?)\]\]#", $message, $m) && $redir && ( !isset($_GET['redirect']) || ( isset($_GET['redirect']) && $_GET['redirect'] != 'no' ) ) )
     {
       dc_here('render: looks like a redirect page to me...');
       $old = $paths->cpage;
--- a/includes/sessions.php	Mon Sep 17 11:52:58 2007 -0400
+++ b/includes/sessions.php	Tue Sep 18 00:30:43 2007 -0400
@@ -150,7 +150,8 @@
    * @var string
    */
    
-   var $valid_username = '([A-Za-z0-9 \!\@\(\)-]+)';
+  //var $valid_username = '([A-Za-z0-9 \!\@\(\)-]+)';
+  var $valid_username = '([^<>_&\?\'"%\n\r\t\a]+)';
    
   /**
    * What we're allowed to do as far as permissions go. This changes based on the value of the "auth" URI param.
@@ -576,10 +577,21 @@
     // Initialize our success switch
     $success = false;
     
+    // Escaped username
+    $db_username = $this->prepare_text(strtolower($username));
+    
     // Select the user data from the table, and decrypt that so we can verify the password
-    $this->sql('SELECT password,old_encryption,user_id,user_level,theme,style,temp_password,temp_password_time FROM '.table_prefix.'users WHERE lcase(username)=\''.$this->prepare_text(strtolower($username)).'\';');
+    $this->sql('SELECT password,old_encryption,user_id,user_level,theme,style,temp_password,temp_password_time FROM '.table_prefix.'users WHERE lcase(username)=\''.$db_username.'\' OR username=\'' . $db_username . '\';');
     if($db->numrows() < 1)
-      return 'The username and/or password is incorrect.';
+    {
+      // This wasn't logged in <1.0.2, dunno how it slipped through
+      if($level > USER_LEVEL_MEMBER)
+        $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
+      else
+        $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
+        
+      return "The username and/or password is incorrect.";
+    }
     $row = $db->fetchrow();
     
     // Check to see if we're logging in using a temporary password
@@ -1392,24 +1404,51 @@
     $username = $this->prepare_text($username);
     $email = $this->prepare_text($email);
     $real_name = $this->prepare_text($real_name);
-    $password = $aes->encrypt($password, $this->private_key, ENC_HEX);
     
     $nameclause = ( $real_name != '' ) ? ' OR real_name=\''.$real_name.'\'' : '';
     $q = $this->sql('SELECT * FROM '.table_prefix.'users WHERE lcase(username)=\''.strtolower($username).'\' OR email=\''.$email.'\''.$nameclause.';');
-    if($db->numrows() > 0) {
+    if($db->numrows() > 0)
+    {
       $r = 'The ';
       $i=0;
       $row = $db->fetchrow();
       // Wow! An error checker that actually speaks English with the properest grammar! :-P
-      if($row['username'] == $username) { $r .= 'username'; $i++; }
-      if($row['email'] == $email) { if($i) $r.=', '; $r .= 'e-mail address'; $i++; }
-      if($row['real_name'] == $real_name && $real_name != '') { if($i) $r.=', and '; $r .= 'real name'; $i++; }
+      if ( $row['username'] == $username )
+      {
+        $r .= 'username';
+        $i++;
+      }
+      if ( $row['email'] == $email )
+      {
+        if($i) $r.=', ';
+        $r .= 'e-mail address';
+        $i++;
+      }
+      if ( $row['real_name'] == $real_name && $real_name != '' )
+      {
+        if($i) $r.=', and ';
+        $r .= 'real name';
+        $i++;
+      }
       $r .= ' that you entered ';
       $r .= ( $i == 1 ) ? 'is' : 'are';
       $r .= ' already in use by another user.';
       return $r;
     }
     
+    // Is the password strong enough?
+    if ( getConfig('pw_strength_enable') )
+    {
+      $min_score = intval( getConfig('pw_strength_minimum') );
+      $pass_score = password_score($password);
+      if ( $pass_score < $min_score )
+      {
+        return 'The password you entered did not meet the complexity requirements for this site. Please choose a stronger password.';
+      }
+    }
+    
+    $password = $aes->encrypt($password, $this->private_key, ENC_HEX);
+    
     // Require the account to be activated?
     switch(getConfig('account_activation'))
     {
--- a/includes/template.php	Mon Sep 17 11:52:58 2007 -0400
+++ b/includes/template.php	Tue Sep 18 00:30:43 2007 -0400
@@ -1146,7 +1146,7 @@
     
     $text_parser = $this->makeParserText($tplvars['sidebar_button']);
     
-    preg_match_all('#\[\[([a-zA-Z0-9 -_]*?)\]\]#is', $message, $il);
+    preg_match_all("#\[\[([^\|\]\n\a\r\t]*?)\]\]#is", $message, $il);
     for($i=0;$i<sizeof($il[1]);$i++)
     {
       $href = makeUrl(str_replace(' ', '_', $il[1][$i]), null, true);
@@ -1158,7 +1158,7 @@
       $message = str_replace("[[{$il[1][$i]}]]", $text_parser->run(), $message);
     }
     
-    preg_match_all('#\[\[([a-zA-Z0-9 -_]*?)\|([a-zA-Z0-9!@\#\$%\^&\*\(\)\{\} -_]*?)\]\]#is', $message, $il);
+    preg_match_all('#\[\[([^\|\]\n\a\r\t]*?)\|([^\]\r\n\a\t]*?)\]\]#is', $message, $il);
     for($i=0;$i<sizeof($il[1]);$i++)
     {
       $href = makeUrl(str_replace(' ', '_', $il[1][$i]), null, true);
--- a/plugins/SpecialAdmin.php	Mon Sep 17 11:52:58 2007 -0400
+++ b/plugins/SpecialAdmin.php	Tue Sep 18 00:30:43 2007 -0400
@@ -191,6 +191,17 @@
     setConfig('smtp_user', $_POST['smtp_user']);
     if($_POST['smtp_pass'] != 'XXXXXXXXXXXX') setConfig('smtp_password', $_POST['smtp_pass']);
     
+    // Password strength
+    if ( isset($_POST['pw_strength_enable']) ) setConfig('pw_strength_enable', '1');
+    else                                       setConfig('pw_strength_enable', '0');
+    
+    $strength = intval($_POST['pw_strength_minimum']);
+    if ( $strength >= -10 && $strength <= 30 )
+    {
+      $strength = strval($strength);
+      setConfig('pw_strength_minimum', $strength);
+    }
+    
     echo '<div class="info-box">Your changes to the site configuration have been saved.</div><br />';
     
   }
@@ -339,6 +350,28 @@
         </td>
       </tr>
       
+      <tr><th colspan="2">Password strength</th></tr>
+      
+      <tr>
+        <td class="row2">
+          <b>Enable password strength analysis</b><br />
+          <small>This should be enabled in most cases. When this is enabled, a strength meter and a numerical score will be displayed wherever a password can be changed.</small>
+        </td>
+        <td class="row2">
+          <label><input type="checkbox" name="pw_strength_enable" <?php if ( getConfig('pw_strength_enable') == '1' ) echo 'checked="checked" '; ?>/> Enabled</label>
+        </td>
+      </tr>
+      
+      <tr>
+        <td class="row1">
+          <b>Minimum strength score</b><br />
+          <small>This is the lowest score a password will be allowed to have. -10 will allow any password. A score of under -3 is considered weak, under 1 is fair, under 4 is good, under 10 is strong, and 10 and above are very strong. The scale is open-ended. This only has an effect if the meter is enabled above.</small>
+        </td>
+        <td class="row1">
+          <input type="text" name="pw_strength_minimum" value="<?php echo ( $x = getConfig('pw_strength_minimum') ) ? $x : '-10'; ?>" />
+        </td>
+      </tr>
+      
     <!-- E-mail options -->
     
     <tr><th colspan="2">E-mail sent from the site</th></tr>
@@ -946,12 +979,15 @@
     else
     {
       $disabled = ( $r['user_id'] == $session->user_id ) ? ' disabled="disabled" ' : '';
+      $evt_get_score = ( getConfig('pw_strength_enable') == '1' ) ? 'onkeyup="password_score_field(this);" style="margin-right: 7px;" ' : '';
+      $meter         = ( getConfig('pw_strength_enable') == '1' ) ? '<tr><td></td><td><div id="pwmeter"></div><p><small>Password complexity requirements are not enforced here.</small></p></td></tr>' : '';
       echo('
       <h3>Edit User Info</h3>
       <form action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post">
         <table border="0" style="margin-left: 0.2in;">   
           <tr><td>Username:</td><td><input type="text" name="new_username" value="'.$r['username'].'" /></td></tr>
-          <tr><td>New Password:</td><td><input ' . $disabled . ' type="password" name="new_pass" /></td></tr>
+          <tr><td>New Password:</td><td><input ' . $disabled . ' type="password" name="new_pass" '.$evt_get_score.'/></td></tr>
+          '.$meter.'
           <tr><td>E-mail:</td><td><input ' . $disabled . ' type="text" name="email" value="'.$r['email'].'" /></td></tr>
           <tr><td>Real Name:</td><td><input ' . $disabled . ' type="text" name="real_name" value="'.$r['real_name'].'" /></td></tr>
           ' . ( ( !empty($disabled) ) ? '<tr><td colspan="2"><small>To change your e-mail address, password, or real name, please use the user control panel.</small></td></tr>' : '' ) . '
--- a/plugins/SpecialUserFuncs.php	Mon Sep 17 11:52:58 2007 -0400
+++ b/plugins/SpecialUserFuncs.php	Tue Sep 18 00:30:43 2007 -0400
@@ -333,6 +333,12 @@
 function page_Special_Register()
 {
   global $db, $session, $paths, $template, $plugins; // Common objects
+  
+  // form field trackers
+  $username = '';
+  $email = '';
+  $realname = '';
+  
   if(getConfig('account_activation') == 'disable' && ( ( $session->user_level >= USER_LEVEL_ADMIN && !isset($_GET['IWannaPlayToo']) ) || $session->user_level < USER_LEVEL_ADMIN || !$session->user_logged_in ))
   {
     $s = ($session->user_level >= USER_LEVEL_ADMIN) ? '<p>Oops...it seems that you <em>are</em> the administrator...hehe...you can also <a href="'.makeUrl($paths->page, 'IWannaPlayToo', true).'">force account registration to work</a>.</p>' : '';
@@ -360,9 +366,38 @@
       else
       {
         $coppa = ( isset($_POST['coppa']) && $_POST['coppa'] == 'yes' );
+        $s = false;
+        
+        // decrypt password
+        // as with the change pass form, we aren't going to bother checking the confirmation code because if the passwords didn't match
+        // and yet the password got encrypted, that means the user screwed with the code, and if the user screwed with the code and thus
+        // forgot his password, that's his problem.
+        
+        if ( $_POST['use_crypt'] == 'yes' )
+        {
+          $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE);
+          $crypt_key = $session->fetch_public_key($_POST['crypt_key']);
+          if ( !$crypt_key )
+          {
+            $s = 'Couldn\'t look up public encryption key';
+          }
+          else
+          {
+            $data = $_POST['crypt_data'];
+            $bin_key = hexdecode($crypt_key);
+            //die("Decrypting with params: key $crypt_key, data $data");
+            $password = $aes->decrypt($data, $bin_key, ENC_HEX);
+          }
+        }
+        else
+        {
+          $password = $_POST['password'];
+        }
         
         // CAPTCHA code was correct, create the account
-        $s = $session->create_user($_POST['username'], $_POST['password'], $_POST['email'], $_POST['real_name'], $coppa);
+        // ... and check for errors returned from the crypto API
+        if ( !$s )
+          $s = $session->create_user($_POST['username'], $password, $_POST['email'], $_POST['real_name'], $coppa);
       }
     }
     if($s == 'success' && !$coppa)
@@ -387,6 +422,9 @@
       $str = 'However, in compliance with the Childrens\' Online Privacy Protection Act, you must have your parent or legal guardian activate your account. Please ask them to check their e-mail for further information.';
       die_friendly('Registration successful', '<p>Thank you for registering, your user account has been created. '.$str.'</p>');
     }
+    $username = htmlspecialchars($_POST['username']);
+    $email    = htmlspecialchars($_POST['email']);
+    $realname = htmlspecialchars($_POST['real_name']);
   }
   $template->header();
   echo 'A user account enables you to have greater control over your browsing experience.';
@@ -396,9 +434,13 @@
     $coppa = ( isset($_GET['coppa']) && $_GET['coppa'] == 'yes' );
     $session->kill_captcha();
     $captchacode = $session->make_captcha();
+    
+    $pubkey = $session->rijndael_genkey();
+    $challenge = $session->dss_rand();
+    
     ?>
       <h3>Create a user account</h3>
-      <form name="regform" action="<?php echo makeUrl($paths->page); ?>" method="post">
+      <form name="regform" action="<?php echo makeUrl($paths->page); ?>" method="post" onsubmit="runEncryption();">
         <div class="tblholder">
           <table border="0" width="100%" cellspacing="1" cellpadding="4">
             <tr><th class="subhead" colspan="3">Please tell us a little bit about yourself.</th></tr>
@@ -412,7 +454,7 @@
                 <span id="e_username"></span>
               </td>
               <td class="row1" style="width: 50%;">
-                <input type="text" name="username" size="30" onkeyup="namegood = false; validateForm();" onblur="checkUsername();" />
+                <input tabindex="1" type="text" name="username" size="30" value="<?php echo $username; ?>" onkeyup="namegood = false; validateForm();" onblur="checkUsername();" />
               </td>
               <td class="row1" style="max-width: 24px;">
                 <img alt="Good/bad icon" src="<?php echo scriptPath; ?>/images/bad.gif" id="s_username" />
@@ -421,14 +463,17 @@
             
             <!-- FIELD: Password -->
             <tr>
-              <td class="row3" style="width: 50%;" rowspan="2">
+              <td class="row3" style="width: 50%;" rowspan="<?php echo ( getConfig('pw_strength_enable') == '1' ) ? '3' : '2'; ?>">
                 Password:
                 <span id="e_password"></span>
+                <?php if ( getConfig('pw_strength_enable') == '1' && getConfig('pw_strength_minimum') > -10 ): ?>
+                <small>It needs to score at least <b><?php echo getConfig('pw_strength_minimum'); ?></b> for your registration to be accepted.</small>
+                <?php endif; ?>
               </td>
               <td class="row3" style="width: 50%;">
-                <input type="password" name="password" size="30" onkeyup="validateForm();" />
+                <input tabindex="2" type="password" name="password" size="15" onkeyup="<?php if ( getConfig('pw_strength_enable') == '1' ): ?>password_score_field(this); <?php endif; ?>validateForm();" /><?php if ( getConfig('pw_strength_enable') == '1' ): ?><span class="password-checker" style="font-weight: bold; color: #aaaaaa;"> Loading...</span><?php endif; ?>
               </td>
-              <td rowspan="2" class="row3" style="max-width: 24px;">
+              <td rowspan="<?php echo ( getConfig('pw_strength_enable') == '1' ) ? '3' : '2'; ?>" class="row3" style="max-width: 24px;">
                 <img alt="Good/bad icon" src="<?php echo scriptPath; ?>/images/bad.gif" id="s_password" />
               </td>
             </tr>
@@ -436,10 +481,20 @@
             <!-- FIELD: Password confirmation -->
             <tr>
               <td class="row3" style="width: 50%;">
-                <input type="password" name="password_confirm" size="30" onkeyup="validateForm();" /> <small>Enter your password again to confirm.</small>
+                <input tabindex="3" type="password" name="password_confirm" size="15" onkeyup="validateForm();" /> <small>Enter your password again to confirm.</small>
               </td>
             </tr>
             
+            <!-- FIELD: Password strength meter -->
+            
+            <?php if ( getConfig('pw_strength_enable') == '1' ): ?>
+            <tr>
+              <td class="row3" style="width: 50%;">
+                <div id="pwmeter"></div>
+              </td>
+            </tr>
+            <?php endif; ?>
+            
             <!-- FIELD: E-mail address -->
             <tr>
               <td class="row1" style="width: 50%;">
@@ -455,7 +510,7 @@
                 ?>
               </td>
               <td class="row1" style="width: 50%;">
-                <input type="text" name="email" size="30" onkeyup="validateForm();" />
+                <input tabindex="4" type="text" name="email" size="30" value="<?php echo $email; ?>" onkeyup="validateForm();" />
               </td>
               <td class="row1" style="max-width: 24px;">
                 <img alt="Good/bad icon" src="<?php echo scriptPath; ?>/images/bad.gif" id="s_email" />
@@ -469,7 +524,7 @@
                 <small>Giving your real name is totally optional. If you choose to provide your real name, it will be used to provide attribution for any edits or contributions you may make to this site.</small>
               </td>
               <td class="row3" style="width: 50%;">
-                <input type="text" name="real_name" size="30" /></td><td class="row3" style="max-width: 24px;">
+                <input tabindex="5" type="text" name="real_name" size="30" value="<?php echo $realname; ?>" /></td><td class="row3" style="max-width: 24px;">
               </td>
             </tr>
             
@@ -493,7 +548,7 @@
             <tr>
               <td class="row1" colspan="2">
                 Code:
-                <input name="captchacode" type="text" size="10" />
+                <input tabindex="6" name="captchacode" type="text" size="10" />
                 <input type="hidden" name="captchahash" value="<?php echo $captchacode; ?>" />
               </td>
             </tr>
@@ -501,7 +556,7 @@
             <!-- FIELD: submit button -->
             <tr>
               <th class="subhead" colspan="3" style="text-align: center;">
-                <input type="submit" name="submit" value="Create my account" />
+                <input tabindex="7" type="submit" name="submit" value="Create my account" />
               </td>
             </tr>
             
@@ -511,6 +566,79 @@
           $val = ( $coppa ) ? 'yes' : 'no';
           echo '<input type="hidden" name="coppa" value="' . $val . '" />';
         ?>
+        <input type="hidden" name="challenge_data" value="<?php echo $challenge; ?>" />
+        <input type="hidden" name="use_crypt" value="no" />
+        <input type="hidden" name="crypt_key" value="<?php echo $pubkey; ?>" />
+        <input type="hidden" name="crypt_data" value="" />
+      <script type="text/javascript">
+        // ENCRYPTION CODE
+        disableJSONExts();
+        str = '';
+        for(i=0;i<keySizeInBits/4;i++) str+='0';
+        var key = hexToByteArray(str);
+        var pt = hexToByteArray(str);
+        var ct = rijndaelEncrypt(pt, key, "ECB");
+        var ct = byteArrayToHex(ct);
+        switch(keySizeInBits)
+        {
+          case 128:
+            v = '66e94bd4ef8a2c3b884cfa59ca342b2e';
+            break;
+          case 192:
+            v = 'aae06992acbf52a3e8f4a96ec9300bd7aae06992acbf52a3e8f4a96ec9300bd7';
+            break;
+          case 256:
+            v = 'dc95c078a2408989ad48a21492842087dc95c078a2408989ad48a21492842087';
+            break;
+        }
+        var aes_testpassed = ( ct == v && md5_vm_test() );
+        function runEncryption()
+        {
+          var frm = document.forms.regform;
+          if ( frm.password.value.length < 1 )
+            return true;
+          if(aes_testpassed)
+          {
+            frm.use_crypt.value = 'yes';
+            var cryptkey = frm.crypt_key.value;
+            frm.crypt_key.value = hex_md5(cryptkey);
+            cryptkey = hexToByteArray(cryptkey);
+            if(!cryptkey || ( ( typeof cryptkey == 'string' || typeof cryptkey == 'object' ) ) && cryptkey.length != keySizeInBits / 8 )
+            {
+              frm.submit.disabled = true;
+              len = ( typeof cryptkey == 'string' || typeof cryptkey == 'object' ) ? '\nLen: '+cryptkey.length : '';
+              alert('The key is messed up\nType: '+typeof(cryptkey)+len);
+            }
+          }
+          pass1 = frm.password.value;
+          pass2 = frm.password_confirm.value;
+          if ( pass1 != pass2 )
+          {
+            alert('The passwords you entered do not match.');
+            return false;
+          }
+          if ( pass1.length < 6 && pass1.length > 0 )
+          {
+            alert('The new password must be 6 characters or greater in length.');
+            return false;
+          }
+          if(aes_testpassed)
+          {
+            pass = frm.password.value;
+            pass = stringToByteArray(pass);
+            cryptstring = rijndaelEncrypt(pass, cryptkey, 'ECB');
+            if(!cryptstring)
+            {
+              return false;
+            }
+            cryptstring = byteArrayToHex(cryptstring);
+            frm.crypt_data.value = cryptstring;
+            frm.password.value = "";
+            frm.password_confirm.value = "";
+          }
+          return true;
+        }
+        </script>
       </form>
       <!-- Don't optimize this script, it fails when compressed -->
       <enano:no-opt>
@@ -525,7 +653,9 @@
             // Username
             if(!namegood)
             {
-              if(frm.username.value.match(/^([A-z0-9 \!@\-\(\)]+){2,}$/ig))
+              //if(frm.username.value.match(/^([A-z0-9 \!@\-\(\)]+){2,}$/ig))
+              var regex = new RegExp('^([^<>_&\?]+){2,}$', 'ig');
+              if ( frm.username.value.match(regex) )
               {
                 document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/unknown.gif';
                 document.getElementById('e_username').innerHTML = ''; // '<br /><small><b>Checking availability...</b></small>';
@@ -616,10 +746,13 @@
           }
           function regenCaptcha()
           {
-            var frm = document.forms.regform;
             document.getElementById('captchaimg').src = '<?php echo makeUrlNS("Special", "Captcha/"); ?>'+frm.captchahash.value+'/'+Math.floor(Math.random() * 100000);
             return false;
           }
+          <?php if ( getConfig('pw_strength_enable') == '1' ): ?>
+          var frm = document.forms.regform;
+          password_score_field(frm.password);
+          <?php endif; ?>
           validateForm();
           setTimeout('checkUsername();', 1000);
           // ]]>
@@ -898,9 +1031,9 @@
     $row = $db->fetchrow();
     $db->free_result();
     
-    if ( ( intval($row['temp_password_time']) + 3600 * 24 ) < time() )
+    if ( ( intval($row['temp_password_time']) + ( 3600 * 24 ) ) < time() )
     {
-      echo '<p>Password has expired</p>';
+      echo '<p>Your temporary password has expired. Please <a href="' . makeUrlNS('Special', 'PasswordReset') . '">request another one</a>.</p>';
       $template->footer();
       return false;
     }
@@ -949,6 +1082,18 @@
         $template->footer();
         return false;
       }
+      if ( getConfig('pw_strength_enable') == '1' )
+      {
+        $min_score = intval(getConfig('pw_strength_minimum'));
+        $inp_score = password_score($data);
+        if ( $inp_score < $min_score )
+        {
+          $url = makeUrl($paths->fullpage);
+          echo "<p>ERROR: Your password did not pass the complexity score requirement. You need $min_score points to pass; your password received a score of $inp_score. <a href=\"$url\">Go back</a></p>";
+          $template->footer();
+          return false;
+        }
+      }
       $encpass = $aes->encrypt($data, $session->private_key, ENC_HEX);
       $q = $db->sql_query('UPDATE '.table_prefix.'users SET password=\'' . $encpass . '\',temp_password=\'\',temp_password_time=0 WHERE user_id='.$user_id.';');
       
@@ -969,14 +1114,19 @@
     // Password reset form
     $pubkey = $session->rijndael_genkey();
     
+    $evt_get_score = ( getConfig('pw_strength_enable') == '1' ) ? 'onkeyup="password_score_field(this);" ' : '';
+    $pw_meter =      ( getConfig('pw_strength_enable') == '1' ) ? '<tr><td class="row1">Password strength rating:</td><td class="row1"><div id="pwmeter"></div><script type="text/javascript">password_score_field(document.forms.resetform.pass);</script></td></tr>' : '';
+    $pw_blurb =      ( getConfig('pw_strength_enable') == '1' && intval(getConfig('pw_strength_minimum')) > -10 ) ? '<br /><small>Your password needs to have a score of at least <b>'.getConfig('pw_strength_minimum').'</b>.</small>' : '';
+    
     ?>
     <form action="<?php echo makeUrl($paths->fullpage); ?>" method="post" name="resetform" onsubmit="return runEncryption();">
       <br />
       <div class="tblholder">
         <table border="0" style="width: 100%;" cellspacing="1" cellpadding="4">
           <tr><th colspan="2">Reset password</th></tr>
-          <tr><td class="row1">Password:</td><td class="row1"><input name="pass" type="password" /></td></tr>
+          <tr><td class="row1">Password:<?php echo $pw_blurb; ?></td><td class="row1"><input name="pass" type="password" <?php echo $evt_get_score; ?>/></td></tr>
           <tr><td class="row2">Confirm: </td><td class="row2"><input name="pass_confirm" type="password" /></td></tr>
+          <?php echo $pw_meter; ?>
           <tr>
             <td colspan="2" class="row1" style="text-align: center;">
               <input type="hidden" name="use_crypt" value="no" />
--- a/plugins/SpecialUserPrefs.php	Mon Sep 17 11:52:58 2007 -0400
+++ b/plugins/SpecialUserPrefs.php	Tue Sep 18 00:30:43 2007 -0400
@@ -214,7 +214,14 @@
           {
             // Perform checks
             if ( strlen($newpass) < 6 )
-              $errors .= '<div class="error-box">Password must be at least 6 characters. You hacked my script, darn you!</div>';
+              $errors .= '<div class="error-box" style="margin: 0 0 10px 0;">Password must be at least 6 characters. You hacked my script, darn you!</div>';
+            if ( getConfig('pw_strength_enable') == '1' )
+            {
+              $score_inp = password_score($newpass);
+              $score_min = intval( getConfig('pw_strength_minimum') );
+              if ( $score_inp < $score_min )
+                $errors .= '<div class="error-box" style="margin: 0 0 10px 0;">Your password did not meet the complexity score requirement for this site. Your password scored '. $score_inp .', while a score of at least '. $score_min .' is needed.</div>';
+            }
             // Encrypt new password
             if ( empty($errors) )
             {
@@ -304,6 +311,12 @@
       break;
     case 'EmailPassword':
       
+      $errors = trim($errors);
+      if ( !empty($errors) )
+      {
+        echo $errors;
+      }
+      
       echo '<form action="' . makeUrlNS('Special', 'Preferences/EmailPassword') . '" method="post" onsubmit="return runEncryption();" name="empwform" >';
       
       // Password change form
@@ -312,20 +325,22 @@
       echo '<fieldset>
         <legend>Change password</legend>
         Type a new password:<br />
-          <input type="password" name="newpass" size="30" tabindex="1" />
+          <input type="password" name="newpass" size="30" tabindex="1" ' . ( getConfig('pw_strength_enable') == '1' ? 'onkeyup="password_score_field(this);" ' : '' ) . '/>' . ( getConfig('pw_strength_enable') == '1' ? '<span class="password-checker" style="font-weight: bold; color: #aaaaaa;"> Loading...</span>' : '' ) . '
         <br />
         <br />
         Type the password again to confirm:<br />
-          <input type="password" name="newpass_conf" size="30" tabindex="2" />
+        <input type="password" name="newpass_conf" size="30" tabindex="2" />
+        ' . ( getConfig('pw_strength_enable') == '1' ? '<br /><br /><div id="pwmeter"></div>
+        <small>Your password needs to score at least <b>'.getConfig('pw_strength_minimum').'</b> in order to be accepted.</small>' : '' ) . '
       </fieldset><br />
       <fieldset>
         <legend>Change e-mail address</legend>
         New e-mail address:<br />
-          <input type="text" name="newemail" size="30" tabindex="3" />
+          <input type="text" value="' . ( isset($_POST['newemail']) ? htmlspecialchars($_POST['newemail']) : '' ) . '" name="newemail" size="30" tabindex="3" />
         <br />
         <br />
         Confirm e-mail address:<br />
-          <input type="text" name="newemail_conf" size="30" tabindex="4" />
+          <input type="text" value="' . ( isset($_POST['newemail']) ? htmlspecialchars($_POST['newemail']) : '' ) . '" name="newemail_conf" size="30" tabindex="4" />
       </fieldset>
       <input type="hidden" name="use_crypt" value="no" />
       <input type="hidden" name="crypt_key" value="' . $pubkey . '" />
@@ -338,6 +353,9 @@
       // ENCRYPTION CODE
       ?>
       <script type="text/javascript">
+      <?php if ( getConfig('pw_strength_enable') == '1' ): ?>
+      password_score_field(document.forms.empwform.newpass);
+      <?php endif; ?>
         disableJSONExts();
         str = '';
         for(i=0;i<keySizeInBits/4;i++) str+='0';