7 private static $temp_extracted_dir;
8 public static $upgrade_available;
9 public static $last_check;
11 public static $latest_version;
12 function __construct()
16 if (!get_config('upgrade'))
18 $config['upgrade'] =[];
19 write_config('upgrade');
22 $tok = split(__DIR__
, '/');
23 unset($tok[count($tok) - 1]);
24 $this->web_dir
= implode('/',$tok).'/';
26 /** prepare the temp directory */
27 $temp_dir = $this->web_dir
."panel_upgrade";
28 $temp_dir .= ($temp_dir[strlen($temp_dir) - 1] != '/') ? "/" : "";
29 if (file_exists($temp_dir)) {
30 deleteDirectoryContents($temp_dir);
33 $mkdir = mkdir($temp_dir, 0755, true);
35 $this->temp_dir
= $mkdir ? $temp_dir : NULL;
36 $this->error
= $mkdir ? NULL : "Could not create directory: $temp_dir";
37 Upgrade::$upgrade_available = false;
39 error_log($this->error);
42 /** Checks for a new upgrade */
43 function checkForNew()
47 $last_check = &$config['upgrade']['last_check'];
48 if (isset($last_check) && time() - $last_check < 300) // only check every 15 mins
50 error_log("Skipping upgrade check
, checked
".time() - $last_check." seconds ago
");
53 error_log(time()." - ".$last_check." = ".time()-$last_check);
54 $apiUrl = "https
://api.github.com/repos/unrealircd/unrealircd-webpanel/releases";
55 $response = file_get_contents($apiUrl, false, stream_context_create(
56 ["http" => ["method" => "GET", "header" => "User-agent: UnrealIRCd Webpanel"]]
59 if ($response === false)
61 $this->error
= "Couldn't check github.";
64 $data = json_decode($response, true);
65 $latest = $data[count($data) - 1];
66 $config['upgrade']['latest_version'] = $latest['tag_name'];
68 $config['upgrade']['download_link'] = $latest['zipball_url'];
69 write_config('upgrade');
70 Upgrade
::$upgrade_available = (float)$latest['tag_name'] > WEBPANEL_VERSION
? true : false;
73 function downloadUpgradeZip()
75 $ch = curl_init(get_config('upgrade::download_link'));
76 $fp = fopen($this->temp_dir
."unrealircd-webpanel-upgrade.zip", 'w+');
78 curl_setopt($ch, CURLOPT_FILE
, $fp);
79 curl_setopt($ch, CURLOPT_TIMEOUT
, 60);
80 curl_setopt($ch, CURLOPT_FOLLOWLOCATION
, true);
81 curl_setopt($ch, CURLOPT_HTTPHEADER
, [
82 'User-Agent: UnrealIRCd Webpanel',
84 $success = curl_exec($ch);
85 $code = curl_getinfo($ch, CURLINFO_HTTP_CODE
);
87 if ($code == "403" || $code == "404")
89 $this->error
="Unable to download";
96 function extractZip() {
97 $zip = new ZipArchive
;
98 if ($zip->open($this->temp_dir
."unrealircd-webpanel-upgrade.zip") === true)
100 $zip->extractTo("$this->temp_dir");
102 unlink($this->temp_dir
."unrealircd-webpanel-upgrade.zip");
103 self
::$temp_extracted_dir = findOnlyDirectory($this->temp_dir
);
104 error_log(self
::$temp_extracted_dir);
110 function cleanupOldFiles()
112 foreach ($this->compareAndGetFilesToDelete() as $file)
114 unlink("$this->web_dir$file");
115 error_log("Deleting
: $file");
118 function compareAndGetFilesToDelete() : array
120 $currentFiles = $this->listFiles($this->web_dir
);
121 $updateFiles = $this->listFiles(self
::$temp_extracted_dir);
122 $filesToDelete = array_diff($currentFiles, $updateFiles);
123 $filesToActuallyDelete = [];
124 error_log("Comparing... Files to delete:");
125 foreach ($filesToDelete as $file)
127 // skip the relevant directories
128 if (str_starts_with($file, "panel_upgrade/")
129 || str_starts_with($file, "vendor/")
130 || str_starts_with($file, "config/")
131 || str_starts_with($file, "data/")
132 || str_starts_with($file, "plugins/"))
134 $filesToActuallyDelete[] = $file;
136 return $filesToActuallyDelete;
139 function extractToWebdir()
141 $zip = new ZipArchive
;
142 if ($zip->open($this->temp_dir
."unrealircd-webpanel-upgrade.zip") === true)
144 $extracted = $zip->extractTo($this->web_dir
);
148 error_log("Cannot extract to web directory. Permission denied.");
158 * Cleans up the extracted update files
161 function cleanupDownloadFiles()
163 $ex_dir = self
::$temp_extracted_dir ?? findOnlyDirectory($this->temp_dir
);
164 deleteDirectoryContents($ex_dir);
168 function listFiles($dir) {
170 $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
171 foreach ($iterator as $file)
175 $f = substr($file->getPathname(), strlen($dir));
176 if ($f[0] == "/") $f = substr($f,1);
177 error_log("$dir => $f");
190 function findOnlyDirectory($topDir) {
191 // Ensure the directory exists and is indeed a directory
192 if (!is_dir($topDir)) {
193 die("The specified path is not a directory
.");
196 // Open the directory
197 $dirHandle = opendir($topDir);
198 if ($dirHandle === false) {
199 die("Unable to open directory
.");
204 // Read through the directory contents
205 while (($entry = readdir($dirHandle)) !== false) {
206 $fullPath = $topDir . DIRECTORY_SEPARATOR . $entry;
207 // Check if the entry is a directory and not . or ..
208 if (is_dir($fullPath) && $entry !== '.' && $entry !== '..') {
209 $directories[] = $fullPath;
213 // Close the directory handle
214 closedir($dirHandle);
216 // Check if there is exactly one directory
217 if (count($directories) === 1) {
218 return $directories[0];
219 } elseif (count($directories) === 0) {
220 return "No directories found after extracting
. Possibly missing php
-zip extention
. Aborting upgrade
.";
222 return "Multiple directories found
. Previous cleanup was unsuccessful
for some reason
, maybe a permissions error
? Aborting upgrade
.";
227 function deleteDirectoryContents($dir) {
228 error_log("Deleting directory contents at
$dir");
230 echo "The provided path is not a directory.";
234 // Open the directory
235 $handle = opendir($dir);
236 if ($handle === false) {
237 echo "Failed to open the directory.";
241 // Loop through the directory contents
242 while (($item = readdir($handle)) !== false) {
243 // Skip the special entries "." and ".."
244 if ($item == "." || $item == "..") {
248 $itemPath = $dir."/".$item;
250 // If the item is a directory, recursively delete its contents
251 if (is_dir($itemPath)) {
252 deleteDirectoryContents($itemPath);
253 // Remove the empty directory
256 // If the item is a file, delete it
261 // Close the directory handle