diff --git a/include/ajax.content.php b/include/ajax.content.php
index 7ba1a1d4c31adfb467e87d01ed856b8e547c909a..197d75ffe50c622296ca0bb22335874773fb499b 100644
--- a/include/ajax.content.php
+++ b/include/ajax.content.php
@@ -15,9 +15,9 @@
 **********************************************************************/
 
 if(!defined('INCLUDE_DIR')) die('!');
-	    
+
 class ContentAjaxAPI extends AjaxController {
-   
+
     function log($id) {
 
         if($id && ($log=Log::lookup($id))) {
@@ -77,6 +77,8 @@ class ContentAjaxAPI extends AjaxController {
                     <tr><td>%{assignee}</td><td>Assigned staff/team</td></tr>
                     <tr><td>%{assigner}</td><td>Staff assigning the ticket</td></tr>
                     <tr><td>%{url}</td><td>osTicket\'s base url (FQDN)</td></tr>
+                    <tr><td>%{reset_link}</td>
+                        <td>Reset link used by the password reset feature</td></tr>
                 </table>
             </td>
         </tr>
diff --git a/include/class.config.php b/include/class.config.php
index 3aaf9d7164102e7ff6275cb0ecd4f94cb36db36d..3726309876cf198d317e81e7988a7aa9f53476d5 100644
--- a/include/class.config.php
+++ b/include/class.config.php
@@ -474,6 +474,30 @@ class OsticketConfig extends Config {
         return ($this->get('staff_ip_binding'));
     }
 
+    /**
+     * Configuration: allow_pw_reset
+     *
+     * TRUE if the <a>Forgot my password</a> link and system should be
+     * enabled, and FALSE otherwise.
+     */
+    function allowPasswordReset() {
+        return $this->get('allow_pw_reset');
+    }
+
+    /**
+     * Configuration: pw_reset_window
+     *
+     * Number of minutes for which the password reset token is valid.
+     *
+     * Returns: Number of seconds the password reset token is valid. The
+     *      number of minutes from the database is automatically converted
+     *      to seconds here.
+     */
+    function getPwResetWindow() {
+        // pw_reset_window is stored in minutes. Return value in seconds
+        return $this->get('pw_reset_window') * 60;
+    }
+
     function isCaptchaEnabled() {
         return (extension_loaded('gd') && function_exists('gd_info') && $this->get('enable_captcha'));
     }
@@ -744,6 +768,8 @@ class OsticketConfig extends Config {
         $f['datetime_format']=array('type'=>'string',   'required'=>1, 'error'=>'Datetime format required');
         $f['daydatetime_format']=array('type'=>'string',   'required'=>1, 'error'=>'Day, Datetime format required');
         $f['default_timezone_id']=array('type'=>'int',   'required'=>1, 'error'=>'Default Timezone required');
+        $f['pw_reset_window']=array('type'=>'int', 'required'=>1, 'min'=>1,
+            'error'=>'Valid password reset window required');
 
 
         if(!Validator::process($f, $vars, $errors) || $errors)
@@ -766,6 +792,8 @@ class OsticketConfig extends Config {
             'client_max_logins'=>$vars['client_max_logins'],
             'client_login_timeout'=>$vars['client_login_timeout'],
             'client_session_timeout'=>$vars['client_session_timeout'],
+            'allow_pw_reset'=>isset($vars['allow_pw_reset'])?1:0,
+            'pw_reset_window'=>$vars['pw_reset_window'],
             'time_format'=>$vars['time_format'],
             'date_format'=>$vars['date_format'],
             'datetime_format'=>$vars['datetime_format'],
diff --git a/include/class.staff.php b/include/class.staff.php
index a4edb4f7abd7e9a104d5ba182f87ff74dc9d62e4..19bd71bf02da54c1ee1fd6c36ac356c32f0ca7aa 100644
--- a/include/class.staff.php
+++ b/include/class.staff.php
@@ -15,6 +15,7 @@
 **********************************************************************/
 include_once(INCLUDE_DIR.'class.ticket.php');
 include_once(INCLUDE_DIR.'class.dept.php');
+include_once(INCLUDE_DIR.'class.error.php');
 include_once(INCLUDE_DIR.'class.team.php');
 include_once(INCLUDE_DIR.'class.group.php');
 include_once(INCLUDE_DIR.'class.passwd.php');
@@ -401,6 +402,7 @@ class Staff {
 
     //Staff profile update...unfortunately we have to separate it from admin update to avoid potential issues
     function updateProfile($vars, &$errors) {
+        global $cfg;
 
         $vars['firstname']=Format::striptags($vars['firstname']);
         $vars['lastname']=Format::striptags($vars['lastname']);
@@ -437,7 +439,17 @@ class Staff {
             elseif($vars['passwd1'] && strcmp($vars['passwd1'], $vars['passwd2']))
                 $errors['passwd2']='Password(s) do not match';
 
-            if(!$vars['cpasswd'])
+            if (($rtoken = $_SESSION['_staff']['reset-token'])) {
+                $_config = new Config('pwreset');
+                if ($_config->get($rtoken) != $this->getId())
+                    $errors['err'] =
+                        'Invalid reset token. Logout and try again';
+                elseif (!($ts = $_config->lastModified($rtoken))
+                        && ($cfg->getPwResetWindow() < (time() - strtotime($ts))))
+                    $errors['err'] =
+                        'Invalid reset token. Logout and try again';
+            }
+            elseif(!$vars['cpasswd'])
                 $errors['cpasswd']='Current password required';
             elseif(!$this->cmp_passwd($vars['cpasswd']))
                 $errors['cpasswd']='Invalid current password!';
@@ -470,8 +482,10 @@ class Staff {
             .' ,default_paper_size='.db_input($vars['default_paper_size']);
 
 
-        if($vars['passwd1'])
+        if($vars['passwd1']) {
             $sql.=' ,change_passwd=0, passwdreset=NOW(), passwd='.db_input(Passwd::hash($vars['passwd1']));
+            $this->cancelResetTokens();
+        }
 
         $sql.=' WHERE staff_id='.db_input($this->getId());
 
@@ -576,7 +590,7 @@ class Staff {
     }
 
     function lookup($id) {
-        return ($id && is_numeric($id) && ($staff= new Staff($id)) && $staff->getId()==$id)?$staff:null;
+        return ($id && ($staff= new Staff($id)) && $staff->getId()) ? $staff : null;
     }
 
     function login($username, $passwd, &$errors, $strike=true) {
@@ -600,31 +614,10 @@ class Staff {
         if($errors) return false;
 
         if(($user=new StaffSession(trim($username))) && $user->getId() && $user->check_passwd($passwd)) {
-            //update last login && password reset stuff.
-            $sql='UPDATE '.STAFF_TABLE.' SET lastlogin=NOW() ';
-            if($user->isPasswdResetDue() && !$user->isAdmin())
-                $sql.=',change_passwd=1';
-            $sql.=' WHERE staff_id='.db_input($user->getId());
-            db_query($sql);
-            //Now set session crap and lets roll baby!
-            $_SESSION['_staff'] = array(); //clear.
-            $_SESSION['_staff']['userID'] = $username;
-            $user->refreshSession(); //set the hash.
-            $_SESSION['TZ_OFFSET'] = $user->getTZoffset();
-            $_SESSION['TZ_DST'] = $user->observeDaylight();
-
-            //Log debug info.
-            $ost->logDebug('Staff login',
-                    sprintf("%s logged in [%s]", $user->getUserName(), $_SERVER['REMOTE_ADDR'])); //Debug.
-
-            //Regenerate session id.
-            $sid=session_id(); //Current id
-            session_regenerate_id(TRUE);
-            //Destroy old session ID - needed for PHP version < 5.1.0 TODO: remove when we move to php 5.3 as min. requirement.
-            if(($session=$ost->getSession()) && is_object($session) && $sid!=session_id())
-                $session->destroy($sid);
+            self::_do_login($user, $username);
 
             Signal::send('auth.login.succeeded', $user);
+            $user->cancelResetTokens();
 
             return $user;
         }
@@ -651,6 +644,36 @@ class Staff {
         return false;
     }
 
+    function _do_login($user, $username) {
+        global $ost;
+
+        //update last login && password reset stuff.
+        $sql='UPDATE '.STAFF_TABLE.' SET lastlogin=NOW() ';
+        if($user->isPasswdResetDue() && !$user->isAdmin())
+            $sql.=',change_passwd=1';
+        $sql.=' WHERE staff_id='.db_input($user->getId());
+        db_query($sql);
+        //Now set session crap and lets roll baby!
+        $_SESSION['_staff'] = array(); //clear.
+        $_SESSION['_staff']['userID'] = $username;
+        $user->refreshSession(); //set the hash.
+        $_SESSION['TZ_OFFSET'] = $user->getTZoffset();
+        $_SESSION['TZ_DST'] = $user->observeDaylight();
+
+        //Log debug info.
+        $ost->logDebug('Staff login',
+                sprintf("%s logged in [%s]", $user->getUserName(), $_SERVER['REMOTE_ADDR'])); //Debug.
+
+        //Regenerate session id.
+        $sid=session_id(); //Current id
+        session_regenerate_id(TRUE);
+        //Destroy old session ID - needed for PHP version < 5.1.0 TODO: remove when we move to php 5.3 as min. requirement.
+        if(($session=$ost->getSession()) && is_object($session) && $sid!=session_id())
+            $session->destroy($sid);
+
+        return $user;
+    }
+
     function create($vars, &$errors) {
         if(($id=self::save(0, $vars, $errors)) && $vars['teams'] && ($staff=Staff::lookup($id))) {
             $staff->updateTeams($vars['teams']);
@@ -660,6 +683,43 @@ class Staff {
         return $id;
     }
 
+    function cancelResetTokens() {
+        // TODO: Drop password-reset tokens from the config table for
+        //       this user id
+        $sql = 'DELETE FROM '.CONFIG_TABLE.' WHERE `namespace`="pwreset"
+            AND `value`='.db_input($this->getId());
+        db_query($sql);
+        unset($_SESSION['_staff']['reset-token']);
+    }
+
+    function sendResetEmail() {
+        global $ost, $cfg;
+
+        if(!($tpl = $this->getDept()->getTemplate()))
+            $tpl= $ost->getConfig()->getDefaultTemplate();
+
+        $token = Misc::randCode(48); // 290-bits
+        if (!($template = $tpl->getMsgTemplate('staff.pwreset')))
+            return new Error('Unable to retrieve password reset email template');
+
+        $msg = $ost->replaceTemplateVariables($template->asArray(), array(
+            'url' => $ost->getConfig()->getBaseUrl(),
+            'token' => $token,
+            'reset_link' => sprintf(
+                "%s/scp/pwreset.php?token=%s",
+                $ost->getConfig()->getBaseUrl(),
+                $token),
+        ));
+
+        if(!($email=$cfg->getAlertEmail()))
+            $email =$cfg->getDefaultEmail();
+
+        $_config = new Config('pwreset');
+        $_config->set($token, $this->getId());
+
+        $email->send($this->getEmail(), $msg['subj'], $msg['body']);
+    }
+
     function save($id, $vars, &$errors) {
 
         $vars['username']=Format::striptags($vars['username']);
@@ -736,8 +796,9 @@ class Staff {
             .' ,signature='.db_input($vars['signature'])
             .' ,notes='.db_input($vars['notes']);
 
-        if($vars['passwd1'])
+        if($vars['passwd1']) {
             $sql.=' ,passwd='.db_input(Passwd::hash($vars['passwd1']));
+        }
 
         if(isset($vars['change_passwd']))
             $sql.=' ,change_passwd=1';
diff --git a/include/class.template.php b/include/class.template.php
index c85297d2fd5f23f1cfb8b1b0df82bfc852369478..c270b41bf8376f13c2803f51bcdb9bc288443a1a 100644
--- a/include/class.template.php
+++ b/include/class.template.php
@@ -56,6 +56,9 @@ class EmailTemplateGroup {
         'ticket.overdue'=>array(
             'name'=>'Overdue Ticket Alert',
             'desc'=>'Alert sent to staff on stale or overdue tickets.'),
+        'staff.pwreset' => array(
+            'name' => 'Staff Password Reset',
+            'desc' => 'Notice sent to staff with the password reset link.'),
         );
 
     function EmailTemplateGroup($id){
diff --git a/include/staff/login.header.php b/include/staff/login.header.php
new file mode 100644
index 0000000000000000000000000000000000000000..679a509f96baa7f581bf8463048d3b642531d51a
--- /dev/null
+++ b/include/staff/login.header.php
@@ -0,0 +1,22 @@
+<?php
+defined('OSTSCPINC') or die('Invalid path');
+?>
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+    <title>osTicket:: SCP Login</title>
+    <link rel="stylesheet" href="css/login.css" type="text/css" />
+    <meta name="robots" content="noindex" />
+    <meta http-equiv="cache-control" content="no-cache" />
+    <meta http-equiv="pragma" content="no-cache" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
+    <script type="text/javascript" src="../js/jquery-1.7.2.min.js"></script>
+    <script type="text/javascript">
+        $(document).ready(function() {
+            $("input:not(.dp):visible:enabled:first").focus();
+         });
+    </script>
+</head>
+<body id="loginBody">
+
diff --git a/include/staff/login.tpl.php b/include/staff/login.tpl.php
index b8b136eb56d01282e8b15dbd4c0f11085e437eaa..6d5435732128d556219c0230bdfb66004bdc21e7 100644
--- a/include/staff/login.tpl.php
+++ b/include/staff/login.tpl.php
@@ -1,26 +1,7 @@
-<?php 
-defined('OSTSCPINC') or die('Invalid path');
-
+<?php
+include_once(INCLUDE_DIR.'staff/login.header.php');
 $info = ($_POST && $errors)?Format::htmlchars($_POST):array();
 ?>
-<!DOCTYPE html>
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head>
-    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
-    <title>osTicket:: SCP Login</title>
-    <link rel="stylesheet" href="css/login.css" type="text/css" />
-    <meta name="robots" content="noindex" />
-    <meta http-equiv="cache-control" content="no-cache" />
-    <meta http-equiv="pragma" content="no-cache" />
-    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
-    <script type="text/javascript" src="../js/jquery-1.7.2.min.js"></script>
-    <script type="text/javascript">
-        $(document).ready(function() {
-            $("input:not(.dp):visible:enabled:first").focus();
-         });
-    </script>
-</head>
-<body id="loginBody">
 <div id="loginBox">
     <h1 id="logo"><a href="index.php">osTicket Staff Control Panel</a></h1>
     <h3><?php echo Format::htmlchars($msg); ?></h3>
@@ -28,9 +9,12 @@ $info = ($_POST && $errors)?Format::htmlchars($_POST):array();
         <?php csrf_token(); ?>
         <input type="hidden" name="do" value="scplogin">
         <fieldset>
-            <input type="text" name="username" id="name" value="<?php echo $info['username']; ?>" placeholder="username" autocorrect="off" autocapitalize="off">
+            <input type="text" name="userid" id="name" value="<?php echo $info['username']; ?>" placeholder="username" autocorrect="off" autocapitalize="off">
             <input type="password" name="passwd" id="pass" placeholder="password" autocorrect="off" autocapitalize="off">
         </fieldset>
+        <?php if ($_SESSION['_staff']['strikes'] > 1 && $cfg->allowPasswordReset()) { ?>
+        <h3 style="display:inline"><a href="pwreset.php">Forgot my password</a></h3>
+        <?php } ?>
         <input class="submit" type="submit" name="submit" value="Log In">
     </form>
 </div>
diff --git a/include/staff/profile.inc.php b/include/staff/profile.inc.php
index 073a7c8a44229e1b18bf7b7b4cc738d8689d8396..2543c80d1cfa68444199a894f117c1521774afb4 100644
--- a/include/staff/profile.inc.php
+++ b/include/staff/profile.inc.php
@@ -190,6 +190,7 @@ $info['id']=$staff->getId();
                 <em><strong>Password</strong>: To reset your password, provide your current password and a new password below.&nbsp;<span class="error">&nbsp;<?php echo $errors['passwd']; ?></span></em>
             </th>
         </tr>
+        <?php if (!isset($_SESSION['_staff']['reset-token'])) { ?>
         <tr>
             <td width="180">
                 Current Password:
@@ -199,6 +200,7 @@ $info['id']=$staff->getId();
                 &nbsp;<span class="error">&nbsp;<?php echo $errors['cpasswd']; ?></span>
             </td>
         </tr>
+        <?php } ?>
         <tr>
             <td width="180">
                 New Password:
diff --git a/include/staff/pwreset.login.php b/include/staff/pwreset.login.php
new file mode 100644
index 0000000000000000000000000000000000000000..6f93f1f0118093aefd5eb7b67568a560d2fb9066
--- /dev/null
+++ b/include/staff/pwreset.login.php
@@ -0,0 +1,26 @@
+<?php
+include_once(INCLUDE_DIR.'staff/login.header.php');
+defined('OSTSCPINC') or die('Invalid path');
+$info = ($_POST)?Format::htmlchars($_POST):array();
+?>
+
+<div id="loginBox">
+    <h1 id="logo"><a href="index.php">osTicket Staff Password Reset</a></h1>
+    <h3><?php echo Format::htmlchars($msg); ?></h3>
+
+    <form action="pwreset.php" method="post">
+        <?php csrf_token(); ?>
+        <input type="hidden" name="do" value="newpasswd"/>
+        <input type="hidden" name="token" value="<?php echo $_REQUEST['token']; ?>"/>
+        <fieldset>
+            <input type="text" name="userid" id="name" value="<?php echo
+                $info['userid']; ?>" placeholder="username or email"
+                autocorrect="off" autocapitalize="off"/>
+        </fieldset>
+        <input class="submit" type="submit" name="submit" value="Login"/>
+    </form>
+</div>
+
+<div id="copyRights">Copyright &copy; <a href='http://www.osticket.com' target="_blank">osTicket.com</a></div>
+</body>
+</html>
diff --git a/include/staff/pwreset.php b/include/staff/pwreset.php
new file mode 100644
index 0000000000000000000000000000000000000000..6aadeb2fcf10dbb308ed7ec3f548f4afd6c94ee8
--- /dev/null
+++ b/include/staff/pwreset.php
@@ -0,0 +1,25 @@
+<?php
+include_once(INCLUDE_DIR.'staff/login.header.php');
+defined('OSTSCPINC') or die('Invalid path');
+$info = ($_POST && $errors)?Format::htmlchars($_POST):array();
+?>
+
+<div id="loginBox">
+    <h1 id="logo"><a href="index.php">osTicket Staff Password Reset</a></h1>
+    <h3><?php echo Format::htmlchars($msg); ?></h3>
+    <form action="pwreset.php" method="post">
+        <?php csrf_token(); ?>
+        <input type="hidden" name="do" value="sendmail">
+        <fieldset>
+            <input type="text" name="userid" id="name" value="<?php echo
+                $info['userid']; ?>" placeholder="username" autocorrect="off"
+                autocapitalize="off">
+        </fieldset>
+        <input class="submit" type="submit" name="submit" value="Send Email"/>
+    </form>
+
+</div>
+
+<div id="copyRights">Copyright &copy; <a href='http://www.osticket.com' target="_blank">osTicket.com</a></div>
+</body>
+</html>
diff --git a/include/staff/pwreset.sent.php b/include/staff/pwreset.sent.php
new file mode 100644
index 0000000000000000000000000000000000000000..832b78ef57470c4045ed615c152dc228eb414e79
--- /dev/null
+++ b/include/staff/pwreset.sent.php
@@ -0,0 +1,22 @@
+<?php
+include_once(INCLUDE_DIR.'staff/login.header.php');
+defined('OSTSCPINC') or die('Invalid path');
+$info = ($_POST && $errors)?Format::htmlchars($_POST):array();
+?>
+
+<div id="loginBox">
+    <h1 id="logo"><a href="index.php">osTicket Staff Password Reset</a></h1>
+    <h3>A confirmation email has been sent</h3>
+    <h3 style="color:black;"><em>
+    A password reset email was sent to the email on file for your account.
+    Follow the link in the email to reset your password.
+    </em></h3>
+
+    <form action="index.php" method="get">
+        <input class="submit" type="submit" name="submit" value="Login"/>
+    </form>
+</div>
+
+<div id="copyRights">Copyright &copy; <a href='http://www.osticket.com' target="_blank">osTicket.com</a></div>
+</body>
+</html>
diff --git a/include/staff/settings-system.inc.php b/include/staff/settings-system.inc.php
index 6fade6d964b1fab6cca118fa0b8cff86841aa417..8915c8b4f87048d60cfba3a4d0fe3e2ca697dc62 100644
--- a/include/staff/settings-system.inc.php
+++ b/include/staff/settings-system.inc.php
@@ -112,7 +112,12 @@ $gmtime = Misc::gmtime();
                 </select>
             </td>
         </tr>
-        <tr><td>Password Reset Policy:</th>
+        <tr>
+            <th colspan="2">
+                <em><b>Authentication Settings</b></em>
+            </th>
+        </tr>
+        <tr><td>Password Change Policy:</th>
             <td>
                 <select name="passwd_reset_period">
                    <option value="0"> &mdash; None &mdash;</option>
@@ -126,10 +131,20 @@ $gmtime = Misc::gmtime();
                 &nbsp;<font class="error">&nbsp;<?php echo $errors['passwd_reset_period']; ?></font>
             </td>
         </tr>
-        <tr><td>Bind Staff Session to IP:</td>
+        <tr><td>Allow Password Resets:</th>
             <td>
-              <input type="checkbox" name="staff_ip_binding" <?php echo $config['staff_ip_binding']?'checked="checked"':''; ?>>
-              <em>(binds staff session to originating IP address upon login)</em>
+              <input type="checkbox" name="allow_pw_reset" <?php echo $config['allow_pw_reset']?'checked="checked"':''; ?>>
+              <em>Enables the <u>Forgot my password</u> link on the staff
+              control panel</em>
+            </td>
+        </tr>
+        <tr><td>Password Reset Window:</th>
+            <td>
+              <input type="text" name="pw_reset_window" size="6" value="<?php
+                    echo $config['pw_reset_window']; ?>">
+                Maximum time <em>in minutes</em> a password reset token can
+                be valid.
+                &nbsp;<font class="error">&nbsp;<?php echo $errors['pw_reset_window']; ?></font>
             </td>
         </tr>
         <tr><td>Staff Excessive Logins:</td>
@@ -182,6 +197,12 @@ $gmtime = Misc::gmtime();
                 &nbsp;Maximum idle time in minutes before a client must log in again (enter 0 to disable).
             </td>
         </tr>
+        <tr><td>Bind Staff Session to IP:</td>
+            <td>
+              <input type="checkbox" name="staff_ip_binding" <?php echo $config['staff_ip_binding']?'checked="checked"':''; ?>>
+              <em>(binds staff session to originating IP address upon login)</em>
+            </td>
+        </tr>
         <tr>
             <th colspan="2">
                 <em><b>Date and Time Options</b>: Please refer to <a href="http://php.net/date" target="_blank">PHP Manual</a> for supported parameters.</em>
diff --git a/include/staff/tpl.inc.php b/include/staff/tpl.inc.php
index 8c1ede75e5e4ed12092c08201d22a0aebeaa2cc2..58e0b57fcef0b42d326818b22652efe976ee2ce9 100644
--- a/include/staff/tpl.inc.php
+++ b/include/staff/tpl.inc.php
@@ -6,6 +6,7 @@ if (is_a($template, EmailTemplateGroup)) {
     $id = 0;
     $tpl_id = $template->getId();
     $name = $template->getName();
+    $group = $template;
     $selected = $_REQUEST['code_name'];
     $action = 'implement';
     $extras = array('code_name'=>$selected, 'tpl_id'=>$tpl_id);
@@ -15,6 +16,7 @@ if (is_a($template, EmailTemplateGroup)) {
     $id = $template->getId();
     $tpl_id = $template->getTplId();
     $name = $template->getGroup()->getName();
+    $group = $template->getGroup();
     $selected = $template->getCodeName();
     $action = 'updatetpl';
     $extras = array();
@@ -33,7 +35,7 @@ $tpl=$msgtemplates[$info['tpl']];
     <select id="tpl_options" name="id" style="width:300px;">
         <option value="">&mdash; Select Setting Group &mdash;</option>
         <?php
-        foreach($template->getGroup()->getTemplates() as $cn=>$t) {
+        foreach($group->getTemplates() as $cn=>$t) {
             $nfo=$t->getDescription();
             if (!$nfo['name'])
                 continue;
@@ -41,6 +43,10 @@ $tpl=$msgtemplates[$info['tpl']];
             echo sprintf('<option value="%s" %s>%s</option>',
                     $t->getId(),$sel,$nfo['name']);
         }
+        if ($id == 0) { ?>
+            <option selected="selected" value="<?php echo $id; ?>"><?php
+            echo $msgtemplates[$selected]['name']; ?></option>
+        <?php }
         ?>
     </select>
     <input type="submit" value="Go">
diff --git a/scp/login.php b/scp/login.php
index fcefaafd666660cdaf2d44c5e32e4b0bcfaeab37..2f3cf2236e9f4996bb10b94764fb6d0a14d99d22 100644
--- a/scp/login.php
+++ b/scp/login.php
@@ -24,7 +24,7 @@ $msg = $_SESSION['_staff']['auth']['msg'];
 $msg = $msg?$msg:'Authentication Required';
 if($_POST) {
     //$_SESSION['_staff']=array(); #Uncomment to disable login strikes.
-    if(($user=Staff::login($_POST['username'], $_POST['passwd'], $errors))){
+    if(($user=Staff::login($_POST['userid'], $_POST['passwd'], $errors))){
         $dest=($dest && (!strstr($dest,'login.php') && !strstr($dest,'ajax.php')))?$dest:'index.php';
         @header("Location: $dest");
         require_once('index.php'); //Just incase header is messed up.
diff --git a/scp/pwreset.php b/scp/pwreset.php
new file mode 100644
index 0000000000000000000000000000000000000000..a8efb2f6ef86715463398d01143d907a0313e10e
--- /dev/null
+++ b/scp/pwreset.php
@@ -0,0 +1,88 @@
+<?php
+/*********************************************************************
+    pwreset.php
+
+    Handles step 2, 3 and 5 of password resetting
+        1. Fail to login (2+ fail login attempts)
+        2. Visit password reset form and enter username or email
+        3. Receive an email with a link and follow it
+        4. Visit password reset form again, with the link
+        5. Enter the username or email address again and login
+        6. Password change is now required, user changes password and
+           continues on with the session
+
+    Peter Rotich <peter@osticket.com>
+    Jared Hancock <jared@osticket.com>
+    Copyright (c)  2006-2013 osTicket
+    http://www.osticket.com
+
+    Released under the GNU General Public License WITHOUT ANY WARRANTY.
+    See LICENSE.TXT for details.
+
+    vim: expandtab sw=4 ts=4 sts=4:
+**********************************************************************/
+require_once('../main.inc.php');
+if(!defined('INCLUDE_DIR')) die('Fatal Error. Kwaheri!');
+
+require_once(INCLUDE_DIR.'class.staff.php');
+require_once(INCLUDE_DIR.'class.csrf.php');
+
+$tpl = 'pwreset.php';
+if($_POST) {
+    if (!$ost->checkCSRFToken()) {
+        Http::response(400, 'Valid CSRF Token Required');
+        exit;
+    }
+    switch ($_POST['do']) {
+        case 'sendmail':
+            if (($staff=Staff::lookup($_POST['userid']))) {
+                if (!$staff->sendResetEmail()) {
+                    $tpl = 'pwreset.sent.php';
+                }
+            }
+            else
+                $msg = 'Unable to verify username '
+                    .Format::htmlchars($_POST['userid']);
+            break;
+        case 'newpasswd':
+            // TODO: Compare passwords
+            $tpl = 'pwreset.login.php';
+            $_config = new Config('pwreset');
+            if (($staff = new StaffSession($_POST['userid'])) &&
+                    !$staff->getId())
+                $msg = 'Invalid user-id given';
+            elseif (!($id = $_config->get($_POST['token']))
+                    || $id != $staff->getId())
+                $msg = 'Invalid reset token';
+            elseif (!($ts = $_config->lastModified($_POST['token']))
+                    && ($ost->getConfig()->getPwResetWindow() < (time() - strtotime($ts))))
+                $msg = 'Invalid reset token';
+            elseif (!$staff->forcePasswdRest())
+                $msg = 'Unable to reset password';
+            else {
+                Staff::_do_login($staff, $_POST['userid']);
+                $_SESSION['_staff']['reset-token'] = $_POST['token'];
+                header('Location: index.php');
+                exit();
+            }
+            break;
+    }
+}
+elseif ($_GET['token']) {
+    $msg = 'Re-enter your username or email';
+    $_config = new Config('pwreset');
+    if (($id = $_config->get($_GET['token']))
+            && ($staff = Staff::lookup($id)))
+        $tpl = 'pwreset.login.php';
+    else
+        header('Location: index.php');
+}
+elseif ($cfg->allowPasswordReset()) {
+    $msg = 'Enter your username or email address below';
+}
+else {
+    $_SESSION['_staff']['auth']['msg']='Password resets are disabled';
+    return header('Location: index.php');
+}
+define("OSTSCPINC",TRUE); //Make includes happy!
+include_once(INCLUDE_DIR.'staff/'. $tpl);
diff --git a/scp/staff.inc.php b/scp/staff.inc.php
index 577fdd12c6a124a0d98dd50b792d080ab40f983e..503c3cd413be64319882c255f9595029cbd0615d 100644
--- a/scp/staff.inc.php
+++ b/scp/staff.inc.php
@@ -1,7 +1,7 @@
 <?php
 /*************************************************************************
     staff.inc.php
-    
+
     File included on every staff page...handles logins (security) and file path issues.
 
     Peter Rotich <peter@osticket.com>
@@ -42,13 +42,14 @@ require_once(INCLUDE_DIR.'class.nav.php');
 require_once(INCLUDE_DIR.'class.csrf.php');
 
 /* First order of the day is see if the user is logged in and with a valid session.
-    * User must be valid staff beyond this point 
+    * User must be valid staff beyond this point
     * ONLY super admins can access the helpdesk on offline state.
 */
 
 
 if(!function_exists('staffLoginPage')) { //Ajax interface can pre-declare the function to  trap expired sessions.
     function staffLoginPage($msg) {
+        global $ost, $cfg;
         $_SESSION['_staff']['auth']['dest']=THISURI;
         $_SESSION['_staff']['auth']['msg']=$msg;
         require(SCP_DIR.'login.php');
@@ -59,7 +60,15 @@ if(!function_exists('staffLoginPage')) { //Ajax interface can pre-declare the fu
 $thisstaff = new StaffSession($_SESSION['_staff']['userID']); //Set staff object.
 //1) is the user Logged in for real && is staff.
 if(!$thisstaff || !is_object($thisstaff) || !$thisstaff->getId() || !$thisstaff->isValid()){
-    $msg=(!$thisstaff || !$thisstaff->isValid())?'Authentication Required':'Session timed out due to inactivity';
+    if (isset($_SESSION['_staff']['auth']['msg'])) {
+        $msg = $_SESSION['_staff']['auth']['msg'];
+        unset($_SESSION['_staff']['auth']['msg']);
+    }
+    elseif ($thisstaff && !$thisstaff->isValid())
+        $msg = 'Session timed out due to inactivity';
+    else
+        $msg = 'Authentication Required';
+
     staffLoginPage($msg);
     exit;
 }
@@ -88,7 +97,7 @@ if ($_POST  && !$ost->checkCSRFToken()) {
     exit;
 }
 
-//Add token to the header - used on ajax calls [DO NOT CHANGE THE NAME] 
+//Add token to the header - used on ajax calls [DO NOT CHANGE THE NAME]
 $ost->addExtraHeader('<meta name="csrf_token" content="'.$ost->getCSRFToken().'" />');
 
 /******* SET STAFF DEFAULTS **********/