manager = $manager; $this->containerDir = $containerDir; $this->tmpDir = $tmpDir; } /** * Determine whether downloading is supported * * @return array list of error messages; empty if OK */ public function checkRequirements() { $errors = array(); if (!$this->containerDir || !is_dir($this->containerDir) || !is_writeable($this->containerDir)) { $civicrmDestination = urlencode(CRM_Utils_System::url('civicrm/admin/extensions', 'reset=1')); $url = CRM_Utils_System::url('civicrm/admin/setting/path', "reset=1&civicrmDestination=${civicrmDestination}"); $errors[] = array( 'title' => ts('Directory Unwritable'), //'message' => ts('Your extensions directory: %1 is not web server writable. Please go to the path setting page and correct it.
', 'message' => ts("Your extensions directory is not set or is not writable. Click here to set the extensions directory.", array( //1 => $this->containerDir, 1 => $url, ) ) ); } if (!class_exists('ZipArchive')) { $errors[] = array( 'title' => ts('ZIP Support Required'), 'message' => ts('You will not be able to install extensions at this time because your installation of PHP does not support ZIP archives. Please ask your system administrator to install the standard PHP-ZIP extension.'), ); } if (empty($errors) && ! CRM_Utils_HttpClient::singleton()->isRedirectSupported()) { CRM_Core_Session::setStatus(ts('WARNING: The downloader may be unable to download files which require HTTP redirection. This may be a configuration issue with PHP\'s open_basedir or safe_mode.')); CRM_Core_Error::debug_log_message('WARNING: The downloader may be unable to download files which require HTTP redirection. This may be a configuration issue with PHP\'s open_basedir or safe_mode.'); } return $errors; } /** * Install or upgrade an extension from a remote URL * * @param string $key the name of the extension being installed * @param string $downloadUrl URL of a .zip file * @return bool TRUE for success * @throws CRM_Extension_Exception */ public function download($key, $downloadUrl) { $filename = $this->tmpDir . DIRECTORY_SEPARATOR . $key . '.zip'; $destDir = $this->containerDir . DIRECTORY_SEPARATOR . $key; if (!$downloadUrl) { CRM_Core_Error::fatal('Cannot install this extension - downloadUrl is not set!'); } if (! $this->fetch($downloadUrl, $filename)) { return FALSE; } $extractedZipPath = $this->extractFiles($key, $filename); if (! $extractedZipPath) { return FALSE; } if (! $this->validateFiles($key, $extractedZipPath)) { return FALSE; } $this->manager->replace($extractedZipPath); return TRUE; } /** * Download the remote zipfile. * * @param string $remoteFile URL of a .zip file * @param string $localFile path at which to store the .zip file * @return boolean Whether the download was successful. */ public function fetch($remoteFile, $localFile) { $result = CRM_Utils_HttpClient::singleton()->fetch($remoteFile, $localFile); switch ($result) { case CRM_Utils_HttpClient::STATUS_OK: return TRUE; default: return FALSE; } } /** * Extract an extension from a zip file * * @param string $key the name of the extension being installed; this usually matches the basedir in the .zip * @param string $zipFile the local path to a .zip file * @return string|FALSE zip file path */ public function extractFiles($key, $zipFile) { $config = CRM_Core_Config::singleton(); $zip = new ZipArchive(); $res = $zip->open($zipFile); if ($res === TRUE) { $zipSubDir = CRM_Utils_Zip::guessBasedir($zip, $key); if ($zipSubDir === FALSE) { CRM_Core_Session::setStatus(ts('Unable to extract the extension: bad directory structure'), '', 'error'); return FALSE; } $extractedZipPath = $this->tmpDir . DIRECTORY_SEPARATOR . $zipSubDir; if (is_dir($extractedZipPath)) { if (!CRM_Utils_File::cleanDir($extractedZipPath, TRUE, FALSE)) { CRM_Core_Session::setStatus(ts('Unable to extract the extension: %1 cannot be cleared', array(1 => $extractedZipPath)), ts('Installation Error'), 'error'); return FALSE; } } if (!$zip->extractTo($this->tmpDir)) { CRM_Core_Session::setStatus(ts('Unable to extract the extension to %1.', array(1 => $this->tmpDir)), ts('Installation Error'), 'error'); return FALSE; } $zip->close(); } else { CRM_Core_Session::setStatus(ts('Unable to extract the extension.'), '', 'error'); return FALSE; } return $extractedZipPath; } /** * Validate that $extractedZipPath contains valid for extension $key * * @param $key * @param $extractedZipPath * * @return bool */ function validateFiles($key, $extractedZipPath) { $filename = $extractedZipPath . DIRECTORY_SEPARATOR . CRM_Extension_Info::FILENAME; if (!is_readable($filename)) { CRM_Core_Session::setStatus(ts('Failed reading data from %1 during installation', array(1 => $filename)), ts('Installation Error'), 'error'); return FALSE; } try { $newInfo = CRM_Extension_Info::loadFromFile($filename); } catch (Exception $e) { CRM_Core_Session::setStatus(ts('Failed reading data from %1 during installation', array(1 => $filename)), ts('Installation Error'), 'error'); return FALSE; } if ($newInfo->key != $key) { CRM_Core_Error::fatal('Cannot install - there are differences between extdir XML file and archive XML file!'); } return TRUE; } }