diff --git a/README.md b/README.md
index 34981849af1f420236229466374cba706e0351e3..4d97925949c0c7e4fff49c52ab5c8b445572f171 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,25 @@ Follow the usual install instructions (beginning from Manual Installation
 above), except, don't delete the setup/ folder. For this reason, such an
 installation is not recommended for a public-facing support system.
 
+Upgrading
+---------
+osTicket supports upgrading from 1.6-rc1 and later versions. As with any
+upgrade, strongly consider a backup of your attachment files, database, and
+osTicket codebase before embarking on an upgrade.
+
+To trigger the update process, fetch the osTicket-1.7 tarball from either
+the osTicket [github](http://github.com/osTicket/osTicket-1.7) page or from
+the osTicket website. Extract the tarball into the folder of you osTicket
+codebase. This can also be accomplished with the zip file, and a FTP client
+can of course be used to upload the new source code to your server. 
+
+Any way you choose your adventure, when you have your codebase upgraded to
+osTicket-1.7, visit the /scp page of you ticketing system. The upgrader will
+be presented and will walk you through the rest of the process. (The couple
+clicks needed to go through the process are pretty boring to describe).
+
+View the UPGRADING.txt file for other todo items to complete your upgrade.
+
 Help
 ----
 Visit the [wiki](http://osticket.com/wiki/Home) or the
diff --git a/include/class.file.php b/include/class.file.php
index b9bfbe1b7d2975ebdcc467bfb826ed35b8f1ec75..891a8b5bfb851415c1ab3cbe804284ae5217f229 100644
--- a/include/class.file.php
+++ b/include/class.file.php
@@ -172,10 +172,18 @@ class AttachmentFile {
             .',type='.db_input($file['type'])
             .',size='.db_input($file['size'])
             .',name='.db_input($file['name'])
-            .',hash='.db_input($file['hash'])
-            .',filedata='.db_input($file['data']);
+            .',hash='.db_input($file['hash']);
 
-        return db_query($sql)?db_insert_id():0;
+        if (!(db_query($sql) && ($id=db_insert_id())))
+            return false;
+
+        foreach (str_split($file['data'], 1024*100) as $chunk) {
+            if (!db_query('UPDATE '.FILE_TABLE.' SET filedata = CONCAT(filedata,'
+                    .db_input($chunk).') WHERE id='.db_input($id)))
+                # Remove partially uploaded file contents
+                return false;
+        }
+        return $id;
     }
 
     /* Static functions */
diff --git a/include/class.migrater.php b/include/class.migrater.php
index 72679537e9b59af366e69bca04a05f68f0dd9471..04f38be2af9c46c7705591e550bb993096d723d2 100644
--- a/include/class.migrater.php
+++ b/include/class.migrater.php
@@ -114,7 +114,7 @@ class AttachmentMigrater {
      */
     function do_batch($time=30, $max=0) {
 
-        if(!$this->queueAttachments() || !$this->getQueueLength())
+        if(!$this->queueAttachments($max) || !$this->getQueueLength())
             return 0;
 
         $count = 0;
@@ -123,7 +123,7 @@ class AttachmentMigrater {
             if($this->next() && $max && ++$count>=$max)
                 break;
 
-        return $this->queueAttachments();
+        return $this->queueAttachments($max);
 
     }
 
@@ -131,7 +131,7 @@ class AttachmentMigrater {
         return $this->skipList;
     }
 
-    function queue($fileinfo) {
+    function enqueue($fileinfo) {
         $this->queue[] = $fileinfo;
     }
 
@@ -159,8 +159,14 @@ class AttachmentMigrater {
         }
         # Get the mime/type of each file
         # XXX: Use finfo_buffer for PHP 5.3+
-        if(function_exists('mime_content_type')) //XXX: function depreciated in newer versions of PHP!!!!!
+        if(function_exists('mime_content_type')) { 
+            //XXX: function depreciated in newer versions of PHP!!!!!
             $info['type'] = mime_content_type($info['path']);
+        } elseif (function_exists('finfo_file')) { // PHP 5.3.0+
+            $finfo = finfo_open(FILEINFO_MIME_TYPE);
+            $info['type'] = finfo_file($finfo, $info['path']);
+        }
+        # TODO: Add extension-based mime-type lookup
 
         if (!($fileId = AttachmentFile::save($info))) {
             return $this->skip($info['attachId'],
@@ -194,8 +200,8 @@ class AttachmentMigrater {
 
         $sql='SELECT attach_id, file_name, file_key, Ti.created'
             .' FROM '.TICKET_ATTACHMENT_TABLE.' TA'
-            .' JOIN '.TICKET_TABLE.' Ti ON Ti.ticket_id=TA.ticket_id'
-            .' WHERE NOT file_id '; //XXX: ignore orphaned attachments?
+            .' INNER JOIN '.TICKET_TABLE.' Ti ON (Ti.ticket_id=TA.ticket_id)'
+            .' WHERE NOT file_id ';
 
         if(($skipList=$this->getSkipList()))
             $sql.= ' AND attach_id NOT IN('.implode(',', db_input($skipList)).')';
@@ -253,10 +259,10 @@ class AttachmentMigrater {
             # anyway.
             $info['size'] = @filesize($info['path']);
             # Coroutines would be nice ..
-            $this->queue($info);
+            $this->enqueue($info);
         }
 
-        return $this->getQueueLength();
+        return $this->queueAttachments($limit);
     }
 
     function skip($attachId, $error) {
diff --git a/include/class.ostsession.php b/include/class.ostsession.php
index 0bcd9449fb234c6b9a895da6b046527f7a7909a4..08cac02ce306f616ad0d6329e6a51436a78d9e7d 100644
--- a/include/class.ostsession.php
+++ b/include/class.ostsession.php
@@ -67,10 +67,13 @@ class osTicketSession {
     function write($id, $data){
         global $thisstaff;
 
+        $ttl = ($this && get_class($this) == 'osTicketSession') 
+            ? $this->getTTL() : SESSION_TTL;
+
         $sql='REPLACE INTO '.SESSION_TABLE.' SET session_updated=NOW() '.
              ',session_id='.db_input($id).
              ',session_data='.db_input($data).
-             ',session_expire=(NOW() + INTERVAL '.$this->getTTL().' SECOND)'.
+             ',session_expire=(NOW() + INTERVAL '.$ttl.' SECOND)'.
              ',user_id='.db_input($thisstaff?$thisstaff->getId():0).
              ',user_ip='.db_input($_SERVER['REMOTE_ADDR']).
              ',user_agent='.db_input($_SERVER['HTTP_USER_AGENT']);
diff --git a/include/class.upgrader.php b/include/class.upgrader.php
index 69dd760fda40c4e1bdc73454565a05e96b202aa7..de11aee3a2e6f054d334205b9538fe73be2b0907 100644
--- a/include/class.upgrader.php
+++ b/include/class.upgrader.php
@@ -181,9 +181,11 @@ class Upgrader extends SetupWizard {
 
     function doTasks() {
 
+        global $ost;
         if(!($tasks=$this->getPendingTasks()))
             return true; //Nothing to do.
 
+        $ost->logDebug('Upgrader', sprintf('There are %d pending upgrade tasks', count($tasks)));
         $start_time = Misc::micro_time();
         foreach($tasks as $k => $task) {
             //TODO: check time used vs. max execution - break if need be
@@ -238,6 +240,9 @@ class Upgrader extends SetupWizard {
             $shash = substr($phash, 9, 8);
             $_SESSION['ost_upgrader'][$shash]['tasks'] = $tasks;
             $_SESSION['ost_upgrader'][$shash]['state'] = 'upgrade';
+            
+            $ost->logDebug('Upgrader', sprintf('Found %d tasks to be executed for %s',
+                            count($tasks), $shash));
             break;
 
         }
@@ -253,13 +258,16 @@ class Upgrader extends SetupWizard {
             case 'c00511c7-7be60a84': //V1.6 ST- 1.7 * {{MD5('1.6 ST') -> c00511c7c1db65c0cfad04b4842afc57}}
                 $tasks[] = array('func' => 'migrateAttachments2DB',
                                  'desc' => 'Migrating attachments to database, it might take a while depending on the number of files.');
+                $tasks[] = array('func' => 'migrateSessionFile2DB',
+                                 'desc' => 'Transitioning to db-backed sessions');
                 break;
         }
 
         //Check IF SQL cleanup is exists. 
         $file=$this->getSQLDir().$phash.'.cleanup.sql';
         if(file_exists($file)) 
-            $tasks[] = array('func' => 'cleanup', 'desc' => 'Post-upgrade cleanup!');
+            $tasks[] = array('func' => 'cleanup', 'desc' => 'Post-upgrade cleanup!',
+                        'phash' => $phash);
 
 
         return $tasks;
@@ -267,8 +275,11 @@ class Upgrader extends SetupWizard {
 
     /************* TASKS **********************/
     function cleanup($taskId) {
+        global $ost;
+
+        $phash = $this->tasks[$taskId]['phash'];
+        $file=$this->getSQLDir().$phash.'.cleanup.sql';
 
-        $file=$this->getSQLDir().$this->getShash().'-cleanup.sql';
         if(!file_exists($file)) //No cleanup script.
             return 0;
 
@@ -276,11 +287,13 @@ class Upgrader extends SetupWizard {
         if($this->load_sql_file($file, $this->getTablePrefix(), false, true))
             return 0;
 
-        //XXX: ???
-        return false;
+        $ost->logDebug('Upgrader', sprintf("%s: Unable to process cleanup file",
+                        $phash));
+        return 0;
     }
 
     function migrateAttachments2DB($taskId) {
+        global $ost;
         
         if(!($max_time = ini_get('max_execution_time')))
             $max_time = 30; //Default to 30 sec batches.
@@ -291,5 +304,11 @@ class Upgrader extends SetupWizard {
 
         return $att_migrater->getQueueLength();
     }
+
+    function migrateSessionFile2DB($taskId) {
+        # How about 'dis for a hack?
+        osTicketSession::write(session_id(), session_encode()); 
+        return 0;
+    }
 }
 ?>
diff --git a/include/upgrader/patches/60fcbee1-f8856d56.patch.sql b/include/upgrader/patches/60fcbee1-f8856d56.patch.sql
index 2b7e48599816e88702f709f50557189b160a727e..624e88968ef3f673bea2f1a96d4b1733fbff9c8a 100644
--- a/include/upgrader/patches/60fcbee1-f8856d56.patch.sql
+++ b/include/upgrader/patches/60fcbee1-f8856d56.patch.sql
@@ -15,5 +15,47 @@ CREATE TABLE `%TABLE_PREFIX%ticket_event` (
 DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_history`;
 DROP TABLE IF EXISTS `%TABLE_PREFIX%history`;
 
+-- Transfer ticket statistics from the %ticket table (inaccurate)
+-- REOPENED
+INSERT INTO `%TABLE_PREFIX%ticket_event`
+    (`ticket_id`, `staff_id`, `team_id`, `dept_id`, `topic_id`,
+        `state`, `staff`, `timestamp`)
+    SELECT `ticket_id`, T1.`staff_id`, `team_id`, T1.`dept_id`, `topic_id`,
+        'reopened', T2.`username`, `reopened`
+    FROM `%TABLE_PREFIX%ticket` T1
+        INNER JOIN `%TABLE_PREFIX%staff` T2
+        ON (T1.`staff_id` = T2.`staff_id`)
+    WHERE `status` = 'open' and `reopened` is not null;
+
+-- CLOSED
+INSERT INTO `%TABLE_PREFIX%ticket_event`
+    (`ticket_id`, `staff_id`, `team_id`, `dept_id`, `topic_id`,
+        `state`, `staff`, `timestamp`)
+    SELECT `ticket_id`, T1.`staff_id`, `team_id`, T1.`dept_id`, `topic_id`,
+        'closed', COALESCE(T2.`username`,'unknown'), `closed`
+    FROM `%TABLE_PREFIX%ticket` T1
+        LEFT JOIN `%TABLE_PREFIX%staff` T2
+        ON (T1.`staff_id` = T2.`staff_id`)
+    WHERE `status` = 'closed' and `closed` is not null;
+
+-- OVERDUE
+INSERT INTO `%TABLE_PREFIX%ticket_event`
+    (`ticket_id`, `staff_id`, `team_id`, `dept_id`, `topic_id`,
+        `state`, `staff`, `timestamp`)
+    SELECT `ticket_id`, T1.`staff_id`, `team_id`, T1.`dept_id`, `topic_id`,
+        'overdue', 'SYSTEM', `duedate`
+    FROM `%TABLE_PREFIX%ticket` T1
+        INNER JOIN `%TABLE_PREFIX%staff` T2
+        ON (T1.`staff_id` = T2.`staff_id`)
+    WHERE `status` = 'open' and `isoverdue`;
+
+-- OPENED
+INSERT INTO `%TABLE_PREFIX%ticket_event`
+    (`ticket_id`, `staff_id`, `team_id`, `dept_id`, `topic_id`,
+        `state`, `staff`, `timestamp`)
+    SELECT `ticket_id`, 0, 0, 0, `topic_id`,
+        'created', 'SYSTEM', T1.`created`
+    FROM `%TABLE_PREFIX%ticket` T1;
+
 UPDATE `%TABLE_PREFIX%config`
     SET `schema_signature`='f8856d56e51c5cc3416389de78b54515';
diff --git a/include/upgrader/patches/7be60a84-522e5b78.patch.sql b/include/upgrader/patches/7be60a84-522e5b78.patch.sql
index acdaf7d0d3c1f8baaedd40bd68e33b01b97e72c9..a31af302d3b10908fdded3c2359b61b426c5a87d 100644
--- a/include/upgrader/patches/7be60a84-522e5b78.patch.sql
+++ b/include/upgrader/patches/7be60a84-522e5b78.patch.sql
@@ -1,9 +1,9 @@
 /**
+ * No longer necessary -- don't clobber email templates for previous
+ * osTicket administrators
+ *
  * @version v1.7-DPR1 (P1)
- */ 
-UPDATE `%TABLE_PREFIX%email_template`
-    SET `ticket_overlimit_subj` = 'Open Tickets Limit Reached'
-    WHERE `tpl_id` = 1 AND `cfg_id` = 1;
+ */
 
 UPDATE `%TABLE_PREFIX%config`
     SET `schema_signature`='522e5b783c2824c67222260ee22baa93';
diff --git a/include/upgrader/patches/c00511c7-7be60a84.cleanup.sql b/include/upgrader/patches/c00511c7-7be60a84.cleanup.sql
new file mode 100644
index 0000000000000000000000000000000000000000..01d69e2d72b732b2d7c3f618fd6e595ccb7c67f7
--- /dev/null
+++ b/include/upgrader/patches/c00511c7-7be60a84.cleanup.sql
@@ -0,0 +1,25 @@
+-- Drop columns we nolonger need - (must be at the very bottom or after session table is created)
+ALTER TABLE `%TABLE_PREFIX%config`
+    DROP COLUMN `ostversion`,
+    DROP COLUMN `timezone_offset`,
+    DROP COLUMN `api_passphrase`;
+
+-- Drop fields we no longer need in the reference table.
+ALTER TABLE `%TABLE_PREFIX%ticket_attachment`
+    DROP `file_size`,
+    DROP `file_name`,
+    DROP `file_key`,
+    DROP `updated`,
+    DROP `isdeleted`;
+
+-- Drop fields we no longer need in staff table.
+ALTER TABLE `%TABLE_PREFIX%staff`
+    DROP `append_signature`,
+    DROP `timezone_offset`;
+
+-- Drop fields we no longer need in department table.
+ALTER TABLE `%TABLE_PREFIX%department`
+    DROP `can_append_signature`;
+
+-- Banlist table has been migrated to the email_filter_rule table
+DROP TABLE `%TABLE_PREFIX%email_banlist`;
diff --git a/include/upgrader/patches/c00511c7-7be60a84.patch.sql b/include/upgrader/patches/c00511c7-7be60a84.patch.sql
index 89dbb31435482baa65fc842dcf58e3edbba40cc6..638248b976019a670c773578b564d99a3daacc6a 100644
--- a/include/upgrader/patches/c00511c7-7be60a84.patch.sql
+++ b/include/upgrader/patches/c00511c7-7be60a84.patch.sql
@@ -302,12 +302,6 @@ CREATE TABLE IF NOT EXISTS `%TABLE_PREFIX%faq_topic` (
   PRIMARY KEY  (`faq_id`,`topic_id`)
 ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
 
--- Drop columns we nolonger need - (must be at the very bottom or after session table is created)
-ALTER TABLE `%TABLE_PREFIX%config`
-    DROP COLUMN `ostversion`,
-    DROP COLUMN `timezone_offset`,
-    DROP COLUMN `api_passphrase`;
-
 
 UPDATE `%TABLE_PREFIX%config`
     SET `schema_signature`='7be60a8432e44989e782d5914ef784d2'; 
diff --git a/include/upgrader/patches/f8856d56-abe9c0cb.patch.sql b/include/upgrader/patches/f8856d56-abe9c0cb.patch.sql
index 192b2c644a80da9ac2ed00bc6974fd985358bbc3..6650754645ae363775d68ac3101f8f5584196f3a 100644
--- a/include/upgrader/patches/f8856d56-abe9c0cb.patch.sql
+++ b/include/upgrader/patches/f8856d56-abe9c0cb.patch.sql
@@ -28,6 +28,7 @@ CREATE TABLE `%TABLE_PREFIX%ticket_thread` (
   PRIMARY KEY  (`id`),
   KEY `ticket_id` (`ticket_id`),
   KEY `staff_id` (`staff_id`),
+  KEY `old_pk` (`old_pk`),
   FULLTEXT KEY `body` (`body`)
 ) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
 
@@ -57,7 +58,9 @@ INSERT INTO `%TABLE_PREFIX%ticket_thread`
 
 -- Connect responses to (new) messages
 CREATE TABLE `%TABLE_PREFIX%T_resp_links`
-    SELECT `id`, `old_pk`, `old_pid` FROM `%TABLE_PREFIX%ticket_thread`;
+    SELECT `id`, `old_pk`, `old_pid`, `thread_type`
+      FROM `%TABLE_PREFIX%ticket_thread`
+     WHERE `thread_type` = 'M';
 
 UPDATE `%TABLE_PREFIX%ticket_thread`
     SET `pid` = ( SELECT T2.`id` FROM `%TABLE_PREFIX%T_resp_links` T2
diff --git a/scp/logout.php b/scp/logout.php
index 32d62d14abb29363df9a7da033a1f0873821cbc9..f167d5a876082b0628f1e64929626444f28a3bb0 100644
--- a/scp/logout.php
+++ b/scp/logout.php
@@ -21,7 +21,6 @@ $ost->logDebug('Staff logout',
 $_SESSION['_staff']=array();
 session_unset();
 session_destroy();
-session_write_close();
 @header('Location: login.php');
 require('login.php');
 ?>
diff --git a/scp/upgrade.php b/scp/upgrade.php
index d13dc4c3392e90cb7b9e2e225b2a74b244ae002c..c441bd5b71f21addf50d9dd704e92cbaba4128b4 100644
--- a/scp/upgrade.php
+++ b/scp/upgrade.php
@@ -27,9 +27,10 @@ if($_POST && $_POST['s'] && !$upgrader->isAborted()) {
                 $errors['err']=' Nothing to do! System already upgraded to the current version';
             elseif(!$upgrader->isUpgradable())
                 $errors['err']='The upgrader does NOT support upgrading from the current vesion!';
-            elseif($upgrader->check_prereq())
+            elseif($upgrader->check_prereq()) {
                 $upgrader->setState('upgrade');
-            else
+                $_SESSION['ost_upgrader'] = null;
+            } else
                 $errors['prereq']='Minimum requirements not met!';
             break;
         case 'upgrade': //Manual upgrade.... when JS (ajax) is not supported.