This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /** |
||
4 | * Represents a file archive of database and/or assets extracted from |
||
5 | * a specific Deploynaut environment. |
||
6 | * |
||
7 | * The model can also represent a request to upload a file later, |
||
8 | * through offline processes like mailing a DVD. In order to associate |
||
9 | * and authenticate those requests easily, an upload token is generated for every archive. |
||
10 | * |
||
11 | * The "OriginalEnvironment" points to original source of this snapshot |
||
12 | * (the one it was backed up from). It will be empty if the snapshot has been created with offline process. |
||
13 | * |
||
14 | * The "Environment" denotes the ownership of the snapshot. It will be initially set to match the |
||
15 | * "OriginalEnvironment", but can be changed later. During the offline process the ownership can be set up |
||
16 | * arbitrarily. |
||
17 | * |
||
18 | * When moving snapshots, the file always remains in its initial location. |
||
19 | * |
||
20 | * The archive can have associations to {@link DNDataTransfer}: |
||
21 | * - Zero transfers if a manual upload was requested, but not fulfilled yet |
||
22 | * - One transfer with Direction=get for a backup from an environment |
||
23 | * - One or more transfers with Direction=push for a restore to an environment |
||
24 | * |
||
25 | * The "Author" is either the person creating the archive through a "backup" operation, |
||
26 | * the person uploading through a web form, or the person requesting a manual upload. |
||
27 | * |
||
28 | * The "Mode" is what the "Author" said the file includes (either 'only assets', 'only |
||
29 | * database', or both). This is used in the ArchiveList.ss template. |
||
30 | * |
||
31 | * @property Varchar $UploadToken |
||
32 | * @property Varchar $ArchiveFileHash |
||
33 | * @property Enum $Mode |
||
34 | * @property Boolean $IsBackup |
||
35 | * @property Boolean $IsManualUpload |
||
36 | * |
||
37 | * @method Member Author() |
||
38 | * @property int $AuthorID |
||
39 | * @method DNEnvironment OriginalEnvironment() |
||
40 | * @property int $OriginalEnvironmentID |
||
41 | * @method DNEnvironment Environment() |
||
42 | * @property int $EnvironmentID |
||
43 | * @method File ArchiveFile() |
||
44 | * @property int $ArchiveFileID |
||
45 | * |
||
46 | * @method ManyManyList DataTransfers() |
||
47 | * |
||
48 | */ |
||
49 | class DNDataArchive extends DataObject { |
||
0 ignored issues
–
show
The property $has_many is not named in camelCase.
This check marks property names that have not been written in camelCase. In camelCase names are written without any punctuation, the start of each new word being marked
by a capital letter. Thus the name database connection string becomes ![]() The property $singular_name is not named in camelCase.
This check marks property names that have not been written in camelCase. In camelCase names are written without any punctuation, the start of each new word being marked
by a capital letter. Thus the name database connection string becomes ![]() The property $plural_name is not named in camelCase.
This check marks property names that have not been written in camelCase. In camelCase names are written without any punctuation, the start of each new word being marked
by a capital letter. Thus the name database connection string becomes ![]() The property $summary_fields is not named in camelCase.
This check marks property names that have not been written in camelCase. In camelCase names are written without any punctuation, the start of each new word being marked
by a capital letter. Thus the name database connection string becomes ![]() The property $searchable_fields is not named in camelCase.
This check marks property names that have not been written in camelCase. In camelCase names are written without any punctuation, the start of each new word being marked
by a capital letter. Thus the name database connection string becomes ![]() The property $_cache_can_restore is not named in camelCase.
This check marks property names that have not been written in camelCase. In camelCase names are written without any punctuation, the start of each new word being marked
by a capital letter. Thus the name database connection string becomes ![]() The property $_cache_can_download is not named in camelCase.
This check marks property names that have not been written in camelCase. In camelCase names are written without any punctuation, the start of each new word being marked
by a capital letter. Thus the name database connection string becomes ![]() |
|||
50 | |||
51 | private static $db = array( |
||
52 | 'UploadToken' => 'Varchar(8)', |
||
53 | "Mode" => "Enum('all, assets, db', '')", |
||
54 | "IsBackup" => "Boolean", |
||
55 | "IsManualUpload" => "Boolean", |
||
56 | ); |
||
57 | |||
58 | private static $has_one = array( |
||
59 | 'Author' => 'Member', |
||
60 | 'OriginalEnvironment' => 'DNEnvironment', |
||
61 | 'Environment' => 'DNEnvironment', |
||
62 | 'ArchiveFile' => 'File' |
||
63 | ); |
||
64 | |||
65 | private static $has_many = array( |
||
66 | 'DataTransfers' => 'DNDataTransfer', |
||
67 | ); |
||
68 | |||
69 | private static $singular_name = 'Data Archive'; |
||
0 ignored issues
–
show
|
|||
70 | |||
71 | private static $plural_name = 'Data Archives'; |
||
72 | |||
73 | private static $summary_fields = array( |
||
74 | 'Created' => 'Created', |
||
75 | 'Author.Title' => 'Author', |
||
76 | 'Environment.Project.Name' => 'Project', |
||
77 | 'OriginalEnvironment.Name' => 'Origin', |
||
78 | 'Environment.Name' => 'Environment', |
||
79 | 'ArchiveFile.Name' => 'File', |
||
80 | ); |
||
81 | |||
82 | private static $searchable_fields = array( |
||
83 | 'Environment.Project.Name' => array( |
||
84 | 'title' => 'Project', |
||
85 | ), |
||
86 | 'OriginalEnvironment.Name' => array( |
||
87 | 'title' => 'Origin', |
||
88 | ), |
||
89 | 'Environment.Name' => array( |
||
90 | 'title' => 'Environment', |
||
91 | ), |
||
92 | 'UploadToken' => array( |
||
93 | 'title' => 'Upload Token', |
||
94 | ), |
||
95 | 'Mode' => array( |
||
96 | 'title' => 'Mode', |
||
97 | ), |
||
98 | ); |
||
99 | |||
100 | private static $_cache_can_restore = array(); |
||
101 | |||
102 | private static $_cache_can_download = array(); |
||
103 | |||
104 | public static function get_mode_map() { |
||
105 | return array( |
||
106 | 'all' => 'Database and Assets', |
||
107 | 'db' => 'Database only', |
||
108 | 'assets' => 'Assets only', |
||
109 | ); |
||
110 | } |
||
111 | |||
112 | /** |
||
113 | * Returns a unique token to correlate an offline item (posted DVD) |
||
114 | * with a specific archive placeholder. |
||
115 | * |
||
116 | * @return string |
||
117 | */ |
||
118 | public static function generate_upload_token($chars = 8) { |
||
119 | $generator = new RandomGenerator(); |
||
120 | return strtoupper(substr($generator->randomToken(), 0, $chars)); |
||
121 | } |
||
122 | |||
123 | public function onBeforeWrite() { |
||
124 | if(!$this->AuthorID) { |
||
125 | $this->AuthorID = Member::currentUserID(); |
||
126 | } |
||
127 | |||
128 | parent::onBeforeWrite(); |
||
129 | } |
||
130 | |||
131 | public function onAfterDelete() { |
||
132 | $file = $this->ArchiveFile(); |
||
133 | if ($file && $file->exists()) { |
||
134 | $file->delete(); |
||
135 | } |
||
136 | } |
||
137 | |||
138 | public function getCMSFields() { |
||
139 | $fields = parent::getCMSFields(); |
||
140 | $fields->removeByName('OriginalEnvironmentID'); |
||
141 | $fields->removeByName('EnvironmentID'); |
||
142 | $fields->removeByName('ArchiveFile'); |
||
143 | $fields->addFieldsToTab( |
||
144 | 'Root.Main', |
||
145 | array( |
||
146 | new ReadonlyField('ProjectName', 'Project', $this->Environment()->Project()->Name), |
||
147 | new ReadonlyField('OriginalEnvironmentName', 'OriginalEnvironment', $this->OriginalEnvironment()->Name), |
||
148 | new ReadonlyField('EnvironmentName', 'Environment', $this->Environment()->Name), |
||
149 | $linkField = new ReadonlyField( |
||
150 | 'DataArchive', |
||
151 | 'Archive File', |
||
152 | sprintf( |
||
153 | '<a href="%s">%s</a>', |
||
154 | $this->ArchiveFile()->AbsoluteURL, |
||
155 | $this->ArchiveFile()->Filename |
||
156 | ) |
||
157 | ), |
||
158 | new GridField( |
||
159 | 'DataTransfers', |
||
160 | 'Transfers', |
||
161 | $this->DataTransfers() |
||
162 | ), |
||
163 | ) |
||
164 | ); |
||
165 | $linkField->dontEscape = true; |
||
166 | $fields = $fields->makeReadonly(); |
||
167 | |||
168 | return $fields; |
||
169 | } |
||
170 | |||
171 | public function getDefaultSearchContext() { |
||
172 | $context = parent::getDefaultSearchContext(); |
||
173 | $context->getFields()->dataFieldByName('Mode')->setHasEmptyDefault(true); |
||
174 | |||
175 | return $context; |
||
176 | } |
||
177 | |||
178 | /** |
||
179 | * Calculates and returns a human-readable size of this archive file. If the file exists, it will determine |
||
180 | * whether to display the output in bytes, kilobytes, megabytes, or gigabytes. |
||
181 | * |
||
182 | * @return string The human-readable size of this archive file |
||
183 | */ |
||
184 | public function FileSize() { |
||
185 | if($this->ArchiveFile()->exists()) { |
||
186 | return $this->ArchiveFile()->getSize(); |
||
187 | } else { |
||
188 | return "N/A"; |
||
189 | } |
||
190 | } |
||
191 | |||
192 | public function getModeNice() { |
||
193 | if($this->Mode == 'all') { |
||
194 | return 'database and assets'; |
||
195 | } else { |
||
196 | return $this->Mode; |
||
197 | } |
||
198 | } |
||
199 | |||
200 | /** |
||
201 | * Some archives don't have files attached to them yet, |
||
202 | * because a file has been posted offline and is waiting to be uploaded |
||
203 | * against this "archive placeholder". |
||
204 | * |
||
205 | * @return boolean |
||
206 | */ |
||
207 | public function isPending() { |
||
208 | return !($this->ArchiveFileID); |
||
209 | } |
||
210 | |||
211 | /** |
||
212 | * Inferred from both restore and backup permissions. |
||
213 | * |
||
214 | * @param Member|null $member The {@link Member} object to test against. |
||
215 | */ |
||
216 | public function canView($member = null) { |
||
217 | return ($this->canRestore($member) || $this->canDownload($member)); |
||
218 | } |
||
219 | |||
220 | /** |
||
221 | * Whether a {@link Member} can restore this archive to an environment. |
||
222 | * This only needs to be checked *once* per member and environment. |
||
223 | * |
||
224 | * @param Member|null $member The {@link Member} object to test against. |
||
225 | * @return true if $member (or the currently logged in member if null) can upload this archive |
||
226 | */ |
||
227 | View Code Duplication | public function canRestore($member = null) { |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
228 | $memberID = $member ? $member->ID : Member::currentUserID(); |
||
229 | if(!$memberID) { |
||
230 | return false; |
||
231 | } |
||
232 | |||
233 | $key = $memberID . '-' . $this->EnvironmentID; |
||
234 | if(!isset(self::$_cache_can_restore[$key])) { |
||
235 | self::$_cache_can_restore[$key] = $this->Environment()->canUploadArchive($member); |
||
236 | } |
||
237 | |||
238 | return self::$_cache_can_restore[$key]; |
||
239 | } |
||
240 | |||
241 | /** |
||
242 | * Whether a {@link Member} can download this archive to their PC. |
||
243 | * This only needs to be checked *once* per member and environment. |
||
244 | * |
||
245 | * @param Member|null $member The {@link Member} object to test against. |
||
246 | * @return true if $member (or the currently logged in member if null) can download this archive |
||
247 | */ |
||
248 | View Code Duplication | public function canDownload($member = null) { |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
249 | $memberID = $member ? $member->ID : Member::currentUserID(); |
||
250 | if(!$memberID) { |
||
251 | return false; |
||
252 | } |
||
253 | |||
254 | $key = $memberID . '-' . $this->EnvironmentID; |
||
255 | if(!isset(self::$_cache_can_download[$key])) { |
||
256 | self::$_cache_can_download[$key] = $this->Environment()->canDownloadArchive($member); |
||
257 | } |
||
258 | return self::$_cache_can_download[$key]; |
||
259 | } |
||
260 | |||
261 | /** |
||
262 | * Whether a {@link Member} can delete this archive from staging area. |
||
263 | * |
||
264 | * @param Member|null $member The {@link Member} object to test against. |
||
265 | * @return boolean if $member (or the currently logged in member if null) can delete this archive |
||
266 | */ |
||
267 | public function canDelete($member = null) { |
||
268 | return $this->Environment()->canDeleteArchive($member); |
||
269 | } |
||
270 | |||
271 | /** |
||
272 | * Check if this member can move archive into the environment. |
||
273 | * |
||
274 | * @param DNEnvironment $targetEnv Environment to check. |
||
275 | * @param Member|null $member The {@link Member} object to test against. If null, uses Member::currentMember(); |
||
276 | * |
||
277 | * @return boolean true if $member can upload archives linked to this environment, false if they can't. |
||
278 | */ |
||
279 | public function canMoveTo($targetEnv, $member = null) { |
||
280 | if($this->Environment()->Project()->ID != $targetEnv->Project()->ID) { |
||
281 | // We don't permit moving snapshots between projects at this stage. |
||
282 | return false; |
||
283 | } |
||
284 | |||
285 | if(!$member) { |
||
286 | $member = Member::currentUser(); |
||
287 | } |
||
288 | |||
289 | // Must be logged in to check permissions |
||
290 | if(!$member) { |
||
291 | return false; |
||
292 | } |
||
293 | |||
294 | // Admin can always move. |
||
295 | if(Permission::checkMember($member, 'ADMIN')) { |
||
296 | return true; |
||
297 | } |
||
298 | |||
299 | // Checks if the user can actually access the archive. |
||
300 | if(!$this->canDownload($member)) { |
||
301 | return false; |
||
302 | } |
||
303 | |||
304 | // Hooks into ArchiveUploaders permission to prevent proliferation of permission checkboxes. |
||
305 | // Bypasses the quota check - we don't need to check for it as long as we move the snapshot within the project. |
||
306 | return $targetEnv->ArchiveUploaders()->byID($member->ID) |
||
307 | || $member->inGroups($targetEnv->ArchiveUploaderGroups()); |
||
308 | } |
||
309 | |||
310 | /** |
||
311 | * Finds all environments within this project where the archive can be moved to. |
||
312 | * Excludes current environment automatically. |
||
313 | * |
||
314 | * @return ArrayList List of valid environments. |
||
315 | */ |
||
316 | public function validTargetEnvironments() { |
||
317 | $archive = $this; |
||
318 | $envs = $this->Environment()->Project()->DNEnvironmentList() |
||
319 | ->filterByCallback(function($item) use ($archive) { |
||
320 | return $archive->EnvironmentID != $item->ID && $archive->canMoveTo($item); |
||
321 | }); |
||
322 | |||
323 | return $envs; |
||
324 | } |
||
325 | |||
326 | /** |
||
327 | * Returns a unique filename, including project/environment/timestamp details. |
||
328 | * @return string |
||
329 | */ |
||
330 | public function generateFilename(\DNDataTransfer $dataTransfer) { |
||
331 | $generator = new RandomGenerator(); |
||
332 | $filter = FileNameFilter::create(); |
||
333 | |||
334 | return sprintf( |
||
335 | '%s-%s-%s-%s-%s', |
||
336 | $filter->filter(strtolower($this->OriginalEnvironment()->Project()->Name)), |
||
337 | $filter->filter(strtolower($this->OriginalEnvironment()->Name)), |
||
338 | $dataTransfer->Mode, |
||
339 | date('Ymd'), |
||
340 | sha1($generator->generateEntropy()) |
||
341 | ); |
||
342 | } |
||
343 | |||
344 | /** |
||
345 | * Returns a path unique to a specific transfer, including project/environment details. |
||
346 | * Does not create the path on the filesystem. Can be used to store files related to this transfer. |
||
347 | * |
||
348 | * @param DNDataTransfer |
||
349 | * @return string Absolute file path |
||
350 | */ |
||
351 | public function generateFilepath(\DNDataTransfer $dataTransfer) { |
||
352 | $data = DNData::inst(); |
||
353 | $transferDir = $data->getDataTransferDir(); |
||
354 | $filter = FileNameFilter::create(); |
||
355 | |||
356 | return sprintf('%s/%s/%s/transfer-%s/', |
||
357 | $transferDir, |
||
358 | $filter->filter(strtolower($this->OriginalEnvironment()->Project()->Name)), |
||
359 | $filter->filter(strtolower($this->OriginalEnvironment()->Name)), |
||
360 | $dataTransfer->ID |
||
361 | ); |
||
362 | } |
||
363 | |||
364 | /** |
||
365 | * Attach an sspak file path to this archive and associate the transfer. |
||
366 | * Does the job of creating a {@link File} record, and setting correct paths into the assets directory. |
||
367 | * |
||
368 | * @param string $sspakFilepath |
||
369 | * @param DNDataTransfer $dataTransfer |
||
370 | * @return bool |
||
371 | */ |
||
372 | public function attachFile($sspakFilepath, DNDataTransfer $dataTransfer) { |
||
373 | $sspakFilepath = ltrim( |
||
374 | str_replace( |
||
375 | array(ASSETS_PATH, realpath(ASSETS_PATH)), |
||
376 | '', |
||
377 | $sspakFilepath |
||
378 | ), |
||
379 | DIRECTORY_SEPARATOR |
||
380 | ); |
||
381 | |||
382 | $folder = Folder::find_or_make(dirname($sspakFilepath)); |
||
383 | $file = new File(); |
||
384 | $file->Name = basename($sspakFilepath); |
||
385 | $file->Filename = $sspakFilepath; |
||
386 | $file->ParentID = $folder->ID; |
||
387 | $file->write(); |
||
388 | |||
389 | // "Status" will be updated by the job execution |
||
390 | $dataTransfer->write(); |
||
391 | |||
392 | $this->ArchiveFileID = $file->ID; |
||
393 | $this->DataTransfers()->add($dataTransfer); |
||
394 | $this->write(); |
||
395 | |||
396 | return true; |
||
397 | } |
||
398 | |||
399 | /** |
||
400 | * Extract the current sspak contents into the given working directory. |
||
401 | * This also extracts the assets and database and puts them into |
||
402 | * <workingdir>/database.sql and <workingdir>/assets, respectively. |
||
403 | * |
||
404 | * @param string|null $workingDir The path to extract to |
||
405 | * @throws RuntimeException |
||
406 | * @return bool |
||
407 | */ |
||
408 | public function extractArchive($workingDir = null) { |
||
409 | if(!is_dir($workingDir)) { |
||
410 | mkdir($workingDir, 0700, true); |
||
411 | } |
||
412 | |||
413 | $cleanupFn = function() use($workingDir) { |
||
414 | $process = new AbortableProcess(sprintf('rm -rf %s', escapeshellarg($workingDir))); |
||
415 | $process->setTimeout(120); |
||
416 | $process->run(); |
||
417 | }; |
||
418 | |||
419 | // Extract *.sspak to a temporary location |
||
420 | $sspakFilename = $this->ArchiveFile()->FullPath; |
||
421 | $process = new AbortableProcess(sprintf( |
||
422 | 'tar -xf %s --directory %s', |
||
423 | escapeshellarg($sspakFilename), |
||
424 | escapeshellarg($workingDir) |
||
425 | )); |
||
426 | $process->setTimeout(3600); |
||
427 | $process->run(); |
||
428 | if(!$process->isSuccessful()) { |
||
429 | $cleanupFn(); |
||
430 | throw new RuntimeException(sprintf('Could not extract the sspak file: %s', $process->getErrorOutput())); |
||
431 | } |
||
432 | |||
433 | // Extract database.sql.gz to <workingdir>/database.sql |
||
434 | View Code Duplication | if(file_exists($workingDir . DIRECTORY_SEPARATOR . 'database.sql.gz')) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
435 | $process = new AbortableProcess('gunzip database.sql.gz', $workingDir); |
||
436 | $process->setTimeout(3600); |
||
437 | $process->run(); |
||
438 | if(!$process->isSuccessful()) { |
||
439 | $cleanupFn(); |
||
440 | throw new RuntimeException(sprintf('Could not extract the db archive: %s', $process->getErrorOutput())); |
||
441 | } |
||
442 | } |
||
443 | |||
444 | // Extract assets.tar.gz to <workingdir>/assets/ |
||
445 | View Code Duplication | if(file_exists($workingDir . DIRECTORY_SEPARATOR . 'assets.tar.gz')) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
446 | $process = new AbortableProcess('tar xzf assets.tar.gz', $workingDir); |
||
447 | $process->setTimeout(3600); |
||
448 | $process->run(); |
||
449 | if(!$process->isSuccessful()) { |
||
450 | $cleanupFn(); |
||
451 | throw new RuntimeException(sprintf('Could not extract the assets archive: %s', $process->getErrorOutput())); |
||
452 | } |
||
453 | } |
||
454 | |||
455 | return true; |
||
456 | } |
||
457 | |||
458 | /** |
||
459 | * Validate that an sspak contains the correct content. |
||
460 | * |
||
461 | * For example, if the user uploaded an sspak containing just the db, but declared in the form |
||
462 | * that it contained db+assets, then the archive is not valid. |
||
463 | * |
||
464 | * @param string|null $mode "db", "assets", or "all". This is the content we're checking for. Default to the archive setting |
||
0 ignored issues
–
show
|
|||
465 | * @return ValidationResult |
||
466 | */ |
||
467 | public function validateArchiveContents($mode = null) { |
||
468 | $mode = $mode ?: $this->Mode; |
||
469 | $result = new ValidationResult(); |
||
470 | |||
471 | $file = $this->ArchiveFile()->FullPath; |
||
472 | |||
473 | if(!is_readable($file)) { |
||
474 | $result->error(sprintf('SSPak file "%s" cannot be read.', $file)); |
||
475 | return $result; |
||
476 | } |
||
477 | |||
478 | $process = new AbortableProcess(sprintf('tar -tf %s', escapeshellarg($file))); |
||
479 | $process->setTimeout(120); |
||
480 | $process->run(); |
||
481 | if(!$process->isSuccessful()) { |
||
482 | throw new RuntimeException(sprintf('Could not list files in archive: %s', $process->getErrorOutput())); |
||
483 | } |
||
484 | |||
485 | $output = explode(PHP_EOL, $process->getOutput()); |
||
486 | $files = array_filter($output); |
||
487 | |||
488 | if(in_array($mode, array('all', 'db')) && !in_array('database.sql.gz', $files)) { |
||
489 | $result->error('The snapshot is missing the database.'); |
||
490 | return $result; |
||
491 | } |
||
492 | |||
493 | if(in_array($mode, array('all', 'assets')) && !in_array('assets.tar.gz', $files)) { |
||
494 | $result->error('The snapshot is missing assets.'); |
||
495 | return $result; |
||
496 | } |
||
497 | |||
498 | return $result; |
||
499 | } |
||
500 | |||
501 | /** |
||
502 | * Given a path that already exists and contains an extracted sspak, including |
||
503 | * the assets, fix all of the file permissions so they're in a state ready to |
||
504 | * be pushed to remote servers. |
||
505 | * |
||
506 | * Normally, command line tar will use permissions found in the archive, but will |
||
507 | * substract the user's umask from them. This has a potential to create unreadable |
||
508 | * files, e.g. cygwin on Windows will pack files with mode 000, hence why this fix |
||
509 | * is necessary. |
||
510 | * |
||
511 | * @param string|null $workingDir The path of where the sspak has been extracted to |
||
512 | * @throws RuntimeException |
||
513 | * @return bool |
||
514 | */ |
||
515 | public function fixArchivePermissions($workingDir) { |
||
516 | $fixCmds = array( |
||
517 | // The directories need to have permissions changed one by one (hence the ; instead of +), |
||
518 | // otherwise we might end up having no +x access to a directory deeper down. |
||
519 | sprintf('find %s -type d -exec chmod 755 {} \;', escapeshellarg($workingDir)), |
||
520 | sprintf('find %s -type f -exec chmod 644 {} +', escapeshellarg($workingDir)) |
||
521 | ); |
||
522 | |||
523 | foreach($fixCmds as $cmd) { |
||
524 | $process = new AbortableProcess($cmd); |
||
525 | $process->setTimeout(3600); |
||
526 | $process->run(); |
||
527 | if(!$process->isSuccessful()) { |
||
528 | throw new RuntimeException($process->getErrorOutput()); |
||
529 | } |
||
530 | } |
||
531 | |||
532 | return true; |
||
533 | } |
||
534 | |||
535 | /** |
||
536 | * Given extracted sspak contents, create an sspak from it |
||
537 | * and overwrite the current ArchiveFile with it's contents. |
||
538 | * Use GZIP=-1 for less compression on assets, which are already |
||
539 | * heavily compressed to begin with. |
||
540 | * |
||
541 | * @param string|null $workingDir The path of where the sspak has been extracted to |
||
542 | * @return bool |
||
543 | */ |
||
544 | public function setArchiveFromFiles($workingDir) { |
||
545 | $commands = array(); |
||
546 | if($this->Mode == 'db') { |
||
547 | if (file_exists($workingDir . '/database.sql')) { |
||
548 | $commands[] = 'gzip database.sql'; |
||
549 | } |
||
550 | $commands[] = sprintf('tar -cf %s database.sql.gz', $this->ArchiveFile()->FullPath); |
||
551 | $commands[] = 'rm -f database.sql.gz'; |
||
552 | } elseif($this->Mode == 'assets') { |
||
553 | $commands[] = 'GZIP=-1 tar --dereference -czf assets.tar.gz assets'; |
||
554 | $commands[] = sprintf('tar -cf %s assets.tar.gz', $this->ArchiveFile()->FullPath); |
||
555 | $commands[] = 'rm -f assets.tar.gz'; |
||
556 | } else { |
||
557 | if (file_exists($workingDir . '/database.sql')) { |
||
558 | $commands[] = 'gzip database.sql'; |
||
559 | } |
||
560 | $commands[] = 'GZIP=-1 tar --dereference -czf assets.tar.gz assets'; |
||
561 | $commands[] = sprintf('tar -cf %s database.sql.gz assets.tar.gz', $this->ArchiveFile()->FullPath); |
||
562 | $commands[] = 'rm -f database.sql.gz assets.tar.gz'; |
||
563 | } |
||
564 | |||
565 | $process = new AbortableProcess(implode(' && ', $commands), $workingDir); |
||
566 | $process->setTimeout(3600); |
||
567 | $process->run(); |
||
568 | if(!$process->isSuccessful()) { |
||
569 | throw new RuntimeException($process->getErrorOutput()); |
||
570 | } |
||
571 | |||
572 | $this->write(); |
||
573 | |||
574 | return true; |
||
575 | } |
||
576 | |||
577 | } |
||
578 |
This check marks property names that have not been written in camelCase.
In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes
databaseConnectionString
.