UPGRADE NOTE: Do not edit or add to this file if you wish to upgrade AbanteCart to newer versions in the future. If you wish to customize AbanteCart for your needs please refer to http://www.AbanteCart.com for more information. ------------------------------------------------------------------------------*/ if (!defined('DIR_CORE')) { header('Location: static_pages/'); } /** * @property AExtensionManager $extension_manager * @property AMessage $messages * @property ALoader $load * @property ASession $session * @property ExtensionsApi $extensions * @property AUser $user * @property ALanguageManager $language * @property ALog $log * @property ACache $cache * @property ADB $db */ class APackageManager { /** * @var Registry */ protected $registry; public $error = ''; /** * size of data in bytes * * @var int */ public $dataSize = 0; public function __construct() { if (!IS_ADMIN) { // forbid for non admin calls throw new AException (AC_ERR_LOAD, 'Error: permission denied to access package manager'); } /** * @var Registry */ $this->registry = Registry::getInstance(); } public function __get($key) { return $this->registry->get($key); } public function __set($key, $value) { $this->registry->set($key, $value); } /** * @param string $url * @param boolean $save * @param string $new_file_name * @return boolean|array */ public function getRemoteFile($url, $save = true, $new_file_name = '') { if (!$url) { return false; } $file = new AConnect(); if ($save) { $result = $file->getFile($url, $new_file_name); //download } else { $result = $file->getResponse($url); // just get data } if (!$result) { $this->error = $file->error; return false; } return $result; } /** * @param string $url * @return bool|string */ public function getRemoteFileHeaders($url) { if (!$url) { return false; } $file = new AConnect(); $file->connect_method = 'socket'; //use this method because curl returns no header 'Content-Disposition' with file name $url = $url . (!is_int(strpos($url, '?')) ? '?file_size=1' : '&file_size=1'); $result = $file->getDataHeaders($url); if (!$result) { $this->error = $file->error; return false; } return $result; } /** * @param string $tar_filename * @param string $dst_dir * @return boolean */ public function unpack($tar_filename, $dst_dir) { if (!file_exists($tar_filename)) { $this->error = 'Error: Can\'t unpack file "' . $tar_filename . '" because it does not exists.'; $error = new AError ($this->error); $error->toLog()->toDebug(); return false; } if (!file_exists($dst_dir) || !is_dir($dst_dir)) { $this->error = 'Error: Can\'t unpack file "' . $tar_filename . '" because destination directory "' . $dst_dir . '" does not exists.'; $error = new AError ($this->error); $error->toLog()->toDebug(); return false; } if (!is_writable($dst_dir)) { $this->error = 'Error: Can\'t unpack file "' . $tar_filename . '" because destination directory "' . $dst_dir . '" have no write permission.'; $error = new AError ($this->error); $error->toLog()->toDebug(); return false; } $exit_code = 0; if(class_exists('PharData') ){ //remove destination folder first $this->removeDir($dst_dir . pathinfo(pathinfo($tar_filename,PATHINFO_FILENAME),PATHINFO_FILENAME) ); //run pathinfo twice for tar.gz. files try { $phar = new PharData($tar_filename); $phar->extractTo($dst_dir, null, true); } catch (Exception $e){ $error = new AError( $e->getMessage() ); $error->toLog()->toDebug(); $this->error = 'Error: Cannot to unpack file "' . $tar_filename . '". Please, see error log for details. '; return false; } }else{ $exit_code = 1; } if($exit_code){ $this->load->library('targz'); $targz = new Atargz(); $targz->extractTar($tar_filename, $dst_dir); } $this->chmod_R($dst_dir . $this->session->data['package_info']['tmp_dir'], 0777, 0777); return true; } /** * Function make backup and move it into admin/system/backup/directory * @param string $extension_id * @return bool */ public function backupPrevious($extension_id = '') { $old_path = !$extension_id ? DIR_ROOT . '/' . $this->session->data['package_info']['dst_dir'] : DIR_EXT; $package_id = !$extension_id ? $this->session->data['package_info']['package_id'] : $extension_id; if(!$package_id){ return false; } if (file_exists($old_path . $package_id)) { $backup = new ABackup($extension_id .'_'. date('Y-m-d-H-i-s')); $backup_dirname = $backup->getBackupName(); if ($backup_dirname) { if (!$backup->backupDirectory($old_path . $package_id, true)) { $this->error = $backup->error; return false; } if (!$backup->dumpDatabase()) { return false; } if (!$backup->archive(DIR_BACKUP . $backup_dirname . '.tar.gz', DIR_BACKUP, $backup_dirname)) { return false; } } else { return false; } $info = $this->extensions->getExtensionInfo($package_id); $install_upgrade_history = new ADataset('install_upgrade_history', 'admin'); $install_upgrade_history->addRows(array('date_added' => date("Y-m-d H:i:s", time()), 'name' => $package_id, 'version' => $info['version'], 'backup_file' => $backup_dirname . '.tar.gz', 'backup_date' => date("Y-m-d H:i:s", time()), 'type' => 'backup', 'user' => $this->user->getUsername())); //delete previous version $this->removeDir($old_path . $package_id); } return true; } public function replaceCoreFiles() { $corefiles = $this->session->data['package_info']['package_content']['core']; if ($this->session->data['package_info']['ftp']) { $ftp_user = $this->session->data['package_info']['ftp_user']; $ftp_password = $this->session->data['package_info']['ftp_password']; $ftp_port = $this->session->data['package_info']['ftp_port']; $ftp_host = $this->session->data['package_info']['ftp_host']; $fconnect = ftp_connect($ftp_host, $ftp_port); ftp_login($fconnect, $ftp_user, $ftp_password); ftp_pasv($fconnect, true); foreach ($corefiles as $core_filename) { $remote_file = pathinfo($this->session->data['package_info']['ftp_path'] . $core_filename, PATHINFO_BASENAME); $remote_dir = pathinfo($this->session->data['package_info']['ftp_path'] . $core_filename, PATHINFO_DIRNAME).'/'; $src_dir = (string)$this->session->data['package_info']['tmp_dir'] . $this->session->data['package_info']['package_dir'] . '/code/' . $core_filename; $result = $this->ftp_move($fconnect, $src_dir, $remote_file, $remote_dir); if ($result) { $install_upgrade_history = new ADataset('install_upgrade_history', 'admin'); $install_upgrade_history->addRows(array('date_added' => date("Y-m-d H:i:s", time()), 'name' => 'Upgrade core file: ' . $remote_file, 'version' => $this->session->data['package_info']['package_version'], 'backup_file' => '', 'backup_date' => '', 'type' => 'upgrade', 'user' => $this->user->getUsername())); } else { $this->error = "Error: Can't upgrade file : '" . $core_filename; $this->messages->saveNotice('Error', $this->error); $error = new AError ($this->error); $error->toLog()->toDebug(); } }// end of loop ftp_close($fconnect); } else { foreach ($corefiles as $core_filename) { if (file_exists(DIR_ROOT . '/' . $core_filename)) { unlink(DIR_ROOT . '/' . $core_filename); } //check is target directory exists before copying $dir = pathinfo(DIR_ROOT . '/' . $core_filename, PATHINFO_DIRNAME); if (!file_exists($dir)) { mkdir($dir, 0777, true); } $result = rename($this->session->data['package_info']['tmp_dir'] . $this->session->data['package_info']['package_dir'] . '/code/' . $core_filename, DIR_ROOT . '/' . $core_filename); if ($result) { // for index.php do not set 777 permissions because hosting providers will ban it if(pathinfo($core_filename,PATHINFO_BASENAME)=='index.php'){ chmod(DIR_ROOT . '/' . $core_filename,0755); }else{ chmod(DIR_ROOT . '/' . $core_filename,0777); } $install_upgrade_history = new ADataset('install_upgrade_history', 'admin'); $install_upgrade_history->addRows(array('date_added' => date("Y-m-d H:i:s", time()), 'name' => 'Upgrade core file: ' . $core_filename, 'version' => $this->session->data['package_info']['package_version'], 'backup_file' => '', 'backup_date' => '', 'type' => 'upgrade', 'user' => $this->user->getUsername())); } else { $this->error = "Error: Can't upgrade file : '" . $core_filename; $this->messages->saveNotice('Error', $this->error); $error = new AError ($this->error); $error->toLog()->toDebug(); } } } } /** * method removes non-empty directory (use it carefully) * * @param string $dir * @return boolean */ public function removeDir($dir = '') { if (!$this->session->data['package_info']['ftp']) { // if not ftp if (is_dir($dir)) { $objects = scandir($dir); foreach ($objects as $obj) { if ($obj != "." && $obj != "..") { chmod($dir . "/" . $obj, 0777); $err = is_dir($dir . "/" . $obj) ? $this->removeDir($dir . "/" . $obj) : unlink($dir . "/" . $obj); if (!$err) { $this->error = "Error: Can't to delete file or directory: '" . $dir . "/" . $obj . "'."; $this->messages->saveNotice('Error', $this->error); $error = new AError ($this->error); $error->toLog()->toDebug(); return false; } } } reset($objects); rmdir($dir); return true; } else { return $dir; } } else { $ftp_user = $this->session->data['package_info']['ftp_user']; $ftp_password = $this->session->data['package_info']['ftp_password']; $ftp_port = $this->session->data['package_info']['ftp_port']; $ftp_host = $this->session->data['package_info']['ftp_host']; $dir = $this->session->data['package_info']['ftp_path'] . $this->session->data['package_info']['dst_dir'] . $dir; $fconnect = ftp_connect($ftp_host, $ftp_port); ftp_login($fconnect, $ftp_user, $ftp_password); ftp_pasv($fconnect, true); $this->delete_ftp_dir($fconnect, $dir); ftp_close($fconnect); } return true; } /** * function returns destination directory of extension or some else. * It looking for package_id in code directory of package * @return bool|mixed */ public function getDestinationDirectories() { $package_dirname = $this->session->data['package_info']['package_dir']; $output = array(); if (!file_exists($this->session->data['package_info']['tmp_dir'] . $package_dirname . "/code")) { return false; } else { $dir = $this->session->data['package_info']['tmp_dir'] . $package_dirname . "/code"; $d = array(); while ($dirs = glob($dir . '/*', GLOB_ONLYDIR)) { $dir .= '/*'; if (!$d) { $d = $dirs; } else { $d = array_merge($d, $dirs); } } } if ($d) { foreach ($d as $dir) { $dir = str_replace($this->session->data['package_info']['tmp_dir'] . $package_dirname . "/code/", "", $dir); $output[] = $dir; } } return $output; } /** * @param string $ftp_user * @param string $ftp_password * @param string $ftp_host * @param string $ftp_path * @param int $ftp_port * @return bool */ public function checkFTP($ftp_user, $ftp_password = '', $ftp_host = '', $ftp_path = '', $ftp_port=21) { $this->load->language('tool/package_installer'); if (!$ftp_host) { $ftp_host = 'localhost'; } else { // looking for port number $ftp_host = explode(':',$ftp_host); $ftp_port = (int)$ftp_host[1]; $ftp_host = $ftp_host[0]; } $ftp_port = !$ftp_port ? 21 : $ftp_port; if (!$ftp_user) { $this->error = $this->language->get('error_ftp_user'); return false; } if (!$ftp_password) { $this->error = $this->language->get('error_ftp_password'); return false; } $fconnect = ftp_connect($ftp_host, $ftp_port); if(!$fconnect && $ftp_host=='localhost'){ //check dns perversion :-) $ftp_host = '127.0.0.1'; $fconnect = ftp_connect($ftp_host, $ftp_port); } if ($fconnect) { $login = ftp_login($fconnect, $ftp_user, $ftp_password); if (!$login) { $this->error = $this->language->get('error_ftp_login') . $ftp_host . ':' . $ftp_port; return false; } $ftp_path = !$ftp_path ? $this->_ftp_find_app_root($fconnect) : $ftp_path; // if all fine - write ftp parameters into session $this->session->data['package_info']['ftp'] = true; $this->session->data['package_info']['ftp_user'] = $ftp_user; $this->session->data['package_info']['ftp_password'] = $ftp_password; $this->session->data['package_info']['ftp_host'] = $ftp_host; $this->session->data['package_info']['ftp_port'] = $ftp_port; $this->session->data['package_info']['ftp_path'] = $ftp_path; ftp_close($fconnect); } else { $this->error = $this->language->get('error_ftp_connect'); return false; } return true; } /** * @param resource $fconnect * @return string|bool */ private function _ftp_find_app_root($fconnect) { if (!$fconnect) { return false; } $abs_path = pathinfo($_SERVER[ 'DOCUMENT_ROOT' ].$_SERVER[ 'PHP_SELF' ],PATHINFO_DIRNAME); // first fo all try to change directory //(for case when ftp-user does not locked in his ftp root directory) if(ftp_chdir($fconnect, $abs_path)){ return $abs_path.'/'; }else{ //for ftp chrooted users //get list of directories if ($files = ftp_nlist($fconnect,'.')){ foreach ($files as $file) { if (ftp_size($fconnect, $file) == "-1"){ $ftp_dir_list[] = $file; } } //find ftp-directory name inside absolute path $target_dir = null; if($ftp_dir_list){ foreach($ftp_dir_list as $dir){ if(is_int($pos = strpos($abs_path,$dir))){ $target_dir = substr($abs_path,$pos); break; } } if($target_dir){ return trim($target_dir,'/').'/'; } } } } return false; } /** * Function for moving directory or file via ftp-connection * * @param $fconnect * @param string $local local path to file or directory * @param string $remote_file remote file or directory name * @param string $remote_dir * @return bool */ public function ftp_move($fconnect, $local, $remote_file, $remote_dir) { $local = (string)$local; $remote_file = (string)$remote_file; $remote_dir = (string)$remote_dir; if (!$this->session->data['package_info']['ftp']) { return false; } // if destination folder does not exists - try to create if (!@ftp_chdir($fconnect, $remote_dir)) { $result = ftp_mkdir($fconnect, $remote_dir); if (!$result) { return false; } if(!ftp_chmod($fconnect, 0777, $remote_dir)){ $error = new AError('Cannot to change mode for directory '.$remote_dir); $error->toLog()->toDebug(); } } @ftp_chdir($fconnect, $remote_dir); if (is_dir($local)) { $this->ftp_put_dir($fconnect, $local, $remote_dir); } else { if (!ftp_put($fconnect, $remote_file, $local, FTP_BINARY)) { return false; } $remote_file = $remote_dir . pathinfo($local, PATHINFO_BASENAME); if(pathinfo($remote_file,PATHINFO_BASENAME)=='index.php'){ $chmod_result = ftp_chmod($fconnect, 0755, $remote_file); }else{ $chmod_result = ftp_chmod($fconnect, 0777, $remote_file); } if(!$chmod_result){ $error = new AError('Cannot to change mode for file '.$remote_file); $error->toLog()->toDebug(); } } return true; } /** * method for moving directory via ftp connection * @param resource $conn_id * @param string $src_dir * @param string $dst_dir */ private function ftp_put_dir($conn_id, $src_dir, $dst_dir) { $d = dir($src_dir); while ($file = $d->read()) { // do this for each file in the directory if ($file != "." && $file != "..") { // to prevent an infinite loop if (is_dir($src_dir . "/" . $file)) { // do the following if it is a directory if (!@ftp_chdir($conn_id, $dst_dir . "/" . $file)) { ftp_mkdir($conn_id, $dst_dir . "/" . $file); // create directories that do not yet exist ftp_chmod($conn_id, 0777, $dst_dir . "/" . $file); } $this->ftp_put_dir($conn_id, $src_dir . "/" . $file, $dst_dir . "/" . $file); // recursive part } else { ftp_put($conn_id, $dst_dir . "/" . $file, $src_dir . "/" . $file, FTP_BINARY); // put the files // for index.php do not set 777 permissions because hosting providers will ban it if(pathinfo($file,PATHINFO_BASENAME)=='index.php'){ ftp_chmod($conn_id, 0755, $dst_dir . "/" . $file); }else{ ftp_chmod($conn_id, 0777, $dst_dir . "/" . $file); } } } } $d->close(); } /** * @param resource $conn * @param string $dir * @return bool */ private function delete_ftp_dir($conn, $dir) { $files = ftp_nlist($conn, $dir); if (!$files) { return ftp_rmdir($conn, $dir); } foreach ($files as $file) { $is_dir = ftp_chdir($conn, $file); if ($is_dir) { $this->delete_ftp_dir($conn, $file); } else { ftp_delete($conn, $file); } } ftp_rmdir($conn, $dir); return true; } /** * @param string $extension_id * @param string $type * @param string $version * @param string $install_mode * @return bool */ public function installExtension($extension_id = '', $type = '', $version = '', $install_mode = 'install') { $type = !$type ? $this->session->data['package_info']['package_type'] : $type; $version = !$version ? $this->session->data['package_info']['package_version'] : $version; $extension_id = !$extension_id ? $this->session->data['package_info']['package_id'] : $extension_id; $package_dirname = $this->session->data['package_info']['package_dir']; switch ($type) { case 'extension': case 'extensions': case 'template': case 'payment': case 'shipping': case 'language': // if extensions is not installed yet - install it if ($install_mode == 'install') { $validate = $this->extension_manager->validate($extension_id); $validateErrors = $this->extension_manager->errors; if (!$validate) { $this->error = implode('
', $validateErrors); $err = new AError($this->error); $err->toLog()->toDebug(); return false; } $result = $this->extension_manager->install($extension_id, getExtensionConfigXml($extension_id)); if ($result === false) { return false; } } elseif ($install_mode == 'upgrade'){ $install_upgrade_history = new ADataset('install_upgrade_history', 'admin'); $install_upgrade_history->addRows(array('date_added' => date("Y-m-d H:i:s", time()), 'name' => $extension_id, 'version' => $version, 'backup_file' => '', 'backup_date' => '', 'type' => 'upgrade', 'user' => $this->user->getUsername())); $config = ''; $ext_conf_filename = $this->session->data['package_info']['tmp_dir'] . $package_dirname . '/code/extensions/' . $extension_id . '/config.xml'; if(is_file($ext_conf_filename)){ $config = simplexml_load_file($ext_conf_filename); } $config = !$config ? getExtensionConfigXml($extension_id) : $config; // running sql upgrade script if it exists if (isset($config->upgrade->sql)) { $file = $this->session->data['package_info']['tmp_dir'] . $package_dirname . '/code/extensions/' . $extension_id . '/' . (string)$config->upgrade->sql; $file = !file_exists($file) ? DIR_EXT . $extension_id . '/' . (string)$config->upgrade->sql : $file; if (file_exists($file)) { $this->db->performSql($file); } } // running php install script if it exists if (isset($config->upgrade->trigger)) { $file = $this->session->data['package_info']['tmp_dir'] . $package_dirname . '/code/extensions/' . $extension_id . '/' . (string)$config->upgrade->trigger; $file = !file_exists($file) ? DIR_EXT . $extension_id . '/' . (string)$config->upgrade->sql : $file; if (file_exists($file)) { /** @noinspection PhpIncludeInspection */ include($file); } } $this->extension_manager->editSetting($extension_id, array('license_key' => $this->session->data['package_info']['extension_key'], 'version' => $version)); } break; default: $this->error = 'Unknown extension type: "' . $type . '"'; $err = new AError($this->error); $err->toLog()->toDebug(); return false; break; } return true; } /** * @param SimpleXmlElement $config */ public function upgradeCore($config) { //clear all cache $this->cache->delete('*'); $package_dirname = $this->session->data['package_info']['package_dir']; $package_tmpdir = $this->session->data['package_info']['tmp_dir']; // running sql upgrade script if it exists if (isset($config->upgrade->sql)) { $file = $package_tmpdir . $package_dirname . '/' . (string)$config->upgrade->sql; if (is_file($file)) { $this->db->performSql($file); } } // running php upgrade script if it exists if (isset($config->upgrade->trigger)) { $file = $package_tmpdir . $package_dirname . '/' . (string)$config->upgrade->trigger; if (is_file($file)) { /** @noinspection PhpIncludeInspection */ include($file); } } // write to history $install_upgrade_history = new ADataset('install_upgrade_history', 'admin'); $install_upgrade_history->addRows(array('date_added' => date("Y-m-d H:i:s", time()), 'name' => 'Core upgrade', 'version' => $this->session->data['package_info']['package_version'], 'backup_file' => '', 'backup_date' => '', 'type' => 'upgrade', 'user' => $this->user->getUsername())); } /** * @param string $new_version * @return bool */ public function updateCoreVersion($new_version) { if (!$new_version) { return false; } $new_version = preg_replace('/[^0-9\.]/', '', $new_version); list($master, $minor, $built) = explode(".", $new_version); $content = "session->data['package_info']['ftp']) { file_put_contents(DIR_CORE . 'version.php', $content); } else { file_put_contents($this->session->data['package_info']['tmp_dir'] . 'version.php', $content); $ftp_user = $this->session->data['package_info']['ftp_user']; $ftp_password = $this->session->data['package_info']['ftp_password']; $ftp_port = $this->session->data['package_info']['ftp_port']; $ftp_host = $this->session->data['package_info']['ftp_host']; $fconnect = ftp_connect($ftp_host, $ftp_port); ftp_login($fconnect, $ftp_user, $ftp_password); ftp_pasv($fconnect, true); $this->ftp_move($fconnect, $this->session->data['package_info']['tmp_dir'] . 'version.php', 'version.php', $this->session->data['package_info']['ftp_path'] . 'core/'); ftp_close($fconnect); } return true; } /** * Method change access mode recursively * @param string $path path to directory or file * @param string $filemode * @param string $dirmode * @return void */ public function chmod_R($path, $filemode, $dirmode) { $path = (string)$path; if (is_dir($path)) { if (!chmod($path, $dirmode)) { $dirmode_str = decoct($dirmode); $error_text = "Notice: Failed applying filemode '".$dirmode_str."' on directory '".$path."'.\n"; $error_text .= " `-> the directory '".$path."' will be skipped from recursive chmod.\n"; $this->log->write($error_text); return null; } $dh = opendir($path); while (($file = readdir($dh)) !== false) { if ($file != '.' && $file != '..') { // skip self and parent pointing directories $fullpath = $path . '/' . $file; $this->chmod_R($fullpath, $filemode, $dirmode); } } closedir($dh); } else { //skip if does not exists if (!file_exists($path)) { return null; } if (is_link($path)) { $this->log->write('Notice: Recursive chmod. Symlink '.$path.' is skipped.'); return null; } // for index.php do not set 777 permissions because hosting providers will ban it if(pathinfo($path,PATHINFO_BASENAME)=='index.php' && $filemode==777){ $filemode = 644; } if (!chmod($path, $filemode)) { $filemode_str = decoct($filemode); $this->log->write("Notice: Failed applying filemode ".$filemode_str." on file ".$path."\n"); return null; } } } }