PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backups, Restore, Migration & Clone / 4.11.0
WP STAGING – WordPress Backups, Restore, Migration & Clone v4.11.0
4.11.0 4.10.0 4.9.5 4.9.4 4.9.3 4.9.2 4.9.1 4.9.0 4.8.1 trunk 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.10.0 3.2.0 3.3.1 3.3.2 3.3.3 3.4.1 3.4.3 3.5.0 3.6.0 3.7.1 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.9.0 3.9.1 3.9.2 3.9.3 3.9.4 4.0.0 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.7.2 4.7.3 4.8.0
wp-staging / Backup / BackupHeader.php
wp-staging / Backup Last commit date
Ajax 1 day ago BackgroundProcessing 1 day ago Dto 1 day ago Entity 1 day ago Exceptions 1 day ago FileHeader 1 day ago Interfaces 1 day ago Job 1 day ago Request 1 day ago Service 1 day ago Storage 1 day ago Task 1 day ago Traits 1 day ago Utils 1 day ago AfterRestore.php 1 day ago BackupDeleter.php 1 day ago BackupDownload.php 1 day ago BackupFileIndex.php 1 day ago BackupGlitchReason.php 1 day ago BackupHeader.php 1 day ago BackupNextOffer.php 1 day ago BackupRepairer.php 1 day ago BackupRetentionHandler.php 1 day ago BackupScheduler.php 1 day ago BackupServiceProvider.php 1 day ago BackupValidator.php 1 day ago BeforeUpdateRowStatus.php 1 day ago FileHeader.php 1 day ago FileHeaderAttribute.php 1 day ago UpdateProtectionPausedNotice.php 1 day ago WithBackupIdentifier.php 1 day ago
BackupHeader.php
439 lines
1 <?php
2
3 namespace WPStaging\Backup;
4
5 use WPStaging\Backup\Traits\EncodingErrorHandler;
6 use WPStaging\Framework\Filesystem\FileObject;
7 use WPStaging\Framework\Utils\DataEncoder;
8 use WPStaging\Framework\Utils\Version;
9
10
11
12
13
14
15
16 class BackupHeader
17 {
18 use EncodingErrorHandler;
19
20
21 const WPSTG_SQL_BACKUP_DUMP_HEADER = "-- WP Staging SQL Backup Dump\n";
22
23
24
25
26
27 const HEADER_SIZE = 512;
28
29
30
31
32 const HEADER_IN_USE_HEX_FORMAT = '48888';
33
34
35
36
37
38
39 const MAGIC = "wpstg";
40
41
42
43
44
45 const MAGIC_SIZE = 8;
46
47
48
49
50
51
52 const MIN_BACKUP_VERSION = '2.0.0';
53
54
55
56
57
58
59
60
61
62
63
64 const BACKUP_VERSION = '2.1.0';
65
66
67
68
69
70
71
72 const COPYRIGHT_TEXT = '57502053746167696e672066696c6520666f726d61742062792052656e65204865726d656e617520262048617373616e20536861666971756520323032342f30';
73
74 /**
75 * Copyright text size
76 * @var int
77 */
78 const COPYRIGHT_TEXT_SIZE = 128;
79
80
81
82
83 private $magic;
84
85
86
87
88 private $backupVersion;
89
90
91
92
93 private $filesIndexStartOffset = 0;
94
95
96
97
98 private $filesIndexEndOffset = 0;
99
100
101
102
103 private $metadataStartOffset = 0;
104
105
106
107
108 private $metadataEndOffset = 0;
109
110
111
112
113 private $copyrightText;
114
115
116 private $encoder;
117
118
119 private $versionUtil;
120
121
122
123
124
125
126 public function __construct(DataEncoder $encoder, Version $versionUtil)
127 {
128 $this->encoder = $encoder;
129 $this->versionUtil = $versionUtil;
130 $this->backupVersion = $this->versionUtil->convertStringFormatToIntFormat(self::BACKUP_VERSION);
131 }
132
133
134
135
136
137
138
139 private function logBackupHeaderEncodingError(string $errorMessage)
140 {
141 $context = [
142 'backupVersion' => $this->backupVersion,
143 'filesIndexStartOffset' => $this->filesIndexStartOffset,
144 'filesIndexEndOffset' => $this->filesIndexEndOffset,
145 'metadataStartOffset' => $this->metadataStartOffset,
146 'metadataEndOffset' => $this->metadataEndOffset,
147 ];
148
149 $this->logEncodingErrorWithContext(
150 $errorMessage,
151 $context,
152 'DataEncoder error in BackupHeader::getHeader(): %s. Using fallback values to continue backup.'
153 );
154 }
155
156
157
158
159
160
161 private function applyBackupHeaderFallbackValues()
162 {
163 if ($this->backupVersion === null) {
164 $this->backupVersion = $this->versionUtil->convertStringFormatToIntFormat(self::BACKUP_VERSION);
165 }
166
167 if ($this->filesIndexStartOffset === null) {
168 $this->filesIndexStartOffset = 0;
169 }
170
171 if ($this->filesIndexEndOffset === null) {
172 $this->filesIndexEndOffset = 0;
173 }
174
175 if ($this->metadataStartOffset === null) {
176 $this->metadataStartOffset = 0;
177 }
178
179 if ($this->metadataEndOffset === null) {
180 $this->metadataEndOffset = 0;
181 }
182 }
183
184
185
186
187
188
189
190
191
192
193 public function getBackupVersion(): int
194 {
195 return $this->backupVersion;
196 }
197
198
199
200
201
202
203
204
205
206
207 public function getFormattedBackupVersion(): string
208 {
209 return $this->versionUtil->convertIntFormatToStringFormat($this->backupVersion);
210 }
211
212 public function getMetadataStartOffset(): int
213 {
214 return $this->metadataStartOffset;
215 }
216
217 public function setMetadataStartOffset(int $metadataStartOffset): BackupHeader
218 {
219 $this->metadataStartOffset = $metadataStartOffset;
220 return $this;
221 }
222
223 public function getMetadataEndOffset(): int
224 {
225 return $this->metadataEndOffset;
226 }
227
228 public function setMetadataEndOffset(int $metadataEndOffset): BackupHeader
229 {
230 $this->metadataEndOffset = $metadataEndOffset;
231 return $this;
232 }
233
234 public function getFilesIndexStartOffset(): int
235 {
236 return $this->filesIndexStartOffset;
237 }
238
239 public function setFilesIndexStartOffset(int $filesIndexStartOffset): BackupHeader
240 {
241 $this->filesIndexStartOffset = $filesIndexStartOffset;
242 return $this;
243 }
244
245 public function getFilesIndexEndOffset(): int
246 {
247 return $this->filesIndexEndOffset;
248 }
249
250 public function setFilesIndexEndOffset(int $filesIndexEndOffset): BackupHeader
251 {
252 $this->filesIndexEndOffset = $filesIndexEndOffset;
253 return $this;
254 }
255
256
257
258
259
260
261
262 public function readFromPath(string $backupFilePath): BackupHeader
263 {
264 if (!file_exists($backupFilePath)) {
265 throw new \RuntimeException('Backup file not found');
266 }
267
268 $file = new FileObject($backupFilePath, FileObject::MODE_READ);
269 return $this->readFromFileObject($file);
270 }
271
272
273
274
275
276
277
278 public function readFromFileObject(FileObject $file): BackupHeader
279 {
280 if ($file->getSize() < self::HEADER_SIZE) {
281 throw new \RuntimeException('Invalid v2 format backup file');
282 }
283
284 $file->seek(0);
285 $rawHeader = $file->fread(self::HEADER_SIZE);
286
287 return $this->setupBackupHeaderFromRaw($rawHeader);
288 }
289
290
291
292
293
294
295
296 public function setupBackupHeaderFromRaw(string $rawHeader): BackupHeader
297 {
298 $this->magic = rtrim(substr($rawHeader, 0, self::MAGIC_SIZE));
299 $this->copyrightText = substr($rawHeader, self::HEADER_SIZE - self::COPYRIGHT_TEXT_SIZE, self::COPYRIGHT_TEXT_SIZE);
300
301
302 $dynamicHeader = substr($rawHeader, self::MAGIC_SIZE, $this->getHeaderInUseSize());
303 $headerIntData = $this->encoder->hexToIntArray(self::HEADER_IN_USE_HEX_FORMAT, $dynamicHeader);
304
305 $this->backupVersion = $headerIntData[0];
306 $this->filesIndexStartOffset = $headerIntData[1];
307 $this->filesIndexEndOffset = $headerIntData[2];
308 $this->metadataStartOffset = $headerIntData[3];
309 $this->metadataEndOffset = $headerIntData[4];
310
311 return $this;
312 }
313
314 public function isValidBackupHeader(): bool
315 {
316 if ($this->magic !== self::MAGIC) {
317 return false;
318 }
319
320 if ($this->copyrightText !== self::COPYRIGHT_TEXT) {
321 return false;
322 }
323
324 return version_compare($this->getFormattedBackupVersion(), self::MIN_BACKUP_VERSION, '>=');
325 }
326
327 public function getHeader(): string
328 {
329 try {
330 $encodedData = $this->encoder->intArrayToHex(
331 self::HEADER_IN_USE_HEX_FORMAT,
332 [
333 $this->backupVersion,
334 $this->filesIndexStartOffset,
335 $this->filesIndexEndOffset,
336 $this->metadataStartOffset,
337 $this->metadataEndOffset,
338 ]
339 );
340 } catch (\InvalidArgumentException $e) {
341
342 $this->logBackupHeaderEncodingError($e->getMessage());
343
344
345 $this->applyBackupHeaderFallbackValues();
346
347
348 $encodedData = $this->encoder->intArrayToHex(
349 self::HEADER_IN_USE_HEX_FORMAT,
350 [
351 $this->backupVersion,
352 $this->filesIndexStartOffset,
353 $this->filesIndexEndOffset,
354 $this->metadataStartOffset,
355 $this->metadataEndOffset,
356 ]
357 );
358 }
359
360 return sprintf(
361 '%s%s%s%s',
362 str_pad(self::MAGIC, self::MAGIC_SIZE, "\0", STR_PAD_RIGHT),
363 $encodedData,
364 bin2hex(str_pad("", $this->getUnusedBytesSize(), "\0", STR_PAD_RIGHT)),
365 self::COPYRIGHT_TEXT
366 );
367 }
368
369
370
371
372
373 public function updateHeader(string $backupFilePath)
374 {
375 $header = $this->getHeader();
376 $file = new FileObject($backupFilePath, 'r+');
377 $file->seek(0);
378 $file->fwrite($header);
379 $file = null;
380 }
381
382
383
384
385
386
387 public function verifyV1FormatHeader(string $content): bool
388 {
389 if (empty($content)) {
390 return false;
391 }
392
393 $wpstgBackupHeaderFileContent = self::WPSTG_SQL_BACKUP_DUMP_HEADER;
394 $headerToVerifyLength = strlen($wpstgBackupHeaderFileContent);
395 if (substr($wpstgBackupHeaderFileContent, 0, $headerToVerifyLength) === substr($content, 0, $headerToVerifyLength)) {
396 return true;
397 }
398
399 $wpstgBackupHeaderFile = WPSTG_RESOURCES_DIR . 'wpstgBackupHeader.txt';
400 if (!file_exists($wpstgBackupHeaderFile)) {
401 return true;
402 }
403
404 $wpstgBackupHeaderFileContent = file_get_contents($wpstgBackupHeaderFile);
405 $headerToVerifyLength = self::HEADER_SIZE;
406 if (!empty($wpstgBackupHeaderFileContent) && substr($wpstgBackupHeaderFileContent, 0, $headerToVerifyLength) === substr($content, 0, $headerToVerifyLength)) {
407 return true;
408 }
409
410 return false;
411 }
412
413 public function getV1FormatHeader(): string
414 {
415 $wpstgBackupHeaderFile = WPSTG_RESOURCES_DIR . 'wpstgBackupHeader.txt';
416
417 if (!file_exists($wpstgBackupHeaderFile)) {
418 return "";
419 }
420
421 return file_get_contents($wpstgBackupHeaderFile);
422 }
423
424 private function getHeaderInUseSize(): int
425 {
426 $size = 0;
427 for ($i = 0; $i < strlen(self::HEADER_IN_USE_HEX_FORMAT); $i++) {
428 $size += intval(substr(self::HEADER_IN_USE_HEX_FORMAT, $i, 1));
429 }
430
431 return $size * 2;
432 }
433
434 private function getUnusedBytesSize(): int
435 {
436 return (self::HEADER_SIZE - $this->getHeaderInUseSize() - self::MAGIC_SIZE - self::COPYRIGHT_TEXT_SIZE) / 2;
437 }
438 }
439