diff --git a/WHATSNEW.md b/WHATSNEW.md
index 653f8a5c0462d8d7630c5506b4b4099a29fe27c1..5034732f4c55257eab64844adb679498e7d80c05 100644
--- a/WHATSNEW.md
+++ b/WHATSNEW.md
@@ -1,3 +1,38 @@
+osTicket v1.9.8
+===============
+### Enhancements
+  * Update user information for existing users when importing CSV (#1993)
+  * Agent names are consistently formatted and sorted throughout the system (#1972)
+  * Memcache session backend support. (See `include/ost-sampleconfig.php`) (#2031)
+  * Email domain validation includes DNS record verification (#2042)
+  * Make ticket queue selection sticky (aa2dc85)
+
+### Improvements
+  * Fix incorrect mapping of ISO charsets to ISO-8859-1, thanks @nerull7
+  * Fix unnecessary drop of ticket CDATA table because of update to deleted
+    field (#1932)
+  * Fix inability to create or update organization custom data (#1942)
+  * Fix inability to update some fields of user custom data (#1942)
+  * Fix filtering user custom data for email tickets (#1943)
+  * Fix missing email headers resulting in incorrectly threaded emails when
+    delivered (#1947)
+  * Cleanup file data when removing custom file uploads (#1942)
+  * Fix crash when exporting PDF and PHAR extension is not enabled
+  * Fix crash processing some TNEF documents (89f3ed7, #1956)
+  * Fix handling of GBK charset when gb2312 is advertised (#2000)
+  * Fix link to client ticket listing when logged in, thanks @neewy (#1952)
+  * Disambiguate staff and collaborators when processing a some emails (#1983)
+  * Fix several i18n phrase and layout issues (#1958, #1962, #2039)
+  * Improve detection of some bounce notices with alternative content (#1994)
+  * Fix image URL rewrite when pasting existing images, from a KB article for
+    instance (#1960)
+  * Preserve internal note formatting on new ticket by staff if HTML is
+    disabled (#2001)
+  * Touch organization `updated` timestamp on custom data update (#2007)
+  * Fix deployment on Windows® platforms, thanks @yadimon (#2033)
+  * Fix upgrade crash if retrying an old, failed upgrade from v1.6 (#1995)
+  * Fix corruption of some html content (9ae01bf)
+
 osTicket v1.9.7
 ===============
 ### Enhancements
diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php
index c2c90a3a78510b85886801992bcaf6f43af2321d..ca2b3f2c8fa2a649f5b9cfa2e55a786737339f38 100644
--- a/include/class.dynamic_forms.php
+++ b/include/class.dynamic_forms.php
@@ -1219,7 +1219,13 @@ class DynamicFormEntry extends VerySimpleModel {
             // Set the entry ID here so that $field->getClean() can use the
             // entry-id if necessary
             $a->entry = $this;
-            $val = $field->to_database($field->getClean());
+            try {
+                $val = $field->to_database($field->getClean());
+            }
+            catch (FieldUnchanged $e) {
+                // Don't update the answer.
+                continue;
+            }
             if (is_array($val)) {
                 $a->set('value', $val[0]);
                 $a->set('value_id', $val[1]);
diff --git a/include/class.format.php b/include/class.format.php
index 52b2bcff56f6c1612b9b0dd100c30c54ec4af3dd..06a07f5065e5ad5bcc5357833817eb9d37544fdd 100644
--- a/include/class.format.php
+++ b/include/class.format.php
@@ -212,7 +212,7 @@ class Format {
             $html);
         $config = array(
             'safe' => 1, //Exclude applet, embed, iframe, object and script tags.
-            'balance' => 1, //balance and close unclosed tags.
+            'balance' => 0, // No balance — corrupts poorly formatted Outlook html
             'comment' => 1, //Remove html comments (OUTLOOK LOVE THEM)
             'tidy' => -1,
             'deny_attribute' => 'id',
diff --git a/include/class.forms.php b/include/class.forms.php
index 75acdbd92a89fca54b6c751fad420b7384f0d32f..9b8de17b2bee8cf3f49853c3823cd36a1adcaa63 100644
--- a/include/class.forms.php
+++ b/include/class.forms.php
@@ -1027,7 +1027,7 @@ class TextboxField extends FormField {
         $config = $this->getConfiguration();
         $validators = array(
             '' =>       null,
-            'email' =>  array(array('Validator', 'is_email'),
+            'email' =>  array(array('Validator', 'is_valid_email'),
                 __('Enter a valid email address')),
             'phone' =>  array(array('Validator', 'is_phone'),
                 __('Enter a valid phone number')),
@@ -1061,12 +1061,20 @@ class TextboxField extends FormField {
 class PasswordField extends TextboxField {
     static $widget = 'PasswordWidget';
 
+    function parse($value) {
+        // Don't trim the value
+        return $value;
+    }
+
     function to_database($value) {
-        return Crypto::encrypt($value, SECRET_SALT, $this->getFormName());
+        // If not set in UI, don't save the empty value
+        if (!$value)
+            throw new FieldUnchanged();
+        return Crypto::encrypt($value, SECRET_SALT, 'pwfield');
     }
 
     function to_php($value) {
-        return Crypto::decrypt($value, SECRET_SALT, $this->getFormName());
+        return Crypto::decrypt($value, SECRET_SALT, 'pwfield');
     }
 }
 
@@ -2549,8 +2557,13 @@ class Widget {
 class TextboxWidget extends Widget {
     static $input_type = 'text';
 
-    function render($options=array()) {
+    function render($options=array(), $extraConfig=false) {
         $config = $this->field->getConfiguration();
+        if (is_array($extraConfig)) {
+            foreach ($extraConfig as $k=>$v)
+                if (!isset($config[$k]) || !$config[$k])
+                    $config[$k] = $v;
+        }
         if (isset($config['size']))
             $size = "size=\"{$config['size']}\"";
         if (isset($config['length']) && $config['length'])
@@ -2601,12 +2614,19 @@ class TextboxSelectionWidget extends TextboxWidget {
 class PasswordWidget extends TextboxWidget {
     static $input_type = 'password';
 
+    function render($mode=false, $extra=false) {
+        $extra = array();
+        if ($this->field->value) {
+            $extra['placeholder'] = '••••••••••••';
+        }
+        return parent::render($mode, $extra);
+    }
+
     function parseValue() {
+        parent::parseValue();
         // Show empty box unless failed POST
-        if ($_SERVER['REQUEST_METHOD'] == 'POST'
-                && $this->field->getForm()->isValid())
-            parent::parseValue();
-        else
+        if ($_SERVER['REQUEST_METHOD'] != 'POST'
+                || $this->field->getForm()->isValid())
             $this->value = '';
     }
 }
@@ -3445,5 +3465,11 @@ class TransferForm extends Form {
     }
 }
 
-
+/**
+ * FieldUnchanged
+ *
+ * Thrown in the to_database() method to indicate the value should not be
+ * saved in the database (it wasn't changed in the request)
+ */
+class FieldUnchanged extends Exception {}
 ?>
diff --git a/include/class.organization.php b/include/class.organization.php
index cc011495fa3e13114ffd8af3e2863946ca86d3f8..e79f18af0316abbc6d2c971a938d2da44c987608 100644
--- a/include/class.organization.php
+++ b/include/class.organization.php
@@ -374,7 +374,8 @@ implements TemplateVariable {
                 $this->save();
             }
             $entry->setSource($vars);
-            $entry->save();
+            if ($entry->save())
+                $this->updated = SqlFunction::NOW();
         }
 
         // Set flags
diff --git a/include/class.ostsession.php b/include/class.ostsession.php
index b300f16ec7a826443e3e1fb52530a7c88bd6486b..d93de839a9770e422dcde63205060af27ff95fa7 100644
--- a/include/class.ostsession.php
+++ b/include/class.ostsession.php
@@ -15,11 +15,17 @@
 **********************************************************************/
 
 class osTicketSession {
+    static $backends = array(
+        'db'        => 'DbSessionBackend',
+        'memcache'  => 'MemcacheSessionBackend',
+        'system'    => 'FallbackSessionBackend',
+    );
 
     var $ttl = SESSION_TTL;
     var $data = '';
     var $data_hash = '';
     var $id = '';
+    var $backend;
 
     function osTicketSession($ttl=0){
         $this->ttl = $ttl ?: ini_get('session.gc_maxlifetime') ?: SESSION_TTL;
@@ -49,24 +55,41 @@ class osTicketSession {
         session_set_cookie_params($ttl, ROOT_PATH, $domain,
             osTicket::is_https());
 
-        //Set handlers.
-        session_set_save_handler(
-            array(&$this, 'open'),
-            array(&$this, 'close'),
-            array(&$this, 'read'),
-            array(&$this, 'write'),
-            array(&$this, 'destroy'),
-            array(&$this, 'gc')
-        );
-
-        //Start the session.
+        if (!defined('SESSION_BACKEND'))
+            define('SESSION_BACKEND', 'db');
+
+        try {
+            $bk = SESSION_BACKEND;
+            if (!class_exists(self::$backends[$bk]))
+                $bk = 'db';
+            $this->backend = new self::$backends[$bk]($this->ttl);
+        }
+        catch (Exception $x) {
+            // Use the database for sessions
+            trigger_error($x->getMessage(), E_USER_WARNING);
+            $this->backend = new self::$backends['db']($this->ttl);
+        }
+
+        if ($this->backend instanceof SessionBackend) {
+            // Set handlers.
+            session_set_save_handler(
+                array($this->backend, 'open'),
+                array($this->backend, 'close'),
+                array($this->backend, 'read'),
+                array($this->backend, 'write'),
+                array($this->backend, 'destroy'),
+                array($this->backend, 'gc')
+            );
+        }
+
+        // Start the session.
         session_start();
     }
 
     function regenerate_id(){
         $oldId = session_id();
         session_regenerate_id();
-        $this->destroy($oldId);
+        $this->backend->destroy($oldId);
     }
 
     static function destroyCookie() {
@@ -86,14 +109,57 @@ class osTicketSession {
             ini_get('session.cookie_httponly'));
     }
 
-    function open($save_path, $session_name){
-        return (true);
+    /* helper functions */
+
+    function get_online_users($sec=0){
+        $sql='SELECT user_id FROM '.SESSION_TABLE.' WHERE user_id>0 AND session_expire>NOW()';
+        if($sec)
+            $sql.=" AND TIME_TO_SEC(TIMEDIFF(NOW(),session_updated))<$sec";
+
+        $users=array();
+        if(($res=db_query($sql)) && db_num_rows($res)) {
+            while(list($uid)=db_fetch_row($res))
+                $users[] = $uid;
+        }
+
+        return $users;
+    }
+
+    /* ---------- static function ---------- */
+    static function start($ttl=0) {
+        return new static($ttl);
+    }
+}
+
+abstract class SessionBackend {
+    var $isnew = false;
+    var $ttl;
+
+    function __construct($ttl) {
+        $this->ttl = $ttl;
     }
 
-    function close(){
-        return (true);
+    function open($save_path, $session_name) {
+        return true;
     }
 
+    function close() {
+        return true;
+    }
+
+    function getTTL() {
+        return $this->ttl;
+    }
+
+    abstract function read($id);
+    abstract function write($id, $data);
+    abstract function destroy($id);
+    abstract function gc($maxlife);
+}
+
+class DbSessionBackend
+extends SessionBackend {
+
     function read($id){
         $this->isnew = false;
         if (!$this->data || $this->id != $id) {
@@ -146,30 +212,100 @@ class osTicketSession {
         $sql='DELETE FROM '.SESSION_TABLE.' WHERE session_expire<NOW()';
         db_query($sql);
     }
+}
 
-    /* helper functions */
+class MemcacheSessionBackend
+extends SessionBackend {
+    var $memcache;
+    var $servers = array();
 
-    function getTTL(){
-        return $this->ttl;
+    function __construct($ttl) {
+        parent::__construct($ttl);
+
+        if (!extension_loaded('memcache'))
+            throw new Exception('Memcached extension is missing');
+        if (!defined('MEMCACHE_SERVERS'))
+            throw new Exception('MEMCACHE_SERVERS must be defined');
+
+        $servers = explode(',', MEMCACHE_SERVERS);
+        $this->memcache = new Memcache();
+
+        foreach ($servers as $S) {
+            @list($host, $port) = explode(':', $S);
+            if (strpos($host, '/') !== false)
+                // Use port '0' for unix sockets
+                $port = 0;
+            else
+                $port = $port ?: ini_get('memcache.default_port') ?: 11211;
+            $this->servers[] = array(trim($host), (int) trim($port));
+            // FIXME: Crash or warn if invalid $host or $port
+        }
     }
 
-    function get_online_users($sec=0){
-        $sql='SELECT user_id FROM '.SESSION_TABLE.' WHERE user_id>0 AND session_expire>NOW()';
-        if($sec)
-            $sql.=" AND TIME_TO_SEC(TIMEDIFF(NOW(),session_updated))<$sec";
+    function getKey($id) {
+        return sha1($id.SECRET_SALT);
+    }
 
-        $users=array();
-        if(($res=db_query($sql)) && db_num_rows($res)) {
-            while(list($uid)=db_fetch_row($res))
-                $users[] = $uid;
+    function read($id) {
+        $key = $this->getKey($id);
+
+        // Try distributed read first
+        foreach ($this->servers as $S) {
+            list($host, $port) = $S;
+            $this->memcache->addServer($host, $port);
+        }
+        $data = $this->memcache->get($key);
+
+        // Read from other servers on failure
+        if ($data === false && count($this->servers) > 1) {
+            foreach ($this->servers as $S) {
+                list($host, $port) = $S;
+                $this->memcache->pconnect($host, $port);
+                if ($data = $this->memcache->get($key))
+                    break;
+            }
         }
 
-        return $users;
+        // No session data on record -- new session
+        $this->isnew = $data === false;
+
+        return $data;
     }
 
-    /* ---------- static function ---------- */
-    function start($ttl=0) {
-        return New osTicketSession($ttl);
+    function write($id, $data) {
+        if (defined('DISABLE_SESSION') && $this->isnew)
+            return;
+
+        $key = $this->getKey($id);
+        foreach ($this->servers as $S) {
+            list($host, $port) = $S;
+            $this->memcache->pconnect($host, $port);
+            if (!$this->memcache->replace($key, $data, 0, $this->getTTL()));
+                $this->memcache->set($key, $data, 0, $this->getTTL());
+        }
+    }
+
+    function destroy($id) {
+        $key = $this->getKey($id);
+        foreach ($this->servers as $S) {
+            list($host, $port) = $S;
+            $this->memcache->pconnect($host, $port);
+            $this->memcache->replace($key, '', 0, 1);
+            $this->memcache->delete($key, 0);
+        }
+    }
+
+    function gc($maxlife) {
+        // Memcache does this automatically
     }
 }
+
+class FallbackSessionBackend {
+    // Use default PHP settings, with some edits for best experience
+    function __construct() {
+        // FIXME: Consider extra possible security tweaks such as adjusting
+        // the session.save_path
+    }
+}
+
 ?>
diff --git a/include/class.plugin.php b/include/class.plugin.php
index c71d86524df95d44be3ebd3f598029bc17eb7074..edb22710c52cab3755de2f5c40fd651e42bf9e08 100644
--- a/include/class.plugin.php
+++ b/include/class.plugin.php
@@ -81,7 +81,13 @@ class PluginConfig extends Config {
             $dbready = array();
             foreach ($config as $name => $val) {
                 $field = $f->getField($name);
-                $dbready[$name] = $field->to_database($val);
+                try {
+                    $dbready[$name] = $field->to_database($val);
+                }
+                catch (FieldUnchanged $e) {
+                    // Don't save the field value
+                    continue;
+                }
             }
             if ($this->updateAll($dbready)) {
                 if (!$msg)
diff --git a/include/class.staff.php b/include/class.staff.php
index b295a15e194272d0e23ca1284f7bf4eec60ef4c5..8362656a1370ba407e864baa49b0510cc995b25d 100644
--- a/include/class.staff.php
+++ b/include/class.staff.php
@@ -549,7 +549,7 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
         if(!$vars['lastname'])
             $errors['lastname']=__('Last name is required');
 
-        if(!$vars['email'] || !Validator::is_email($vars['email']))
+        if(!$vars['email'] || !Validator::is_valid_email($vars['email']))
             $errors['email']=__('Valid email is required');
         elseif(Email::getIdByEmail($vars['email']))
             $errors['email']=__('Already in-use as system email');
@@ -761,7 +761,7 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
         unset($_SESSION['_staff']['reset-token']);
     }
 
-    function sendResetEmail($template='pwreset-staff') {
+    function sendResetEmail($template='pwreset-staff', $log=true) {
         global $ost, $cfg;
 
         $content = Page::lookupByType($template);
@@ -785,7 +785,7 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
         if (!($email = $cfg->getAlertEmail()))
             $email = $cfg->getDefaultEmail();
 
-        $info = array('email' => $email, 'vars' => &$vars, 'log'=>true);
+        $info = array('email' => $email, 'vars' => &$vars, 'log'=>$log);
         Signal::send('auth.pwreset.email', $this, $info);
 
         if ($info['log'])
@@ -927,7 +927,7 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
 
         if ($this->save() && $this->updateTeams($vars['teams'])) {
             if ($vars['welcome_email'])
-                $this->sendResetEmail('registration-staff');
+                $this->sendResetEmail('registration-staff', false);
             return true;
         }
 
diff --git a/include/class.ticket.php b/include/class.ticket.php
index cbb51b4143eca41bdfdf9570835237d5e51db4be..d114d1664665c5444e6ddd3c41c692f88f25b83e 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -1491,7 +1491,7 @@ implements RestrictedAccess, Threadable, TemplateVariable {
             // Skip all the other recipients of the message
             foreach ($entry->getAllEmailRecipients() as $R) {
                 foreach ($recipients as $R2) {
-                    if ($R2->getEmail() == ($R->mailbox.'@'.$R->hostname)) {
+                    if (0 === strcasecmp($R2->getEmail(), $R->mailbox.'@'.$R->host)) {
                         $skip[$R2->getUserId()] = true;
                         break;
                     }
diff --git a/include/class.validator.php b/include/class.validator.php
index c14f910743f85b72be0ab65b97af64c48354ffb3..fcb350126a22191da7572719617d42a0cfab7be8 100644
--- a/include/class.validator.php
+++ b/include/class.validator.php
@@ -140,7 +140,7 @@ class Validator {
 
     /*** Functions below can be called directly without class instance.
          Validator::func(var..);  (nolint) ***/
-    function is_email($email, $list=false) {
+    function is_email($email, $list=false, $verify=false) {
         require_once PEAR_DIR . 'Mail/RFC822.php';
         require_once PEAR_DIR . 'PEAR.php';
         if (!($mails = Mail_RFC822::parseAddressList($email)) || PEAR::isError($mails))
@@ -156,8 +156,16 @@ class Validator {
                 return false;
         }
 
+        if ($verify && !checkdnsrr($m->host, 'MX'))
+            return false;
+
         return true;
     }
+
+    function is_valid_email($email) {
+        return self::is_email($email, false, true);
+    }
+
     function is_phone($phone) {
         /* We're not really validating the phone number but just making sure it doesn't contain illegal chars and of acceptable len */
         $stripped=preg_replace("(\(|\)|\-|\.|\+|[  ]+)","",$phone);
diff --git a/include/ost-sampleconfig.php b/include/ost-sampleconfig.php
index a4624d1e706ce75f36c2e23a6ac1c0ec3b916eb3..0b26400698271c1d89ab46f55a209d501b5b1dae 100644
--- a/include/ost-sampleconfig.php
+++ b/include/ost-sampleconfig.php
@@ -106,4 +106,24 @@ define('TABLE_PREFIX','%CONFIG-PREFIX');
 # ROOT_PATH *must* end with a forward-slash!
 
 # define('ROOT_PATH', '/support/');
+
+#
+# Session Storage Options
+# ---------------------------------------------------
+# Option: SESSION_BACKEND (default: db)
+#
+# osTicket supports Memcache as a session storage backend if the `memcache`
+# pecl extesion is installed. This also requires MEMCACHE_SERVERS to be
+# configured as well.
+#
+# MEMCACHE_SERVERS can be defined as a comma-separated list of host:port
+# specifications. If more than one server is listed, the session is written
+# to all of the servers for redundancy.
+#
+# Values: 'db' (default)
+#         'memcache' (Use Memcache servers)
+#         'system' (use PHP settings as configured (not recommended!))
+#
+# define('SESSION_BACKEND', 'memcache');
+# define('MEMCACHE_SERVERS', 'server1:11211,server2:11211');
 ?>
diff --git a/include/staff/profile.inc.php b/include/staff/profile.inc.php
index 5f6288db738b1cd3cd11fcbf7a1e389e70b2bcd6..5ef77f8707d4573e7aca06e4e22cefcfef5909d7 100644
--- a/include/staff/profile.inc.php
+++ b/include/staff/profile.inc.php
@@ -189,7 +189,7 @@ $info['id']=$staff->getId();
                   }
                   ?>
                 </select>
-                <em><?php echo __('(This can be selectected when replying to a ticket)');?></em>
+                <em><?php echo __('(This can be selected when replying to a ticket)');?></em>
                 &nbsp;<span class="error">&nbsp;<?php echo $errors['default_signature_type']; ?></span>
             </td>
         </tr>
diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php
index 78844c8aa7a0ee3a6a2e3f3c457be3b514bd28cf..35fb34acacdc3334b36e1ffd7381ad9d82b72bd3 100644
--- a/include/staff/ticket-view.inc.php
+++ b/include/staff/ticket-view.inc.php
@@ -46,6 +46,8 @@ if (!$errors['err']) {
                 $lock->getStaffName());
     elseif (($emailBanned=Banlist::isBanned($ticket->getEmail())))
         $errors['err'] = __('Email is in banlist! Must be removed before any reply/response');
+    elseif (!Validator::is_valid_email($ticket->getEmail()))
+        $errors['err'] = __('EndUser email address is not valid! Consider updating it before responding');
 }
 
 $unbannable=($emailBanned) ? BanList::includes($ticket->getEmail()) : false;
diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php
index 7a606522abd4dcec007a97bd5601a6cd654059d3..612f76f1759a62230fc2fc9a87410b7b9ed17ed7 100644
--- a/include/staff/tickets.inc.php
+++ b/include/staff/tickets.inc.php
@@ -31,6 +31,9 @@ $sort_options = array(
 $use_subquery = true;
 
 $queue_name = strtolower($_GET['status'] ?: $_GET['a']); //Status is overloaded
+// Stash current queue view
+$_SESSION['::Q'] = $queue_name;
+
 switch ($queue_name) {
 case 'closed':
     $status='closed';
diff --git a/include/staff/user-view.inc.php b/include/staff/user-view.inc.php
index 07085d4b9a44fe0440217bce5d68915d9579d792..29c573a608649af3242abe7bee5a972615eac07a 100644
--- a/include/staff/user-view.inc.php
+++ b/include/staff/user-view.inc.php
@@ -212,6 +212,7 @@ $(function() {
                 window.location.href = 'users.php';
             else
                 window.location.href = window.location.href;
+            return false;
          }, {
             onshow: function() { $('#user-search').focus(); }
          });
diff --git a/include/upgrader/streams/core/435c62c3-2e7531a2.patch.sql b/include/upgrader/streams/core/435c62c3-2e7531a2.patch.sql
index fd75833c30ca760b971aad04f7c0ef47bf936426..18d54b03313828b73b5344ef9816f175757c57eb 100644
--- a/include/upgrader/streams/core/435c62c3-2e7531a2.patch.sql
+++ b/include/upgrader/streams/core/435c62c3-2e7531a2.patch.sql
@@ -5,6 +5,7 @@
  */
 
 -- Group department access table
+DROP TABLE IF EXISTS `%TABLE_PREFIX%group_dept_access`;
 CREATE TABLE `%TABLE_PREFIX%group_dept_access` (
   `group_id` int(10) unsigned NOT NULL default '0',
   `dept_id` int(10) unsigned NOT NULL default '0',
diff --git a/include/upgrader/streams/core/c00511c7-7be60a84.patch.sql b/include/upgrader/streams/core/c00511c7-7be60a84.patch.sql
index 97c2bf97b45d6e5fa1368e8011d05e07d48c545e..96d97a041e64677848a90a0b67c116cfdfa47422 100644
--- a/include/upgrader/streams/core/c00511c7-7be60a84.patch.sql
+++ b/include/upgrader/streams/core/c00511c7-7be60a84.patch.sql
@@ -96,6 +96,7 @@ INSERT INTO `%TABLE_PREFIX%sla` (`isactive`, `enable_priority_escalation`,
     VALUES (1, 1, 0, 48, 'Default SLA', NULL, NOW(), NOW());
 
 -- Create a TEAM table
+DROP TABLE IF EXISTS `%TABLE_PREFIX%team`;
 CREATE TABLE IF NOT EXISTS `%TABLE_PREFIX%team` (
     `team_id` int(10) unsigned NOT NULL auto_increment,
     `lead_id` int(10) unsigned NOT NULL default '0',
@@ -282,6 +283,7 @@ ALTER TABLE `%TABLE_PREFIX%kb_premade`
   ADD `notes` TEXT NOT NULL AFTER `response`,
   DROP INDEX `title`;
 
+DROP TABLE IF EXISTS `%TABLE_PREFIX%canned_response`;
 ALTER TABLE `%TABLE_PREFIX%kb_premade` RENAME TO `%TABLE_PREFIX%canned_response`;
 
 DROP TABLE IF EXISTS `%TABLE_PREFIX%faq_category`;
diff --git a/include/upgrader/streams/core/d0e37dca-1da1bcba.patch.sql b/include/upgrader/streams/core/d0e37dca-1da1bcba.patch.sql
index 3d3bda68e2fd49293e1a73545506416c7a1cc126..2519a5bd32330f5df7df5b2389fd7b2d7aa99088 100644
--- a/include/upgrader/streams/core/d0e37dca-1da1bcba.patch.sql
+++ b/include/upgrader/streams/core/d0e37dca-1da1bcba.patch.sql
@@ -3,16 +3,18 @@
  * @signature 1da1bcbafcedc65efef58f142a48ac91
  *
  *  Upgrade from 1.6 RC3 + filters
- *  
+ *
  */
 
+DROP TABLE IF EXISTS `%TABLE_PREFIX%filter`;
 RENAME TABLE  `%TABLE_PREFIX%email_filter` TO  `%TABLE_PREFIX%filter`;
 
+DROP TABLE IF EXISTS `%TABLE_PREFIX%filter_rule`;
 RENAME TABLE  `%TABLE_PREFIX%email_filter_rule` TO  `%TABLE_PREFIX%filter_rule`;
 
 ALTER TABLE  `%TABLE_PREFIX%filter` CHANGE  `reject_email`  `reject_ticket` TINYINT( 1 ) UNSIGNED NOT NULL DEFAULT  '0';
 
-ALTER TABLE  `%TABLE_PREFIX%filter` 
+ALTER TABLE  `%TABLE_PREFIX%filter`
     ADD  `target` ENUM(  'Any',  'Web',  'Email',  'API' ) NOT NULL DEFAULT  'Any' AFTER  `sla_id` ,
     ADD INDEX (  `target` );
 
diff --git a/scp/autocron.php b/scp/autocron.php
index eabc67151374c7d28eaf4895efe409f21652e5d9..d6ff919b359791e87caedd2f9b2c04dac7a397c0 100644
--- a/scp/autocron.php
+++ b/scp/autocron.php
@@ -39,14 +39,25 @@ ob_start(); //Keep the image output clean. Hide our dirt.
 $sec=time()-$_SESSION['lastcroncall'];
 $caller = $thisstaff->getUserName();
 
-if($sec>180 && $ost && !$ost->isUpgradePending()): //user can call cron once every 3 minutes.
+// Agent can call cron once every 3 minutes.
+if ($sec < 180 || !$ost || $ost->isUpgradePending())
+    ob_end_clean();
+
 require_once(INCLUDE_DIR.'class.cron.php');
 
-$thisstaff = null; //Clear staff obj to avoid false credit internal notes & auto-assignment
-Cron::TicketMonitor(); //Age tickets: We're going to age tickets regardless of cron settings.
+// Clear staff obj to avoid false credit internal notes & auto-assignment
+$thisstaff = null;
+
+// Release the session to prevent locking a future request while this is
+// running
+$_SESSION['lastcroncall'] = time();
+session_write_close();
+
+// Age tickets: We're going to age tickets regardless of cron settings.
+Cron::TicketMonitor();
 
-// Run file purging about every 30 minutes
-if (mt_rand(1, 9) == 4)
+// Run file purging about every 20 cron runs (1h40 on a five minute cron)
+if (mt_rand(1, 20) == 4)
     Cron::CleanOrphanedFiles();
 
 if($cfg && $cfg->isAutoCronEnabled()) { //ONLY fetch tickets if autocron is enabled!
@@ -57,7 +68,5 @@ if($cfg && $cfg->isAutoCronEnabled()) { //ONLY fetch tickets if autocron is enab
 $data = array('autocron'=>true);
 Signal::send('cron', $data);
 
-$_SESSION['lastcroncall']=time();
-endif;
 ob_end_clean();
 ?>
diff --git a/scp/emailtest.php b/scp/emailtest.php
index 1246dff088799736f62941acf470f2901b335e1c..9e15baf36b1ccb10d0cd3cc93e87b94edf9d88c3 100644
--- a/scp/emailtest.php
+++ b/scp/emailtest.php
@@ -25,8 +25,8 @@ if($_POST){
     if(!$_POST['email_id'] || !($email=Email::lookup($_POST['email_id'])))
         $errors['email_id']=__('Select from email address');
 
-    if(!$_POST['email'] || !Validator::is_email($_POST['email']))
-        $errors['email']=__('To email address required');
+    if(!$_POST['email'] || !Validator::is_valid_email($_POST['email']))
+        $errors['email']=__('Valid recipient email address required');
 
     if(!$_POST['subj'])
         $errors['subj']=__('Subject required');
@@ -48,11 +48,12 @@ if($_POST){
         $errors['err']=__('Error sending email - try again.');
     }
 }
-$info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
 $nav->setTabActive('emails');
 $ost->addExtraHeader('<meta name="tip-namespace" content="emails.diagnostic" />',
     "$('#content').data('tipNamespace', '".$tip_namespace."');");
 require(STAFFINC_DIR.'header.inc.php');
+
+$info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
 ?>
 <form action="emailtest.php" method="post" id="save">
  <?php csrf_token(); ?>
diff --git a/scp/tickets.php b/scp/tickets.php
index c4a9591e25049e1a9c7292d089ce5a8c2a695ee4..4f0dcec4e3642c8628ed5bc0f743926d746becc2 100644
--- a/scp/tickets.php
+++ b/scp/tickets.php
@@ -36,9 +36,12 @@ if($_REQUEST['id']) {
 }
 
 //Lookup user if id is available.
-if ($_REQUEST['uid'])
+if ($_REQUEST['uid']) {
     $user = User::lookup($_REQUEST['uid']);
-
+}
+elseif (!isset($_GET['status']) && isset($_SESSION['::Q'])) {
+    $_GET['status'] = $_REQUEST['status'] = $_SESSION['::Q'];
+}
 // Configure form for file uploads
 $response_form = new SimpleForm(array(
     'attachments' => new FileUploadField(array('id'=>'attach',
diff --git a/setup/cli/modules/deploy.php b/setup/cli/modules/deploy.php
index f402e99e2fb355159983473232f6e6f8b710e8b8..7d0e87ecc21b86cb596e2b20067028a9f8ba7171 100644
--- a/setup/cli/modules/deploy.php
+++ b/setup/cli/modules/deploy.php
@@ -151,11 +151,12 @@ class Deployment extends Unpacker {
 
         # Locate the upload folder
         $root = $this->find_root_folder();
+        $rootPattern = str_replace("\\","\\\\", $root); //need for windows case
 
-        $exclusions = array("$root/include", "$root/.git*",
+        $exclusions = array("$rootPattern/include", "$rootPattern/.git*",
             "*.sw[a-z]","*.md", "*.txt");
         if (!$options['setup'])
-            $exclusions[] = "$root/setup";
+            $exclusions[] = "$rootPattern/setup";
 
         # Unpack everything but the include/ folder
         $this->unpackage("$root/{,.}*", $this->destination, -1,
diff --git a/setup/install.php b/setup/install.php
index 4a348cf632d24b3c7c62a406d80fc1bf86ca898c..1420f2dfab36db7ee48d874b9e127308b45dd32d 100644
--- a/setup/install.php
+++ b/setup/install.php
@@ -65,7 +65,7 @@ if($_POST && $_POST['s']) {
 
             if(!$_POST['email'])
                 $errors['email'] = __('Required');
-            elseif(!Validator::is_email($_POST['email']))
+            elseif(!Validator::is_valid_email($_POST['email']))
                 $errors['email'] = __('Invalid');
 
             if(!$_POST['alerts'] && !$_POST['news'])
diff --git a/setup/test/tests/stubs.php b/setup/test/tests/stubs.php
index 83a6cdd297a4688f96528aeee46a9360fa0ae9f1..620f763a55090b6bb423e2b36a6b378f44255a8e 100644
--- a/setup/test/tests/stubs.php
+++ b/setup/test/tests/stubs.php
@@ -163,4 +163,12 @@ class NumberFormatter {
 class Aws_Route53_Client {
     function changeResourceRecordSets() {}
 }
+
+class Memcache {
+    function addServer() {}
+    function pconnect() {}
+    function replace() {}
+    function set() {}
+    function get() {}
+}
 ?>