PluginProbe
JetBackup – Backup, Restore & Migrate / 3.1.21.3
JetBackup – Backup, Restore & Migrate v3.1.21.3
3.1.23.6 3.1.23.5 3.1.23.3 3.1.22.4 3.1.22.3 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.8.1 1.4.9 1.5.0 1.5.1 1.5.1.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 1.6.10 All 82 releases
backup / src / JetBackup / JetBackupLinux / JetBackupLinux.php
JetBackupLinux.php
398 lines 13.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace JetBackup\JetBackupLinux;
4
5 use JetBackup\BackupJob\BackupJob;
6 use JetBackup\Config\System;
7 use JetBackup\Data\Engine;
8 use JetBackup\Entities\Util;
9 use JetBackup\Exception\DBException;
10 use JetBackup\Exception\JetBackupLinuxException;
11 use JetBackup\Exception\QueueException;
12 use JetBackup\Factory;
13 use JetBackup\JetBackup;
14 use JetBackup\Queue\Queue;
15 use JetBackup\Queue\QueueItem;
16 use JetBackup\Queue\QueueItemReindex;
17 use JetBackup\Snapshot\Snapshot;
18 use JetBackup\SocketAPI\Client\Client;
19 use JetBackup\SocketAPI\Exception\SocketAPIException;
20 use JetBackup\SocketAPI\SocketAPI;
21 use JetBackup\Wordpress\Wordpress;
22 use SleekDB\Exceptions\InvalidArgumentException;
23 use SleekDB\Exceptions\IOException;
24
25 if (!defined( '__JETBACKUP__')) die('Direct access is not allowed');
26
27 class JetBackupLinux {
28
29 const BACKUP_TYPE_ACCOUNT = 1;
30
31 const BACKUP_TYPE_ACCOUNT_CONFIG = 1<<0;
32 const BACKUP_TYPE_ACCOUNT_HOMEDIR = 1<<1;
33 const BACKUP_TYPE_ACCOUNT_DATABASES = 1<<2;
34 const BACKUP_TYPE_ACCOUNT_EMAILS = 1<<3;
35 const BACKUP_TYPE_ACCOUNT_CRON_JOBS = 1<<4;
36 const BACKUP_TYPE_ACCOUNT_DOMAINS = 1<<5;
37 const BACKUP_TYPE_ACCOUNT_CERTIFICATES = 1<<6;
38 const BACKUP_TYPE_ACCOUNT_DATABASE_USERS = 1<<7;
39 const BACKUP_TYPE_ACCOUNT_FTP = 1<<8;
40 const BACKUP_TYPE_ACCOUNT_FULL =
41 self::BACKUP_TYPE_ACCOUNT_CONFIG |
42 self::BACKUP_TYPE_ACCOUNT_HOMEDIR |
43 self::BACKUP_TYPE_ACCOUNT_DATABASES |
44 self::BACKUP_TYPE_ACCOUNT_EMAILS |
45 self::BACKUP_TYPE_ACCOUNT_CRON_JOBS |
46 self::BACKUP_TYPE_ACCOUNT_DOMAINS |
47 self::BACKUP_TYPE_ACCOUNT_CERTIFICATES |
48 self::BACKUP_TYPE_ACCOUNT_DATABASE_USERS |
49 self::BACKUP_TYPE_ACCOUNT_FTP;
50
51 const BACKUP_STRUCTURE_INCREMENTAL = 1;
52 const BACKUP_STRUCTURE_ARCHIVED = 2;
53 const BACKUP_STRUCTURE_COMPRESSED = 4;
54
55 const BACKUP_STRUCTURE_DEDUPLICATION = 8;
56
57
58
59 const QUEUE_STATUS_RESTORE_ACCOUNT_CONFIG = 30;
60 const QUEUE_STATUS_RESTORE_ACCOUNT_DOMAINS = 31;
61 const QUEUE_STATUS_RESTORE_ACCOUNT_CERTIFICATES = 32;
62 const QUEUE_STATUS_RESTORE_ACCOUNT_FTP = 33;
63 const QUEUE_STATUS_RESTORE_ACCOUNT_CRON_JOBS = 34;
64 const QUEUE_STATUS_RESTORE_ACCOUNT_IMPORTING_DATABASES = 35;
65 const QUEUE_STATUS_RESTORE_ACCOUNT_DATABASES = 36;
66 const QUEUE_STATUS_RESTORE_ACCOUNT_DATABASE_USERS = 37;
67 const QUEUE_STATUS_RESTORE_ACCOUNT_HOMEDIR = 38;
68 const QUEUE_STATUS_RESTORE_ACCOUNT_EMAILS = 39;
69 const QUEUE_STATUS_RESTORE_ACCOUNT_POST_RESTORE = 40;
70 const QUEUE_STATUS_RESTORE_ACCOUNT_PRE_RESTORE = 41;
71
72 const QUEUE_STATUS_RESTORE_MAPPING = [
73 self::QUEUE_STATUS_RESTORE_ACCOUNT_IMPORTING_DATABASES => Queue::STATUS_RESTORE_JB_IMPORTING_DB,
74 self::QUEUE_STATUS_RESTORE_ACCOUNT_DATABASES => Queue::STATUS_RESTORE_JB_DATABASES,
75 self::QUEUE_STATUS_RESTORE_ACCOUNT_DATABASE_USERS => Queue::STATUS_RESTORE_JB_DATABASE_USERS,
76 self::QUEUE_STATUS_RESTORE_ACCOUNT_HOMEDIR => Queue::STATUS_RESTORE_JB_HOMEDIR,
77 self::QUEUE_STATUS_RESTORE_ACCOUNT_POST_RESTORE => Queue::STATUS_RESTORE_JB_POST,
78 self::QUEUE_STATUS_RESTORE_ACCOUNT_PRE_RESTORE => Queue::STATUS_RESTORE_JB_PRE,
79 ];
80
81 const QUEUE_STATUS_RESTORE_ACCOUNT_NAMES = [
82 self::QUEUE_STATUS_RESTORE_ACCOUNT_CONFIG => "Restoring Panel Configurations",
83 self::QUEUE_STATUS_RESTORE_ACCOUNT_DOMAINS => "Restoring Domains and DNS",
84 self::QUEUE_STATUS_RESTORE_ACCOUNT_CERTIFICATES => "Restoring SSL Certificates",
85 self::QUEUE_STATUS_RESTORE_ACCOUNT_FTP => "Restoring FTP Accounts",
86 self::QUEUE_STATUS_RESTORE_ACCOUNT_CRON_JOBS => "Restoring Cron Jobs",
87 self::QUEUE_STATUS_RESTORE_ACCOUNT_IMPORTING_DATABASES => "Importing databases data",
88 self::QUEUE_STATUS_RESTORE_ACCOUNT_DATABASES => "Restoring Databases",
89 self::QUEUE_STATUS_RESTORE_ACCOUNT_DATABASE_USERS => "Restoring Database Users",
90 self::QUEUE_STATUS_RESTORE_ACCOUNT_HOMEDIR => "Restoring Home Directory files",
91 self::QUEUE_STATUS_RESTORE_ACCOUNT_EMAILS => "Restoring Email Accounts",
92 self::QUEUE_STATUS_RESTORE_ACCOUNT_POST_RESTORE => "Performing post restore actions",
93 self::QUEUE_STATUS_RESTORE_ACCOUNT_PRE_RESTORE => "Performing pre restore actions",
94 ];
95
96
97 const QUEUE_STATUS_COMPLETED = 100;
98 const QUEUE_STATUS_PARTIALLY_COMPLETED = 101;
99 const QUEUE_STATUS_FAILED = 102;
100 const QUEUE_STATUS_ABORTED = 103;
101 const QUEUE_STATUS_NEVER_FINISHED = 104;
102
103 const QUEUE_STATUS_MAPPING = [
104 self::QUEUE_STATUS_COMPLETED => Queue::STATUS_DONE,
105 self::QUEUE_STATUS_PARTIALLY_COMPLETED => Queue::STATUS_PARTIALLY,
106 self::QUEUE_STATUS_FAILED => Queue::STATUS_FAILED,
107 self::QUEUE_STATUS_ABORTED => Queue::STATUS_ABORTED,
108 self::QUEUE_STATUS_NEVER_FINISHED => Queue::STATUS_NEVER_FINISHED,
109 ];
110
111 const QUEUE_TYPE_RESTORE = 1<<1;
112
113 const MINIMUM_VERSION = '5.3.15';
114
115 private function __construct() {}
116
117 public static function isEnabled():bool {
118 return Factory::getSettingsGeneral()->isJBIntegrationEnabled() && self::isInstalled();
119 }
120
121 /**
122 * Return true will allow to check if socket file exist, we will not continue to check for socket file is this return false
123 * @return bool
124 */
125 private static function isOpenBaseDir(): bool
126 {
127 $openBasedir = ini_get('open_basedir');
128 if (!$openBasedir) return true;
129
130 $socket = realpath(Client::SOCKET_FILE) ?: Client::SOCKET_FILE;
131
132 foreach (explode(PATH_SEPARATOR, $openBasedir) as $allowed) {
133 $allowed = trim($allowed);
134 if ($allowed === '') continue;
135
136 // Normalize to "dir/" form
137 $allowedReal = realpath($allowed) ?: $allowed;
138 $allowedReal = rtrim($allowedReal, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
139
140 if (str_starts_with($socket, $allowedReal)) return true;
141 }
142
143 return false;
144 }
145
146
147 private static function isSocket(): bool {
148 return file_exists(Client::SOCKET_FILE);
149 }
150
151
152 /**
153 * Using this in the background for different implementations, just to verify if JBLinux is installed
154 * @return bool
155 */
156 public static function isInstalled():bool {
157 return !System::isWindowsOS() && self::isOpenBaseDir() && self::isSocket();
158 }
159
160 /**
161 * Using this for Ajax validation
162 * @return void
163 * @throws JetBackupLinuxException
164 */
165 public static function checkRequirements():void {
166 // Check for blocking requirements first (these prevent further checks)
167 if(System::isWindowsOS()) throw new JetBackupLinuxException("This feature is not supported in Windows operating system");
168 if(!self::isOpenBaseDir()) throw new JetBackupLinuxException("'open_basedir' restriction is enabled, make sure " . dirname(Client::SOCKET_FILE) . " is available");
169
170 // Collect all missing function errors at once for better user experience
171 $missingFunctions = [];
172 if(!Util::has_posix_getpwuid()) $missingFunctions[] = 'posix_getpwuid';
173 if(!Util::has_posix_getgrgid()) $missingFunctions[] = 'posix_getgrgid';
174 if(!Util::has_posix_geteuid()) $missingFunctions[] = 'posix_geteuid';
175 if(!function_exists('socket_connect')) $missingFunctions[] = 'socket_connect';
176
177 if(!empty($missingFunctions)) {
178 $functionList = implode(', ', $missingFunctions);
179 $plural = count($missingFunctions) > 1 ? 's' : '';
180 throw new JetBackupLinuxException("Required function{$plural} '{$functionList}' disabled or not installed. Enable to allow Socket API.");
181 }
182
183 if (!self::isSocket()) throw new JetBackupLinuxException("Cannot find JetBackup Linux socket");
184
185 try {
186 $response = SocketAPI::api('getMyAccount')->execute();
187 } catch(SocketAPIException $e) {
188 throw new JetBackupLinuxException($e->getMessage());
189 }
190
191 if(!isset($response['system']['version'])) throw new JetBackupLinuxException("Failed fetching JetBackup linux version");
192 if(version_compare(self::MINIMUM_VERSION, $response['system']['version'], '>')) throw new JetBackupLinuxException("JetBackup linux version is not compatible ({$response['system']['version']}), Minimum required version is " . self::MINIMUM_VERSION);
193 }
194
195 /**
196 * @return array|mixed
197 * @throws JetBackupLinuxException
198 * @throws SocketAPIException
199 */
200 public static function getAccountInfo() {
201 return Query::api('getMyAccount')->execute();
202 }
203
204 /**
205 * @param string $id
206 * @param string $path
207 * @param int $limit
208 * @param int $skip
209 * @param array $sort
210 *
211 * @return array|mixed
212 * @throws JetBackupLinuxException
213 * @throws SocketAPIException
214 */
215 public static function fileManager(string $id, string $path, int $limit=0, int $skip=0, array $sort=[]) {
216 $query = Query::api('fileManager')
217 ->arg('type', 'backup')
218 ->arg('_id', $id)
219 ->arg('path', $path);
220
221 if($limit || $skip) $query->limit($limit, $skip);
222
223 foreach($sort as $key => $direction) {
224 if($direction > 0) $query->sortAsc($key);
225 else if($direction < 0) $query->sortDesc($key);
226 }
227
228 return $query->execute();
229 }
230
231 /**
232 * @param array $items
233 * @param array $files
234 * @param array $options
235 *
236 * @return array|mixed
237 * @throws JetBackupLinuxException
238 * @throws SocketAPIException
239 */
240 public static function addQueueItems(array $items, array $files=[], array $options=[]) {
241
242 // Force exclude plugin's main folder and datadir
243 if(!isset($options['exclude'])) $options['exclude'] = [];
244 $options['exclude'][] = Factory::getWPHelper()->getWordPressRelativePublicDir() .
245 JetBackup::SEP . Wordpress::WP_CONTENT .
246 JetBackup::SEP . Wordpress::WP_PLUGINS .
247 JetBackup::SEP . JetBackup::PLUGIN_NAME;
248 if (Factory::getLocations()->getPublicDataDir()) {
249 $options['exclude'][] = Factory::getWPHelper()->getWordPressRelativePublicDir() . JetBackup::SEP . Factory::getLocations()->getPublicDataDir();
250 } else {
251 $options['exclude'][] = Factory::getLocations()->getRelativeDataDir();
252 }
253
254 return Query::api('addQueueItems')
255 ->arg('type', self::QUEUE_TYPE_RESTORE)
256 ->arg('items', $items)
257 ->arg('files', $files)
258 ->arg('options', $options)
259 ->execute();
260 }
261
262
263 /**
264 * @param array $sort
265 *
266 * @return array
267 * @throws JetBackupLinuxException|SocketAPIException
268 */
269 public static function listQueueGroups(array $sort=[]):array {
270 $query = Query::api('listQueueGroups')
271 ->arg('type', self::QUEUE_TYPE_RESTORE)
272 ->limit(9999999);
273
274 foreach($sort as $key => $direction) {
275 if($direction > 0) $query->sortAsc($key);
276 else if($direction < 0) $query->sortDesc($key);
277 }
278
279 $response = $query->execute();
280
281 return $response['groups'];
282 }
283
284 /**
285 * @param string $group_id
286 *
287 * @return array
288 * @throws JetBackupLinuxException|SocketAPIException
289 */
290 public static function getQueueGroup(string $group_id) {
291 return Query::api('getQueueGroup')->arg('_id', $group_id)->execute();
292 }
293
294 /**
295 * @param string $group_id
296 * @param array $sort
297 *
298 * @return array
299 * @throws JetBackupLinuxException|SocketAPIException
300 */
301 public static function listQueueItems(string $group_id, array $sort=[]):array {
302
303 $query = Query::api('listQueueItems')
304 ->arg('group_id', $group_id)
305 ->limit(9999999);
306
307 foreach($sort as $key => $direction) {
308 if($direction > 0) $query->sortAsc($key);
309 else if($direction < 0) $query->sortDesc($key);
310 }
311
312 $response = $query->execute();
313
314 return $response['items'];
315 }
316
317 /**
318 * @param array $sort
319 *
320 * @return array
321 * @throws JetBackupLinuxException
322 * @throws SocketAPIException
323 */
324 public static function listBackups(array $sort=[], int $limit=25, int $skip=0):array {
325
326 $query = Query::api('listBackupsWithItems')
327 ->arg('type', self::BACKUP_TYPE_ACCOUNT)
328 ->arg('structure', self::BACKUP_STRUCTURE_INCREMENTAL)
329 ->limit($limit, $skip);
330
331 foreach($sort as $key => $direction) {
332 if($direction > 0) $query->sortAsc($key);
333 else if($direction < 0) $query->sortDesc($key);
334 }
335
336 $response = $query->execute();
337
338 return $response['backups'];
339 }
340
341 /**
342 * @return void
343 * @throws IOException
344 * @throws InvalidArgumentException
345 * @throws DBException
346 */
347 public static function deleteSnapshots():void {
348
349 $list = Snapshot::query()
350 ->where([Engine::ENGINE, '=', Engine::ENGINE_JB])
351 ->getQuery()
352 ->fetch();
353
354 if (empty($list)) return;
355
356 foreach ($list as $item) {
357 $snapshot = new Snapshot($item[JetBackup::ID_FIELD]);
358 if(!$snapshot->getId()) continue;
359 $snapshot->delete();
360 }
361 }
362
363 /**
364 * @return void
365 * @throws IOException
366 * @throws InvalidArgumentException
367 * @throws QueueException
368 * @throws JetBackupLinuxException
369 */
370 public static function addToQueue():void {
371
372 if(!Factory::getSettingsGeneral()->isJBIntegrationEnabled()) throw new JetBackupLinuxException("JetBackup Linux integration is disabled");
373 if(!self::isInstalled()) throw new JetBackupLinuxException("JetBackup Linux integration is not installed");
374
375 $reindex = new QueueItemReindex();
376 $queue_item = QueueItem::prepare();
377 $queue_item->setType(Queue::QUEUE_TYPE_REINDEX);
378 $queue_item->setItemId(0);
379 $queue_item->setItemData($reindex);
380
381 Queue::addToQueue($queue_item);
382 }
383
384 public static function prepareFilesForRestore($homedir_snapID, $fileManger): array {
385
386 $public_dir = trim(Factory::getWPHelper()->getWordPressHomedir(true), JetBackup::SEP);
387 $files = [];
388 if(!sizeof($fileManger)) return $files;
389
390 foreach ($fileManger as $file) {
391 $path = JetBackup::SEP . $public_dir . JetBackup::SEP . trim($file['path'], JetBackup::SEP);
392 $files[$homedir_snapID][$path] = $file['type'];
393 }
394
395 return $files;
396
397 }
398 }