]>
Commit | Line | Data |
---|---|---|
1 | <?php | |
2 | ||
3 | class Upgrade | |
4 | { | |
5 | public $web_dir; | |
6 | private $temp_dir; | |
7 | private static $temp_extracted_dir; | |
8 | public static $upgrade_available; | |
9 | public static $last_check; | |
10 | public $error; | |
11 | public static $latest_version; | |
12 | function __construct() | |
13 | { | |
14 | global $config; | |
15 | read_config_db(); | |
16 | if (!get_config('upgrade')) | |
17 | { | |
18 | $config['upgrade'] =[]; | |
19 | write_config('upgrade'); | |
20 | } | |
21 | ||
22 | $tok = split(__DIR__, '/'); | |
23 | unset($tok[count($tok) - 1]); | |
24 | $this->web_dir = implode('/',$tok).'/'; | |
25 | ||
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); | |
31 | rmdir($temp_dir); | |
32 | } | |
33 | $mkdir = mkdir($temp_dir, 0755, true); | |
34 | ||
35 | $this->temp_dir = $mkdir ? $temp_dir : NULL; | |
36 | $this->error = $mkdir ? NULL : "Could not create directory: $temp_dir"; | |
37 | Upgrade::$upgrade_available = false; | |
38 | if ($this->error) | |
39 | error_log($this->error); | |
40 | } | |
41 | ||
42 | /** Checks for a new upgrade */ | |
43 | function checkForNew() | |
44 | { | |
45 | global $config; | |
46 | read_config_db(); | |
47 | $last_check = &$config['upgrade']['last_check']; | |
48 | if (isset($last_check) && time() - $last_check < 300) // only check every 15 mins | |
49 | { | |
50 | error_log("Skipping upgrade check, checked ".time() - $last_check." seconds ago"); | |
51 | return false; | |
52 | } | |
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"]] | |
57 | )); | |
58 | ||
59 | if ($response === false) | |
60 | { | |
61 | $this->error = "Couldn't check github."; | |
62 | return false; | |
63 | } | |
64 | $data = json_decode($response, true); | |
65 | $latest = $data[count($data) - 1]; | |
66 | $config['upgrade']['latest_version'] = $latest['tag_name']; | |
67 | $last_check = time(); | |
68 | $config['upgrade']['download_link'] = $latest['zipball_url']; | |
69 | write_config('upgrade'); | |
70 | Upgrade::$upgrade_available = (float)$latest['tag_name'] > WEBPANEL_VERSION ? true : false; | |
71 | } | |
72 | ||
73 | function downloadUpgradeZip() | |
74 | { | |
75 | $ch = curl_init(get_config('upgrade::download_link')); | |
76 | $fp = fopen($this->temp_dir."unrealircd-webpanel-upgrade.zip", 'w+'); | |
77 | ||
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', | |
83 | ]); | |
84 | $success = curl_exec($ch); | |
85 | $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); | |
86 | ||
87 | if ($code == "403" || $code == "404") | |
88 | { | |
89 | $this->error ="Unable to download"; | |
90 | } | |
91 | curl_close($ch); | |
92 | fclose($fp); | |
93 | ||
94 | return $success; | |
95 | } | |
96 | function extractZip() { | |
97 | $zip = new ZipArchive; | |
98 | if ($zip->open($this->temp_dir."unrealircd-webpanel-upgrade.zip") === true) | |
99 | { | |
100 | $zip->extractTo("$this->temp_dir"); | |
101 | $zip->close(); | |
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); | |
105 | return true; | |
106 | } else { | |
107 | return false; | |
108 | } | |
109 | } | |
110 | function cleanupOldFiles() | |
111 | { | |
112 | foreach ($this->compareAndGetFilesToDelete() as $file) | |
113 | unlink($file); | |
114 | } | |
115 | function compareAndGetFilesToDelete() : array | |
116 | { | |
117 | $currentFiles = $this->listFiles($this->web_dir); | |
118 | $updateFiles = $this->listFiles(self::$temp_extracted_dir); | |
119 | $filesToDelete = array_diff($currentFiles, $updateFiles); | |
120 | $filesToActuallyDelete = []; | |
121 | error_log("Comparing... Files to delete:"); | |
122 | foreach ($filesToDelete as $file) | |
123 | { | |
124 | // skip the relevant directories | |
125 | if (str_starts_with($file, "panel_upgrade/") | |
126 | || str_starts_with($file, "vendor/") | |
127 | || str_starts_with($file, "config/") | |
128 | || str_starts_with($file, "data/") | |
129 | || str_starts_with($file, "plugins/")) | |
130 | continue; | |
131 | $filesToActuallyDelete[] = $file; | |
132 | } | |
133 | return $filesToActuallyDelete; | |
134 | } | |
135 | ||
136 | function extractToWebdir() | |
137 | { | |
138 | $zip = new ZipArchive; | |
139 | if ($zip->open($this->temp_dir."unrealircd-webpanel-upgrade.zip") === true) | |
140 | { | |
141 | $extracted = $zip->extractTo(str_replace("//","/",get_config('base_url'))); | |
142 | $zip->close(); | |
143 | if (!$extracted) | |
144 | { | |
145 | error_log("Cannot extract to web directory. Permission denied."); | |
146 | return false; | |
147 | } | |
148 | return true; | |
149 | } else { | |
150 | return false; | |
151 | } | |
152 | } | |
153 | ||
154 | /** | |
155 | * Cleans up the extracted update files | |
156 | * @return void | |
157 | */ | |
158 | function cleanupDownloadFiles() | |
159 | { | |
160 | $ex_dir = self::$temp_extracted_dir ?? findOnlyDirectory($this->temp_dir); | |
161 | deleteDirectoryContents($ex_dir); | |
162 | rmdir($ex_dir); | |
163 | } | |
164 | ||
165 | function listFiles($dir) { | |
166 | $files = []; | |
167 | $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir)); | |
168 | foreach ($iterator as $file) | |
169 | { | |
170 | if ($file->isFile()) | |
171 | { | |
172 | $f = substr($file->getPathname(), strlen($dir)); | |
173 | if ($f[0] == "/") $f = substr($f,1); | |
174 | error_log("$dir => $f"); | |
175 | ||
176 | ||
177 | ||
178 | ||
179 | $files[] = $f; | |
180 | } | |
181 | } | |
182 | return $files; | |
183 | } | |
184 | } | |
185 | ||
186 | ||
187 | function findOnlyDirectory($topDir) { | |
188 | // Ensure the directory exists and is indeed a directory | |
189 | if (!is_dir($topDir)) { | |
190 | die("The specified path is not a directory."); | |
191 | } | |
192 | ||
193 | // Open the directory | |
194 | $dirHandle = opendir($topDir); | |
195 | if ($dirHandle === false) { | |
196 | die("Unable to open directory."); | |
197 | } | |
198 | ||
199 | $directories = []; | |
200 | ||
201 | // Read through the directory contents | |
202 | while (($entry = readdir($dirHandle)) !== false) { | |
203 | $fullPath = $topDir . DIRECTORY_SEPARATOR . $entry; | |
204 | // Check if the entry is a directory and not . or .. | |
205 | if (is_dir($fullPath) && $entry !== '.' && $entry !== '..') { | |
206 | $directories[] = $fullPath; | |
207 | } | |
208 | } | |
209 | ||
210 | // Close the directory handle | |
211 | closedir($dirHandle); | |
212 | ||
213 | // Check if there is exactly one directory | |
214 | if (count($directories) === 1) { | |
215 | return $directories[0]; | |
216 | } elseif (count($directories) === 0) { | |
217 | return "No directories found after extracting. Possibly missing php-zip extention. Aborting upgrade."; | |
218 | } else { | |
219 | return "Multiple directories found. Previous cleanup was unsuccessful for some reason, maybe a permissions error? Aborting upgrade."; | |
220 | } | |
221 | } | |
222 | ||
223 | ||
224 | function deleteDirectoryContents($dir) { | |
225 | error_log("Deleting directory contents at $dir"); | |
226 | if (!is_dir($dir)) { | |
227 | echo "The provided path is not a directory."; | |
228 | return false; | |
229 | } | |
230 | ||
231 | // Open the directory | |
232 | $handle = opendir($dir); | |
233 | if ($handle === false) { | |
234 | echo "Failed to open the directory."; | |
235 | return false; | |
236 | } | |
237 | ||
238 | // Loop through the directory contents | |
239 | while (($item = readdir($handle)) !== false) { | |
240 | // Skip the special entries "." and ".." | |
241 | if ($item == "." || $item == "..") { | |
242 | continue; | |
243 | } | |
244 | ||
245 | $itemPath = $dir."/".$item; | |
246 | ||
247 | // If the item is a directory, recursively delete its contents | |
248 | if (is_dir($itemPath)) { | |
249 | deleteDirectoryContents($itemPath); | |
250 | // Remove the empty directory | |
251 | rmdir($itemPath); | |
252 | } else { | |
253 | // If the item is a file, delete it | |
254 | unlink($itemPath); | |
255 | } | |
256 | } | |
257 | ||
258 | // Close the directory handle | |
259 | closedir($handle); | |
260 | ||
261 | return true; | |
262 | } |