diff --git a/setup/inc/class.migrater.php b/setup/inc/class.migrater.php
new file mode 100644
index 0000000000000000000000000000000000000000..6475ac03cf1ca6247e9e0402e447635d886f188b
--- /dev/null
+++ b/setup/inc/class.migrater.php
@@ -0,0 +1,55 @@
+<?php
+/*********************************************************************
+    class.migrater.php
+
+    SQL database migrater. This provides the engine capable of rolling the
+    database for an osTicket installation forward (and perhaps even
+    backward) in time using a set of included migration scripts. Each script
+    will roll the database between two database checkpoints. Where possible,
+    the migrater will roll several checkpoint scripts into one to be applied
+    together.
+
+    Jared Hancock <jared@osticket.com>
+    Copyright (c)  2006-2012 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:
+**********************************************************************/
+
+class DatabaseMigrater {
+
+    function DatabaseMigrater($sqldir) {
+        $this->sqldir = $sqldir;
+    }
+
+    function getRollup($stops) {
+        $cfg->reload();
+        $start = $cfg->getSchemaSignature();
+
+        $patches = array();
+        while (true) {
+            $next = glob($this->sqldir . $substr($start,0,8)
+                         . '-*.patch.sql');
+            if (count($next) == 1) {
+                $patches[] = $next[0];
+                $start = substr($next[0], 0, 8);
+            } elseif ($count($next) == 0) {
+                # There are no patches leaving the current signature. We
+                # have to assume that we've applied all the available
+                # patches.
+                break;
+            } else {
+                # Problem -- more than one patch exists from this snapshot.
+                # We probably need a graph approach to solve this.
+                break;
+            }
+
+            if (array_key_exists($next[0], $stops))
+                break;
+        }
+        return $patches;
+    }
+}
diff --git a/setup/inc/class.setup.php b/setup/inc/class.setup.php
index 5108cd4306bc840bc38177088cd6b11c938c391b..34bc57486942bca10d9540459dfd8daf37a4f232 100644
--- a/setup/inc/class.setup.php
+++ b/setup/inc/class.setup.php
@@ -120,452 +120,4 @@ Class SetupWizard {
        return $this->setError($error);
     }
 }
-
-class Upgrader extends SetupWizard {
-
-    var $prefix;
-    var $sqldir;
-    var $signature;
-
-    function Upgrader($signature, $prefix, $sqldir) {
-
-        $this->signature = $signature;
-        $this->shash = substr($signature, 0, 8);
-        $this->prefix = $prefix;
-        $this->sqldir = $sqldir;
-        $this->errors = array();
-
-        //Init persistent state of upgrade.
-        $this->state = &$_SESSION['ost_upgrader'][$this->getShash()]['state'];
-
-        //Init the task Manager.
-        if(!isset($_SESSION['ost_upgrader'][$this->getShash()]))
-            $_SESSION['ost_upgrader'][$this->getShash()]['tasks']=array();
-
-        //Tasks to perform - saved on the session.
-        $this->tasks = &$_SESSION['ost_upgrader'][$this->getShash()]['tasks'];
-    }
-
-    function onError($error) {
-        $this->setError($error);
-        $this->setState('aborted');
-    }
-
-    function isUpgradable() {
-        return (!$this->isAborted() && $this->getNextPatch());
-    }
-
-    function isAborted() {
-        return !strcasecmp($this->getState(), 'aborted');
-    }
-
-    function getSchemaSignature() {
-        return $this->signature;
-    }
-
-    function getShash() {
-        return $this->shash;
-    }
-
-    function getTablePrefix() {
-        return $this->prefix;
-    }
-
-    function getSQLDir() {
-        return $this->sqldir;
-    }
-
-    function getState() {
-        return $this->state;
-    }
-
-    function setState($state) {
-        $this->state = $state;
-    }
-
-    function getNextPatch() {
-
-        if(!($patch=glob($this->getSQLDir().$this->getShash().'-*.patch.sql')))
-            return null;
-
-        return $patch[0];
-    }
-
-    function getThisPatch() {
-                
-        if(!($patch=glob($this->getSQLDir().'*-'.$this->getShash().'.patch.sql')))
-            return null;
-
-        return $patch[0];
-
-    }
-
-    function getNextVersion() {
-        if(!$patch=$this->getNextPatch())
-            return '(Latest)';
-
-        if(preg_match('/\*(.*)\*/', file_get_contents($patch), $matches))
-            $info=$matches[0];
-        else
-            $info=substr(basename($patch), 9, 8);
-
-        return $info;
-    }
-
-    function getNextAction() {
-
-        $action='Upgrade osTicket to '.$this->getVersion();
-        if($this->getNumPendingTasks() && ($task=$this->getNextTask())) {
-            $action = $task['desc'];
-            if($task['status']) //Progress report... 
-                $action.=' ('.$task['status'].')';
-        } elseif($this->isUpgradable() && ($nextversion = $this->getNextVersion())) {
-            $action = "Upgrade to $nextversion";
-        }
-
-        return $action;
-    }
-
-    function getNextStepInfo() {
-
-        if(($patches=$this->getPatches()))
-            return $patches[0];
-        
-        if(($hooks=$this->getScriptedHooks()))
-            return $hooks[0]['desc'];
-
-        return null;
-    }
-
-    function getPatches($ToSignature) {
-     
-        $signature = $this->getSignature();
-        $patches = array();
-        while(($patch=glob($this->getSQLDir().substr($signature,0,8).'-*.patch.sql')) && $i++) {
-            $patches = array_merge($patches, $patch);
-            if(!($signature=substr(basename($patch[0]), 10, 8)) || !strncasecmp($signature, $ToSignature, 8))
-                break;
-        }
-
-        return $patches;
-    }
-
-    function getNumPendingTasks() {
-
-        return count($this->getPendingTasks());
-    }
-
-    function getPendingTasks() {
-
-        $pending=array();
-        if(($tasks=$this->getTasks())) {
-            foreach($tasks as $k => $task) {
-                if(!$task['done'])
-                    $pending[$k] = $task;
-            }  
-        }
-        
-        return $pending;
-    }
-
-    function getTasks() {
-       return $this->tasks;
-    }
-
-    function getNextTask() {
-
-        if(!($tasks=$this->getPendingTasks()))
-            return null;
-
-        return current($tasks);
-    }
-
-    function removeTask($tId) {
-
-        if(isset($this->tasks[$tId]))
-            unset($this->tasks[$tId]);
-
-        return (!$this->tasks[$tId]);
-    }
-
-    function setTaskStatus($tId, $status) {
-        if(isset($this->tasks[$tId]))
-            $this->tasks[$tId]['status'] = $status;
-    }
-
-    function doTasks() {
-
-        if(!($tasks=$this->getPendingTasks()))
-            return true; //Nothing to do.
-
-        foreach($tasks as $k => $task) {
-            if(call_user_func(array($this, $task['func']), $k)===0) {
-                $this->tasks[$k]['done'] = true;
-            } else { //Task has pending items to process.
-                break;
-            }
-        }
-
-        return (!$this->getPendingTasks());
-    }
-    
-    function upgrade() {
-
-        if($this->getPendingTasks() || !($patch=$this->getNextPatch()))
-            return false;
-
-        if(!$this->load_sql_file($patch, $this->getTablePrefix(), true, true))
-            return false;
-
-        //TODO: Log the upgrade
-
-        //Load up post install tasks.
-        $shash = substr(basename($patch), 9, 8); 
-        $phash = substr(basename($patch), 0, 17); 
-        
-        $tasks=array();
-        $tasks[] = array('func' => 'sometask',
-                         'desc' => 'Some Task.... blah');
-        switch($phash) { //Add  patch specific scripted tasks.
-            case 'xxxx': //V1.6 ST- 1.7 *
-                $tasks[] = array('func' => 'migrateAttachments2DB',
-                                 'desc' => 'Migrating attachments to database, it might take a while depending on the number of files.');
-                break;
-        }
-        
-        $tasks[] = array('func' => 'cleanup',
-                         'desc' => 'Post-upgrade cleanup!');
-        
-        
-        
-        //Load up tasks - NOTE: writing directly to the session - back to the future majic.
-        $_SESSION['ost_upgrader'][$shash]['tasks'] = $tasks;
-        $_SESSION['ost_upgrader'][$shash]['state'] = 'upgrade';
-
-        //clear previous patch info - 
-        unset($_SESSION['ost_upgrader'][$this->getSHash()]);
-
-        return true;
-    }
-
-    /************* TASKS **********************/
-    function sometask($tId) {
-        
-        $this->setTaskStatus($tId, 'Doing... '.time(). ' #'.$_SESSION['sometask']);
-
-        sleep(2);
-        $_SESSION['sometask']+=1;
-        if($_SESSION['sometask']<4)
-            return 22;
-
-        $_SESSION['sometask']=0;
-
-        return 0;  //Change to 1 for testing...
-    }
-
-    function cleanup($tId=0) {
-
-        $file=$this->getSQLDir().$this->getSchemaSignature().'-cleanup.sql';
-        if(!file_exists($file)) //No cleanup script.
-            return 0;
-
-        //We have a cleanup script  ::XXX: Don't abort on error? 
-        if($this->load_sql_file($file, $this->getTablePrefix(), false, true))
-            return 0;
-
-        //XXX: ???
-        return false;
-    }
-    
-
-    function migrateAttachments2DB($tId=0) {
-        echo "Process attachments here - $tId";
-        return 0;
-    }
-}
-
-/*
-   Installer class - latest version.
-   */
-class Installer extends SetupWizard {
-
-    var $config;
-
-    function Installer($configfile) {
-        $this->config =$configfile;
-        $this->errors=array();
-    }
-
-    function getConfigFile() {
-        return $this->config;
-    }
-
-    function config_exists() {
-        return ($this->getConfigFile() && file_exists($this->getConfigFile()));
-    }
-
-    function config_writable() {
-        return ($this->getConfigFile() && is_writable($this->getConfigFile()));
-    }
-
-    function check_config() {
-        return ($this->config_exists() && $this->config_writable());
-    }
-
-    //XXX: Latest version insall logic...no carry over.
-    function install($vars) {
-
-        $this->errors=$f=array();
-        
-        $f['name']          = array('type'=>'string',   'required'=>1, 'error'=>'Name required');
-        $f['email']         = array('type'=>'email',    'required'=>1, 'error'=>'Valid email required');
-        $f['fname']         = array('type'=>'string',   'required'=>1, 'error'=>'First name required');
-        $f['lname']         = array('type'=>'string',   'required'=>1, 'error'=>'Last name required');
-        $f['admin_email']   = array('type'=>'email',    'required'=>1, 'error'=>'Valid email required');
-        $f['username']      = array('type'=>'username', 'required'=>1, 'error'=>'Username required');
-        $f['passwd']        = array('type'=>'password', 'required'=>1, 'error'=>'Password required');
-        $f['passwd2']       = array('type'=>'string',   'required'=>1, 'error'=>'Confirm password');
-        $f['prefix']        = array('type'=>'string',   'required'=>1, 'error'=>'Table prefix required');
-        $f['dbhost']        = array('type'=>'string',   'required'=>1, 'error'=>'Hostname required');
-        $f['dbname']        = array('type'=>'string',   'required'=>1, 'error'=>'Database name required');
-        $f['dbuser']        = array('type'=>'string',   'required'=>1, 'error'=>'Username required');
-        $f['dbpass']        = array('type'=>'string',   'required'=>1, 'error'=>'password required');
-        
-
-        if(!Validator::process($f,$vars,$this->errors) && !$this->errors['err'])
-            $this->errors['err']='Missing or invalid data - correct the errors and try again.';
-
-
-        //Staff's email can't be same as system emails.
-        if($vars['admin_email'] && $vars['email'] && !strcasecmp($vars['admin_email'],$vars['email']))
-            $this->errors['admin_email']='Conflicts with system email above';
-        //Admin's pass confirmation. 
-        if(!$this->errors && strcasecmp($vars['passwd'],$vars['passwd2']))
-            $this->errors['passwd2']='passwords to not match!';
-        //Check table prefix underscore required at the end!
-        if($vars['prefix'] && substr($vars['prefix'], -1)!='_')
-            $this->errors['prefix']='Bad prefix. Must have underscore (_) at the end. e.g \'ost_\'';
-
-        //Make sure admin username is not very predictable. XXX: feels dirty but necessary 
-        if(!$this->errors['username'] && in_array(strtolower($vars['username']),array('admin','admins','username','osticket')))
-            $this->errors['username']='Bad username';
-
-        //MYSQL: Connect to the DB and check the version & database (create database if it doesn't exist!)
-        if(!$this->errors) {
-            if(!db_connect($vars['dbhost'],$vars['dbuser'],$vars['dbpass']))
-                $this->errors['db']='Unable to connect to MySQL server. Possibly invalid login info.';
-            elseif(db_version()< $this->getMySQLVersion())
-                $this->errors['db']=sprintf('osTicket requires MySQL %s or better!',$this->getMySQLVersion());
-            elseif(!db_select_database($vars['dbname']) && !db_create_database($vars['dbname'])) {
-                $this->errors['dbname']='Database doesn\'t exist';
-                $this->errors['db']='Unable to create the database.';
-            } elseif(!db_select_database($vars['dbname'])) {
-                $this->errors['dbname']='Unable to select the database';
-            }
-        }
-
-        //bailout on errors.
-        if($this->errors) return false;
-
-        /*************** We're ready to install ************************/
-        define('ADMIN_EMAIL',$vars['admin_email']); //Needed to report SQL errors during install.
-        define('PREFIX',$vars['prefix']); //Table prefix
-
-        $schemaFile =INC_DIR.'sql/osticket-v1.7-mysql.sql'; //DB dump.
-        $debug = true; //XXX:Change it to true to show SQL errors.
-
-        //Last minute checks.
-        if(!file_exists($schemaFile))
-            $this->errors['err']='Internal Error - please make sure your download is the latest (#1)';
-        elseif(!file_exists($this->getConfigFile()) || !($configFile=file_get_contents($this->getConfigFile())))
-            $this->errors['err']='Unable to read config file. Permission denied! (#2)';
-        elseif(!($fp = @fopen($this->getConfigFile(),'r+')))
-            $this->errors['err']='Unable to open config file for writing. Permission denied! (#3)';
-        elseif(!$this->load_sql_file($schemaFile,$vars['prefix'], true, $debug))
-            $this->errors['err']='Error parsing SQL schema! Get help from developers (#4)';
-              
-        if(!$this->errors) {
-            //Create admin user.
-            $sql='INSERT INTO '.PREFIX.'staff SET created=NOW() '
-                .', isactive=1, isadmin=1, group_id=1, dept_id=1, timezone_id=8, max_page_size=25 '
-                .', email='.db_input($_POST['admin_email'])
-                .', firstname='.db_input($vars['fname'])
-                .', lastname='.db_input($vars['lname'])
-                .', username='.db_input($vars['username'])
-                .', passwd='.db_input(Passwd::hash($vars['passwd']));
-            if(!mysql_query($sql) || !($uid=mysql_insert_id()))
-                $this->errors['err']='Unable to create admin user (#6)';
-        }
-
-        if(!$this->errors) {
-            //Create config settings---default settings!
-            //XXX: rename ostversion  helpdesk_* ??
-            $sql='INSERT INTO '.PREFIX.'config SET updated=NOW(), isonline=0 '
-                .', default_email_id=1, alert_email_id=2, default_dept_id=1 '
-                .', default_sla_id=1, default_timezone_id=8, default_template_id=1 '
-                .', admin_email='.db_input($vars['admin_email'])
-                .', schema_signature='.db_input(md5_file($schemaFile))
-                .', helpdesk_url='.db_input(URL)
-                .', helpdesk_title='.db_input($vars['name']);
-            if(!mysql_query($sql) || !($cid=mysql_insert_id()))
-                $this->errors['err']='Unable to create config settings (#7)';
-        }
-
-        if($this->errors) return false; //Abort on internal errors.
-
-
-        //Rewrite the config file - MUST be done last to allow for installer recovery.
-        $configFile= str_replace("define('OSTINSTALLED',FALSE);","define('OSTINSTALLED',TRUE);",$configFile);
-        $configFile= str_replace('%ADMIN-EMAIL',$vars['admin_email'],$configFile);
-        $configFile= str_replace('%CONFIG-DBHOST',$vars['dbhost'],$configFile);
-        $configFile= str_replace('%CONFIG-DBNAME',$vars['dbname'],$configFile);
-        $configFile= str_replace('%CONFIG-DBUSER',$vars['dbuser'],$configFile);
-        $configFile= str_replace('%CONFIG-DBPASS',$vars['dbpass'],$configFile);
-        $configFile= str_replace('%CONFIG-PREFIX',$vars['prefix'],$configFile);
-        $configFile= str_replace('%CONFIG-SIRI',Misc::randcode(32),$configFile);
-        if(!$fp || !ftruncate($fp,0) || !fwrite($fp,$configFile)) {
-            $this->errors['err']='Unable to write to config file. Permission denied! (#5)';
-            return false;
-        }
-        @fclose($fp);
-
-        /************* Make the system happy ***********************/
-        //Create default emails!
-        $email = $vars['email'];
-        list(,$domain)=explode('@',$vars['email']);
-        $sql='INSERT INTO '.PREFIX.'email (`email_id`, `dept_id`, `name`,`email`,`created`,`updated`) VALUES '
-                ." (1,1,'Support','$email',NOW(),NOW())"
-                .",(2,1,'osTicket Alerts','alerts@$domain',NOW(),NOW())"
-                .",(3,1,'','noreply@$domain',NOW(),NOW())";
-        @mysql_query($sql);
-                   
-        //Create a ticket to make the system warm and happy.
-        $sql='INSERT INTO '.PREFIX.'ticket SET created=NOW(), status="open", source="Web" '
-            .' ,priority_id=2, dept_id=1, topic_id=1 '
-            .' ,ticketID='.db_input(Misc::randNumber(6))
-            .' ,email="support@osticket.com" '
-            .' ,name="osTicket Support" '
-            .' ,subject="osTicket Installed!"';
-        if(mysql_query($sql) && ($tid=mysql_insert_id())) {
-            if(!($msg=file_get_contents(INC_DIR.'msg/installed.txt')))
-                $msg='Congratulations and Thank you for choosing osTicket!';
-                        
-            $sql='INSERT INTO '.PREFIX.'ticket_message SET created=NOW(),source="Web" '
-                .', ticket_id='.db_input($tid)
-                .', message='.db_input($msg);
-            @mysql_query($sql);
-        }
-        //TODO: create another personalized ticket and assign to admin??
-                    
-        //Log a message.
-        $msg="Congratulations osTicket basic installation completed!\n\nThank you for choosing osTicket!";
-        $sql='INSERT INTO '.PREFIX.'syslog SET created=NOW(),updated=NOW(),log_type="Debug" '
-            .', title="osTicket installed!"'
-            .', log='.db_input($msg)
-            .', ip_address='.db_input($_SERVER['REMOTE_ADDR']);
-        @mysql_query($sql);
-
-        return true;
-    }
-}
 ?>
diff --git a/setup/inc/sql/522e5b78-02decaa2.patch.sql b/setup/inc/sql/522e5b78-02decaa2.patch.sql
index a6d9a6e09cc1c16aac70dc6a9b4a0f03987a6c9b..78c951d69cc10f255ffb3e4ba85fcf93dae6db58 100644
--- a/setup/inc/sql/522e5b78-02decaa2.patch.sql
+++ b/setup/inc/sql/522e5b78-02decaa2.patch.sql
@@ -1,4 +1,6 @@
-/* v1.7-DPR2-P2 */
+/**
+ * @version v1.7-DPR2-P2 
+ */
 UPDATE `%TABLE_PREFIX%sla`
     SET `created` = NOW(),
         `updated` = NOW()
diff --git a/setup/inc/sql/7be60a84-522e5b78.patch.sql b/setup/inc/sql/7be60a84-522e5b78.patch.sql
index 35e744566d50f8a5e8d37d56c3fe3b795db2e1e5..acdaf7d0d3c1f8baaedd40bd68e33b01b97e72c9 100644
--- a/setup/inc/sql/7be60a84-522e5b78.patch.sql
+++ b/setup/inc/sql/7be60a84-522e5b78.patch.sql
@@ -1,4 +1,6 @@
-/* v1.7-DPR1 (P1) */ 
+/**
+ * @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;