diff --git a/setup/inc/class.installer.php b/setup/inc/class.installer.php
new file mode 100644
index 0000000000000000000000000000000000000000..ab6c5216d80d3d83b9b49cebe93e88262f152439
--- /dev/null
+++ b/setup/inc/class.installer.php
@@ -0,0 +1,199 @@
+<?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_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/class.upgrader.php b/setup/inc/class.upgrader.php
new file mode 100644
index 0000000000000000000000000000000000000000..971a542c9fa45e0e172a47e3c62bd7295e52f3ce
--- /dev/null
+++ b/setup/inc/class.upgrader.php
@@ -0,0 +1,279 @@
+<?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';
+
+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'];
+
+        $this->migrater = DatabaseMigrater($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->getRollup($this->getStops());
+    }
+
+    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
+
+        //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";
+        $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/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..bd961286a51cf5dda8941e2caf96c578990e72dc 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);