Merge pull request #19152 from MegaphoneJon/financial-160
[civicrm-core.git] / CRM / Utils / Zip.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035
TO
11
12/**
13 *
14 * @package CRM
ca5cec67 15 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035
TO
16 */
17
18/**
19 * Utilities for working with zip files
20 */
21class CRM_Utils_Zip {
22
23 /**
24 * Given a zip file which contains a single root directory, determine the root's name.
25 *
f4aaa82a
EM
26 * @param ZipArchive $zip
27 *
50bfb460
SB
28 * @return mixed
29 * FALSE if #root level items !=1; otherwise, the name of base dir
6a488035 30 */
6714d8d2 31 public static function findBaseDirName(ZipArchive $zip) {
6a488035
TO
32 $cnt = $zip->numFiles;
33
34 $base = FALSE;
35 $baselen = FALSE;
36
37 for ($i = 0; $i < $cnt; $i++) {
38 $filename = $zip->getNameIndex($i);
39 if ($base === FALSE) {
40 if (preg_match('/^[^\/]+\/$/', $filename) && $filename != './' && $filename != '../') {
41 $base = $filename;
42 $baselen = strlen($filename);
0db6c3e1
TO
43 }
44 else {
6a488035
TO
45 return FALSE;
46 }
d3e86119
TO
47 }
48 elseif (0 != substr_compare($base, $filename, 0, $baselen)) {
6a488035
TO
49 return FALSE;
50 }
51 }
52
53 return $base;
54 }
55
56 /**
57 * Given a zip file, find all directory names in the root
58 *
f4aaa82a
EM
59 * @param ZipArchive $zip
60 *
50bfb460
SB
61 * @return array(string)
62 * no trailing /
6a488035 63 */
6714d8d2 64 public static function findBaseDirs(ZipArchive $zip) {
6a488035 65 $cnt = $zip->numFiles;
be2fb01f 66 $basedirs = [];
6a488035
TO
67
68 for ($i = 0; $i < $cnt; $i++) {
69 $filename = $zip->getNameIndex($i);
70 // hypothetically, ./ or ../ would not be legit here
71 if (preg_match('/^[^\/]+\/$/', $filename) && $filename != './' && $filename != '../') {
72 $basedirs[] = rtrim($filename, '/');
73 }
74 }
75
76 return $basedirs;
77 }
78
79 /**
fe482240 80 * Determine the name of the folder within a zip.
6a488035 81 *
f4aaa82a
EM
82 * @param ZipArchive $zip
83 * @param $expected
84 *
50bfb460
SB
85 * @return string|bool
86 * Return string or FALSE
6a488035 87 */
6714d8d2 88 public static function guessBasedir(ZipArchive $zip, $expected) {
6a488035
TO
89 $candidate = FALSE;
90 $basedirs = CRM_Utils_Zip::findBaseDirs($zip);
91 if (in_array($expected, $basedirs)) {
92 $candidate = $expected;
0db6c3e1
TO
93 }
94 elseif (count($basedirs) == 1) {
6a488035
TO
95 $candidate = array_shift($basedirs);
96 }
97 if ($candidate !== FALSE && preg_match('/^[a-zA-Z0-9]/', $candidate)) {
98 return $candidate;
0db6c3e1
TO
99 }
100 else {
6a488035
TO
101 return FALSE;
102 }
103 }
104
6a488035
TO
105 /**
106 * An inefficient helper for creating a ZIP file from data in memory.
107 * This is only intended for building temp files for unit-testing.
108 *
5a4f6742
CW
109 * @param string $zipName
110 * file name.
111 * @param array $dirs
77855840 112 * Array, list of directory paths.
5a4f6742 113 * @param array $files
77855840 114 * Array, keys are file names and values are file contents.
6a488035
TO
115 * @return bool
116 */
6714d8d2 117 public static function createTestZip($zipName, $dirs, $files) {
6a488035
TO
118 $zip = new ZipArchive();
119 $res = $zip->open($zipName, ZipArchive::CREATE);
120 if ($res === TRUE) {
121 foreach ($dirs as $dir) {
122 if (!$zip->addEmptyDir($dir)) {
123 return FALSE;
124 }
125 }
126 foreach ($files as $fileName => $fileData) {
127 if (!$zip->addFromString($fileName, $fileData)) {
128 return FALSE;
129 }
130 }
131 $zip->close();
0db6c3e1
TO
132 }
133 else {
e7292422 134 return FALSE;
6a488035
TO
135 }
136 return TRUE;
137 }
96025800 138
6a488035 139}