<?php
namespace WpDownloader\Component;
use WpDownloader\Utility;
use WpDownloader\Log;
abstract class Base
{
public $temporaryDirectory = '/tmp/wpinstaller';
public $localPath; // Where the WP files are stored locally
public $saveDataFile = '.wpdownloader.json'; // What file, inside the $localPath we will use to store data about this
public $tempPath; // Where to put files before checksums are verified
public $distPath = '/usr/local/cpanel/3rdparty/wpinstaller/files/plugins'; // Where we put files to be installed from
public $version; // Holds the version number of the current component
public $componentData; // Info about what the plugin is, where to obtain it, etc...
public $slug; // Borrowing this term from the WP vocabulary; it is a way to identify a component.
abstract public function getDestinationPath();
public function cleanupTmp()
{
$tmp_dir = $this->temporaryDirectory;
$files = scandir($this->temporaryDirectory);
foreach ($files as $file) {
if (preg_match('/\.tmp/', $file)) {
$path = $tmp_dir . '/'. $file;
Log::write("\tRemoving old temp file $path");
unlink($path);
}
}
}
public function getSourcePath()
{
return "{$this->localPath}/{$this->getSlug()}";
}
public function getActivationFile()
{
return false;
}
public function isMuPlugin()
{
return false;
}
public function isPlugin()
{
return false;
}
public function getSlug()
{
return $this->slug;
}
public function thawLocalData()
{
$localDataFilePath = $this->getLocalDataFilePath();
if (!file_exists($localDataFilePath)) {
return [];
}
$localJson = file_get_contents($localDataFilePath);
return json_decode($localJson, 1) ?: [];
}
public function getCurrentLocalVersion()
{
$data = $this->thawLocalData();
if (empty($data['version'])) {
return $this->getDefaultVersionNumber();
}
return $data['version'];
}
public function getDefaultVersionNumber()
{
return "0.0.0";
}
/**
* Merges a new set of data into the existing JSON file, overwriting recursively if necessary.
*
* @param array $toAppend The new keys and values to merge into the existing saved JSON data
*/
public function appendLocalData($toAppend)
{
$existingData = $this->thawLocalData();
$newData = array_merge_recursive($existingData, $toAppend);
$localDataFilePath = $this->getLocalDataFilePath();
$result = file_put_contents($localDataFilePath, json_encode($newData, JSON_PRETTY_PRINT));
if (!$result) {
Log::throwException("Failed to save updated data to {$localDataFilePath}");
}
return $newData;
}
public function getLocalDataFilePath()
{
return "{$this->localPath}/{$this->saveDataFile}";
}
public function downloadFile($url, $file, $dlappend=null)
{
if ($dlappend===null) {
$dlappend = gmdate("Y_m_d_h.i.s");
}
$tmp_file = $file . ".tmp." . $dlappend;
$tmpDir = dirname($tmp_file);
if (!is_dir($tmpDir)) {
if (!mkdir($tmpDir, 0755, true)) {
Log::throwException("[ERROR] Unable to create temporary download dir: {$tmpDir}");
}
}
Log::write("\tAttemtping to download $url to $tmp_file");
$fp = fopen($tmp_file, 'w');
if (!$fp) {
Log::throwException("[ERROR] Unable to acquire file handle for {$tmp_file}");
}
if (!flock($fp, LOCK_EX)) {
Log::throwException("[ERROR] Unable to acquire flock for {$tmp_file}");
}
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_FILE, $fp);
//$data = curl_exec($ch);
$ret = curl_exec($ch);
curl_close($ch);
flock($fp, LOCK_UN);
fclose($fp);
if ($ret !== true) {
Log::throwException("[ERROR] Unable to download $tmp_file");
unlink($tmp_file);
return $ret;
}
// Check if file is zip
if (preg_match('/\.zip$/', $file)) {
$ret = $this->verifyZipIntegrity($tmp_file);
} else {
// Do other checks
Log::write("\t[WARN]\tNot a Zip file, skipping integrity check ($tmp_file)");
$ret = true;
}
if ($ret !== true) {
unlink($tmp_file);
Log::throwException("[ERROR] Corrupt file $tmp_file");
}
if (rename($tmp_file, $file)) {
Log::write("[OK]\t$tmp_file renamed to $file");
} else {
Log::throwException("[ERROR] failed to rename $tmp_file to $file");
}
if (!$file || !file_exists($file)) {
Log::throwException("Downloaded failed or local file {$file} does not exist.");
}
return $file;
}
public function verifyZipIntegrity($file)
{
$zip = new \ZipArchive;
$res = $zip->open($file, \ZIPARCHIVE::CHECKCONS);
if ($res === true) {
/*
for ($i = 0; $i < $zip->numFiles; $i++) {
$filename = $zip->getNameIndex($i);
Log::write($filename);
}
*/
Log::write("[OK]\tZip integirty good ($res) ($file)");
return $res;
} else {
Log::write("[ERROR]\tZip integirty issue ($res) ($file)");
return $res;
}
}
protected function extractArchive($inputFilePath)
{
Log::write("Opening archive {$inputFilePath}");
$zip = new \ZipArchive;
if (!$zip->open($inputFilePath)) {
Log::throwException("Failed to open handle to zip file {$inputFilePath}");
}
if (!is_dir($this->tempPath)) {
if (!mkdir($this->tempPath, 0755, true)) {
Log::throwException("Failed to create temporary directory for extraction {$this->tempPath}");
}
}
Log::write("Extracting {$inputFilePath} to {$this->tempPath}");
$zip->extractTo($this->tempPath);
$zip->close();
return $this->tempPath;
}
public function commitTempToPersistent()
{
Log::write("Committing temp files {$this->tempPath} to persist at {$this->localPath}");
if (file_exists($this->localPath) && is_dir($this->localPath)) {
Log::write("Deleting directory {$this->localPath} to make room for new files.");
Utility::deleteDirectory($this->localPath);
}
return rename($this->tempPath, $this->localPath);
}
public function hasUpdateAvailable()
{
$localVersion = $this->getCurrentLocalVersion();
$remoteVersion = $this->getLatestVersion();
Log::write("Local version: $localVersion Remote version: $remoteVersion. Slug: {$this->getSlug()}");
return version_compare($localVersion, $remoteVersion, '<');
}
public function setData($data)
{
$this->componentData = $data;
$this->afterSetData();
}
public function afterSetData()
{
return true;
}
}