Skip to content
Snippets Groups Projects
class.setup.php 19.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • Jared Hancock's avatar
    Jared Hancock committed
    <?php
    /*********************************************************************
        class.setup.php
    
        osTicket setup wizard.
    
        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:
    **********************************************************************/
    
    Class SetupWizard {
    
        //Mimimum requirements
        var $prereq = array('php'   => '4.3',
                            'mysql' => '4.4');
    
        //Version info - same as the latest version.
    
        var $version ='1.7-dpr2';
        var $version_verbose='1.7 DPR 2';
    
    Jared Hancock's avatar
    Jared Hancock committed
    
        //Errors
        var $errors=array();
    
        function SetupWizard(){
            $this->errors=array();
        }
    
    
        function load_sql_file($file, $prefix, $abort=true, $debug=false) {
    
    Jared Hancock's avatar
    Jared Hancock committed
            
            if(!file_exists($file) || !($schema=file_get_contents($file)))
    
                return $this->abort('Error accessing SQL file '.basename($file));
    
            return $this->load_sql($schema, $prefix, $abort, $debug);
    
    Jared Hancock's avatar
    Jared Hancock committed
        }
    
        /*
            load SQL schema - assumes MySQL && existing connection
            */
    
        function load_sql($schema, $prefix, $abort=true, $debug=false) {
    
    Jared Hancock's avatar
    Jared Hancock committed
    
            # Strip comments and remarks
            $schema=preg_replace('%^\s*(#|--).*$%m','',$schema);
            # Replace table prefis
            $schema = str_replace('%TABLE_PREFIX%',$prefix, $schema);
            # Split by semicolons - and cleanup 
            if(!($statements = array_filter(array_map('trim', @explode(';', $schema)))))
                return $this->abort('Error parsing SQL schema');
    
    
            @mysql_query('SET SESSION SQL_MODE =""');
            foreach($statements as $k=>$sql) {
                if(!mysql_query($sql)) {
                    if($debug) echo "[$sql]=>".mysql_error();
    
                    if($abort)
                        return $this->abort("[$sql] - ".mysql_error());
    
    Jared Hancock's avatar
    Jared Hancock committed
                }
            }
    
            return true;
        }
    
        function getVersion() {
            return $this->version;
        }
    
        function getVersionVerbose() {
            return $this->version_verbose;
        }
    
        function getPHPVersion() {
            return $this->prereq['php'];
        }
    
        function getMySQLVersion() {
            return $this->prereq['mysql'];
        }
    
        function check_php() {
            return (version_compare(PHP_VERSION,$this->getPHPVersion())>=0);
        }
    
        function check_mysql() {
            return (extension_loaded('mysql'));
        }
    
        function check_prereq() {
            return ($this->check_php() && $this->check_mysql());
        }
    
        /*
            @error is a mixed var.
        */
        function abort($error) {
    
           
            $this->onError($error);
    
            return false; // Always false... It's an abort.
        }
    
        function setError($error) {
        
    
    Jared Hancock's avatar
    Jared Hancock committed
            if($error && is_array($error))
                $this->errors = array_merge($this->errors,$error);
            elseif($error)
                $this->errors[] = $error;
        }
    
        function getErrors(){
    
            return $this->errors;
        }
    
    
        function onError($error) {
           return $this->setError($error);
    
    Jared Hancock's avatar
    Jared Hancock committed
        }
    }
    
    class Upgrader extends SetupWizard {
    
        var $prefix;
    
        var $sqldir;
        var $signature;
    
        function Upgrader($signature, $prefix, $sqldir) {
    
            $this->signature = $signature;
            $this->shash = substr($signature, 0, 8);
    
    Jared Hancock's avatar
    Jared Hancock committed
            $this->prefix = $prefix;
    
            $this->sqldir = $sqldir;
    
    Jared Hancock's avatar
    Jared Hancock committed
            $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;
    
    Jared Hancock's avatar
    Jared Hancock committed
        }
    
        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))
    
        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()))
    
    Jared Hancock's avatar
    Jared Hancock committed
                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.');
    
    Jared Hancock's avatar
    Jared Hancock committed
                    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']);
    
    
    Jared Hancock's avatar
    Jared Hancock committed
            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;
    
    Jared Hancock's avatar
    Jared Hancock committed
        }
    }
    
    /*
       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']))
    
    Jared Hancock's avatar
    Jared Hancock committed
                $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))
    
    Jared Hancock's avatar
    Jared Hancock committed
                $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'])
    
    Jared Hancock's avatar
    Jared Hancock committed
                    .', 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 '
    
    Jared Hancock's avatar
    Jared Hancock committed
                    .', 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 '
    
    Jared Hancock's avatar
    Jared Hancock committed
                .' ,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;
        }
    }
    ?>