Newer
Older
<?php
/*********************************************************************
class.installer.php
osTicket Intaller - installs the latest version.
Peter Rotich <peter@osticket.com>
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 INCLUDE_DIR.'class.migrater.php';
require_once INCLUDE_DIR.'class.setup.php';
require_once INCLUDE_DIR.'class.i18n.php';
class Installer extends SetupWizard {
var $config;
function __construct($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'=>'string', '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'=>__('Host name 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'));
$vars = array_map('trim', $vars);
if(!Validator::process($f,$vars,$this->errors) && !$this->errors['err'])
__('Missing or invalid data'),
__('Correct any errors below 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 do 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');
// Support port number specified in the hostname with a colon (:)
list($host, $port) = explode(':', $vars['dbhost']);
if ($port && is_numeric($port) && ($port < 1 || $port > 65535))
$this->errors['db'] = __('Invalid database port number');
//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']=sprintf(__('Unable to connect to MySQL server: %s'), db_connect_error());
elseif(explode('.', db_version()) < explode('.', $this->getMySQLVersion()))
$this->errors['db']=sprintf(__('osTicket requires MySQL %s or later!'),$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');
} else {
//Abort if we have another installation (or table) with same prefix.
$sql = 'SELECT * FROM `'.$vars['prefix'].'config` LIMIT 1';
Peter Rotich
committed
if(db_query($sql, false)) {
$this->errors['err'] = __('We have a problem - another installation with same table prefix exists!');
$this->errors['prefix'] = __('Prefix already in-use');
} else {
//Try changing charset and collation of the DB - no bigie if we fail.
Peter Rotich
committed
db_query('ALTER DATABASE '.$vars['dbname'].' DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci', false);
}
}
/*************** We're ready to install ************************/
define('ADMIN_EMAIL',$vars['admin_email']); //Needed to report SQL errors during install.
define('TABLE_PREFIX',$vars['prefix']); //Table prefix
Bootstrap::defineTables(TABLE_PREFIX);
// Check password against password policy (after loading code)
try {
PasswordPolicy::checkPassword($vars['passwd'], null);
}
catch (BadPassword $e) {
$this->errors['passwd'] = $e->getMessage();
}
// bailout on errors.
if ($this->errors)
return false;
$debug = true; // Change it to false to squelch SQL errors.
if(!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)');
$streams = DatabaseMigrater::getUpgradeStreams(INCLUDE_DIR.'upgrader/streams/');
foreach ($streams as $stream=>$signature) {
$schemaFile = INC_DIR."streams/$stream/install-mysql.sql";
if (!file_exists($schemaFile) || !($fp2 = fopen($schemaFile, 'rb')))
$this->errors['err'] = sprintf(
__('%s: Internal error occurred - please make sure your download is the latest (#1)'),
elseif (
// TODO: Make the hash algo configurable in the streams
// configuration ( core : md5 )
!($hash = md5(fread($fp2, filesize($schemaFile))))
|| strcasecmp($signature, $hash))
$this->errors['err'] = sprintf(
__('%s: Unknown or invalid schema signature (%s .. %s)'),
$stream,
$signature, $hash);
elseif (!$this->load_sql_file($schemaFile, $vars['prefix'], true, $debug))
$this->errors['err'] = sprintf(
__('%s: Error parsing SQL schema! Get help from developers (#4)'),
$stream);
if ($this->errors)
return false;
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
// TODO: Use language selected from install worksheet
$i18n = new Internationalization($vars['lang_id']);
$i18n->loadDefaultData();
Signal::send('system.install', $this);
list($sla_id) = Sla::objects()->order_by('id')->values_flat('id')->first();
list($dept_id) = Dept::objects()->order_by('id')->values_flat('id')->first();
list($role_id) = Role::objects()->order_by('id')->values_flat('id')->first();
$sql='SELECT `tpl_id` FROM `'.TABLE_PREFIX.'email_template_group` ORDER BY `tpl_id` LIMIT 1';
$template_id_1 = db_result(db_query($sql, false));
// Create admin user.
$staff = Staff::create(array(
'isactive' => 1,
'isadmin' => 1,
'max_page_size' => 25,
'dept_id' => $dept_id,
'role_id' => $role_id,
'email' => $vars['admin_email'],
'firstname' => $vars['fname'],
'lastname' => $vars['lname'],
'username' => $vars['username'],
));
$staff->updatePerms(array(
User::PERM_CREATE,
User::PERM_EDIT,
User::PERM_DELETE,
User::PERM_MANAGE,
User::PERM_DIRECTORY,
Organization::PERM_CREATE,
Organization::PERM_EDIT,
Organization::PERM_DELETE,
FAQ::PERM_MANAGE,
Email::PERM_BANLIST,
$staff->setPassword($vars['passwd']);
if (!$staff->save()) {
$this->errors['err'] = __('Unable to create admin user (#6)');
return false;
// Extended Access
foreach (Dept::objects()
->filter(Q::not(array('id' => $dept_id)))
->values_flat('id') as $row) {
$da = new StaffDeptAccess(array(
'dept_id' => $row[0],
'role_id' => $role_id
));
$staff->dept_access->add($da);
}
$staff->dept_access->saveAll();
// Create default emails!
$email = $vars['email'];
list(,$domain) = explode('@', $vars['email']);
foreach (array(
"Support" => $email,
"osTicket Alerts" => "alerts@$domain",
'' => "noreply@$domain",
) as $name => $mailbox) {
$mb = Email::create(array(
'name' => $name,
'email' => $mailbox,
'dept_id' => $dept_id,
));
$mb->save();
if ($mailbox == $email)
$support_email_id = $mb->email_id;
if ($mailbox == "alerts@$domain")
$alert_email_id = $mb->email_id;
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
//Create config settings---default settings!
$defaults = array(
'default_email_id'=>$support_email_id,
'alert_email_id'=>$alert_email_id,
'default_dept_id'=>$dept_id, 'default_sla_id'=>$sla_id,
'default_template_id'=>$template_id_1,
'default_timezone' => $vars['timezone'] ?: date_default_timezone_get(),
'admin_email'=>$vars['admin_email'],
'schema_signature'=>$streams['core'],
'helpdesk_url'=>URL,
'helpdesk_title'=>$vars['name']
);
$config = new Config('core');
if (!$config->updateAll($defaults))
$this->errors['err']=__('Unable to create config settings').' (#7)';
// Set company name
require_once(INCLUDE_DIR.'class.company.php');
$company = new Company();
$company->getForm()->setAnswer('name', $vars['name']);
$company->getForm()->save();
foreach ($streams as $stream => $signature) {
if ($stream != 'core') {
$config = new Config($stream);
if (!$config->update('schema_signature', $signature))
$this->errors['err'] = __('Unable to create config settings').' (#8)';
}
}
if ($this->errors)
return false; //Abort on internal errors.
//Rewrite the config file - MUST be done last to allow for installer recovery.
$configFile = strtr($configFile, array(
"define('OSTINSTALLED',FALSE);" => "define('OSTINSTALLED',TRUE);",
'%ADMIN-EMAIL' => $vars['admin_email'],
'%CONFIG-DBHOST' => $vars['dbhost'],
'%CONFIG-DBNAME' => $vars['dbname'],
'%CONFIG-DBUSER' => $vars['dbuser'],
'%CONFIG-DBPASS' => $vars['dbpass'],
'%CONFIG-PREFIX' => $vars['prefix'],
'%CONFIG-SIRI' => Misc::randCode(32),
));
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 a ticket to make the system warm and happy.
$ticket_vars = $i18n->getTemplate('templates/ticket/installed.yaml')
->getData();
$ticket = Ticket::create($ticket_vars, $errors, 'api', false, false);
&& ($org = Organization::objects()->order_by('id')->one())
) {
$user = User::lookup($ticket->getOwnerId());
$user->setOrganization($org);
}
//TODO: create another personalized ticket and assign to admin??
$msg=__("Congratulations osTicket basic installation completed!\n\nThank you for choosing osTicket!");
$sql='INSERT INTO '.TABLE_PREFIX.'syslog SET created=NOW(), updated=NOW(), log_type="Debug" '
.', title="osTicket installed!"'
.', log='.db_input($msg)
.', ip_address='.db_input($_SERVER['REMOTE_ADDR']);
Peter Rotich
committed
db_query($sql, false);
return true;
}
}
?>