diff --git a/include/class.config.php b/include/class.config.php
index e54c0d7392d8268c9b273ed2f802a562dcaf20b3..95a5204e389b55c161cf192c1af9511793732e70 100644
--- a/include/class.config.php
+++ b/include/class.config.php
@@ -93,7 +93,7 @@ class Config {
         if($this->config['schema_signature'])
             return $this->config['schema_signature'];
         elseif($this->config['ostversion']) //old version 1.6 st.
-            return md5($this->config['ostversion']);
+            return md5(strtoupper($this->config['ostversion']));
 
         return null;
     }
diff --git a/setup/inc/class.installer.php b/setup/inc/class.installer.php
new file mode 100644
index 0000000000000000000000000000000000000000..75ebdc7cdef148a89fd9016e1d1eb62e278f8e39
--- /dev/null
+++ b/setup/inc/class.installer.php
@@ -0,0 +1,202 @@
+<?php
+/*********************************************************************
+    class.installer.php
+
+    osTicket Intaller - installs the latest version.
+
+    Peter Rotich <peter@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:
+**********************************************************************/
+require_once INC_DIR.'class.setup.php';
+
+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_thread SET created=NOW()'
+                .', source="Web" '
+                .', thread_type="M" '
+                .', ticket_id='.db_input($tid)
+                .', title='.db_input('osTicket Installed')
+                .', body='.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/class.migrater.php b/setup/inc/class.migrater.php
new file mode 100644
index 0000000000000000000000000000000000000000..de8a3d9590e97b9d78ecb905528bcbc1bc2c03a5
--- /dev/null
+++ b/setup/inc/class.migrater.php
@@ -0,0 +1,67 @@
+<?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 {
+
+    var $start;
+    var $end;
+    var $sqldir;
+
+    function DatabaseMigrater($start, $end, $sqldir) {
+       
+        $this->start = $start;
+        $this->end = $end;
+        $this->sqldir = $sqldir;
+       
+    }
+
+    function getPatches($stop=null) {
+
+        $start= $this->start;
+        $stop = $stop?$stop:$this->end;
+
+        $patches = array();
+        while (true) {
+            $next = glob($this->sqldir . substr($start, 0, 8)
+                         . '-*.patch.sql');
+            if (count($next) == 1) {
+                $patches[] = $next[0];
+                $start = substr(basename($next[0]), 9, 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;
+            }
+
+            # Break if we've reached our target stop.
+            if(!$start || !strncasecmp($start, $stop, 8))
+                break;
+        }
+
+        return $patches;
+    }
+}
+?>
diff --git a/setup/inc/class.setup.php b/setup/inc/class.setup.php
index add91be89f15299203d5ac4b4424f2af92b72957..34bc57486942bca10d9540459dfd8daf37a4f232 100644
--- a/setup/inc/class.setup.php
+++ b/setup/inc/class.setup.php
@@ -120,453 +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_thread SET created=NOW(),source="Web" '
-                .', poster="System"'
-                .', ticket_id='.db_input($tid)
-                .', body='.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/class.upgrader.php b/setup/inc/class.upgrader.php
new file mode 100644
index 0000000000000000000000000000000000000000..cd785a9d2f0b0709483a39e8ac4effaed67589ac
--- /dev/null
+++ b/setup/inc/class.upgrader.php
@@ -0,0 +1,277 @@
+<?php
+/*********************************************************************
+    class.upgrader.php
+
+    osTicket Upgrader
+
+    Peter Rotich <peter@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:
+**********************************************************************/
+
+require_once INC_DIR.'class.setup.php';
+require_once INC_DIR.'class.migrater.php';
+
+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'];
+
+        //Database migrater 
+        $this->migrater = new DatabaseMigrater($this->signature, SCHEMA_SIGNATURE, $this->sqldir);
+    }
+
+    function getStops() {
+        return array('7be60a84' => 'migrateAttachments2DB');
+    }
+
+    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 getPatches() {
+        return $this->migrater->getPatches();
+    }
+
+    function getNextPatch() {
+        $p = $this->getPatches();
+        return (count($p)) ? $p[0] : false;
+    }
+
+    function getNextVersion() {
+        if(!$patch=$this->getNextPatch())
+            return '(Latest)';
+
+        $info = $this->readPatchInfo($patch);
+        return $info['version'];
+    }
+
+    function readPatchInfo($patch) {
+        $info = array();
+        if (preg_match('/\*(.*)\*/', file_get_contents($patch), $matches)) {
+            if (preg_match('/@([\w\d_-]+)\s+(.*)$/', $matches[0], $matches2))
+                foreach ($matches2 as $match)
+                    $info[$match[0]] = $match[1];
+        }
+        if (!isset($info['version']))
+            $info['version'] = 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 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() || !($patches=$this->getPatches()))
+            return false;
+
+        foreach ($patches as $patch) {
+            if (!$this->load_sql_file($patch, $this->getTablePrefix()))
+                return false;
+
+            //TODO: Log the upgrade
+        
+            
+            //clear previous patch info - 
+            unset($_SESSION['ost_upgrader'][$this->getSHash()]);
+
+            //Load up post-upgrade tasks.... if any.
+            $phash = substr(basename($patch), 0, 17);
+            if(!($tasks=$this->getTasksForPatch($phash)))
+                continue;
+
+            //We have tasks to perform - set the tasks and break.
+            $shash = substr($phash, 9, 8);
+            $_SESSION['ost_upgrader'][$shash]['tasks'] = $tasks;
+            $_SESSION['ost_upgrader'][$shash]['state'] = 'upgrade';
+            break;
+        }
+
+        return true;
+
+    }
+
+    function getTasksForPatch($phash) {
+
+        $tasks=array();
+        switch($phash) { //Add  patch specific scripted tasks.
+            case 'd4fe13b1-7be60a84': //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;
+        }
+
+        //Check if cleanup p 
+        $file=$this->getSQLDir().$phash.'.cleanup.sql';
+        if(file_exists($file)) 
+            $tasks[] = array('func' => 'cleanup', 'desc' => 'Post-upgrade cleanup!');
+
+
+        return $tasks;
+    }
+
+    /************* TASKS **********************/
+    function cleanup($tId=0) {
+
+        $file=$this->getSQLDir().$this->getSHash().'-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";
+        $att_migrater = new AttachmentMigrater();
+        $att_migrater->start_migration();
+        # XXX: Loop here (with help of task manager)
+        $att_migrater->do_batch();
+        return 0;
+    }
+}
+?>
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;
diff --git a/setup/install.php b/setup/install.php
index 27b1388933d03ef9c0db5690dd147213848d750f..d019598e55de7099c9dbb517b4a9aa1663886ad0 100644
--- a/setup/install.php
+++ b/setup/install.php
@@ -15,6 +15,9 @@
 **********************************************************************/
 require('setup.inc.php');
 
+require_once INC_DIR.'class.installer.php';
+
+
 //define('OSTICKET_CONFIGFILE','../include/ost-config.php'); //osTicket config file full path.
 define('OSTICKET_CONFIGFILE','../include/ost-config.php'); //XXX: Make sure the path is corrent b4 releasing.
 
diff --git a/setup/p.php b/setup/p.php
index b508c9303f1b33a951b15e7dbafc8e0047c3ff3a..ebc4128b3455ac4063425bb9802abc856ff2c0e9 100644
--- a/setup/p.php
+++ b/setup/p.php
@@ -27,7 +27,8 @@ if(!$thisstaff or !$thisstaff->isadmin()) {
 define('SETUPINC', true);
 define('INC_DIR', './inc/');
 define('SQL_DIR', INC_DIR.'sql/');
-require_once INC_DIR.'class.setup.php';
+
+require_once INC_DIR.'class.upgrader.php';
 
 
 $upgrader = new Upgrader($cfg->getSchemaSignature(), TABLE_PREFIX, SQL_DIR);
diff --git a/setup/setup.inc.php b/setup/setup.inc.php
index d8108e079c5a0df0411ff57cce4d1ecb97eb9e87..44b2ad4d593f5521b66ad170c9e801a817cf74f1 100644
--- a/setup/setup.inc.php
+++ b/setup/setup.inc.php
@@ -52,7 +52,6 @@ ini_set('include_path', './'.PATH_SEPARATOR.INC_DIR.PATH_SEPARATOR.INCLUDE_DIR.P
 endif;
 
 #required files
-require_once(INC_DIR.'class.setup.php');
 require_once(INCLUDE_DIR.'class.validator.php');
 require_once(INCLUDE_DIR.'class.format.php');
 require_once(INCLUDE_DIR.'class.misc.php');
diff --git a/setup/upgrade.php b/setup/upgrade.php
index c70d183ffe290de4a09ade74b1ff7ec47bd4acde..324c467efd72d887b7592af33f6ba201c6f26e22 100644
--- a/setup/upgrade.php
+++ b/setup/upgrade.php
@@ -30,7 +30,8 @@ if(!$thisstaff or !$thisstaff->isadmin()) {
 define('SETUPINC', true);
 define('INC_DIR', './inc/');
 define('SQL_DIR', INC_DIR.'sql/');
-require_once INC_DIR.'class.setup.php';
+
+require_once INC_DIR.'class.upgrader.php';
 
 //$_SESSION['ost_upgrader']=null;
 $upgrader = new Upgrader($cfg->getSchemaSignature(), TABLE_PREFIX, SQL_DIR);
@@ -48,7 +49,9 @@ if($_POST && $_POST['s'] && !$upgrader->isAborted()) {
         case 'prereq':
             //XXX: check if it's upgradable version??
             if(!$cfg->isUpgradePending())
-                $errors['err']=' Nothing to do! System already upgraded to the current version'; 
+                $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())
                 $upgrader->setState('upgrade');
             else
@@ -88,6 +91,8 @@ switch(strtolower($upgrader->getState())) {
             $inc='upgrade-aborted.inc.php';
         elseif(!$cfg->isUpgradePending())
             $errors['err']='Nothing to do! System already upgraded to the latest version';
+        elseif(!$upgrader->isUpgradable())
+            $errors['err']='The upgrader does NOT support upgrading from the current vesion!';
 }
 
 require(INC_DIR.'header.inc.php');