Import from SVN (r45945, r596)
[civicrm-core.git] / CRM / Utils / Zip.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.3 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2013 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26 */
27
28 /**
29 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2013
32 * $Id$
33 *
34 */
35
36 /**
37 * Utilities for working with zip files
38 */
39 class CRM_Utils_Zip {
40
41 /**
42 * Given a zip file which contains a single root directory, determine the root's name.
43 *
44 * @return mixed; FALSE if #root level items !=1; otherwise, the name of base dir
45 */
46 static public function findBaseDirName(ZipArchive $zip) {
47 $cnt = $zip->numFiles;
48
49 $base = FALSE;
50 $baselen = FALSE;
51
52 for ($i = 0; $i < $cnt; $i++) {
53 $filename = $zip->getNameIndex($i);
54 if ($base === FALSE) {
55 if (preg_match('/^[^\/]+\/$/', $filename) && $filename != './' && $filename != '../') {
56 $base = $filename;
57 $baselen = strlen($filename);
58 } else {
59 return FALSE;
60 }
61 } elseif (0 != substr_compare($base, $filename, 0, $baselen)) {
62 return FALSE;
63 }
64 }
65
66 return $base;
67 }
68
69 /**
70 * Given a zip file, find all directory names in the root
71 *
72 * @return array(string), no trailing /
73 */
74 static public function findBaseDirs(ZipArchive $zip) {
75 $cnt = $zip->numFiles;
76 $basedirs = array();
77
78 for ($i = 0; $i < $cnt; $i++) {
79 $filename = $zip->getNameIndex($i);
80 // hypothetically, ./ or ../ would not be legit here
81 if (preg_match('/^[^\/]+\/$/', $filename) && $filename != './' && $filename != '../') {
82 $basedirs[] = rtrim($filename, '/');
83 }
84 }
85
86 return $basedirs;
87 }
88
89 /**
90 * Determine the name of the folder within a zip
91 *
92 * @return string or FALSE
93 */
94 static public function guessBasedir(ZipArchive $zip, $expected) {
95 $candidate = FALSE;
96 $basedirs = CRM_Utils_Zip::findBaseDirs($zip);
97 if (in_array($expected, $basedirs)) {
98 $candidate = $expected;
99 } elseif (count($basedirs) == 1) {
100 $candidate = array_shift($basedirs);
101 }
102 if ($candidate !== FALSE && preg_match('/^[a-zA-Z0-9]/', $candidate)) {
103 return $candidate;
104 } else {
105 return FALSE;
106 }
107 }
108
109
110 /**
111 * An inefficient helper for creating a ZIP file from data in memory.
112 * This is only intended for building temp files for unit-testing.
113 *
114 * @param $zipName string, file name
115 * @param $dirs array, list of directory paths
116 * @param $files array, keys are file names and values are file contents
117 * @return bool
118 */
119 static public function createTestZip($zipName, $dirs, $files) {
120 $zip = new ZipArchive();
121 $res = $zip->open($zipName, ZipArchive::CREATE);
122 if ($res === TRUE) {
123 foreach ($dirs as $dir) {
124 if (!$zip->addEmptyDir($dir)) {
125 return FALSE;
126 }
127 }
128 foreach ($files as $fileName => $fileData) {
129 if (!$zip->addFromString($fileName, $fileData)) {
130 return FALSE;
131 }
132 }
133 $zip->close();
134 } else {
135 return FALSE;
136 }
137 return TRUE;
138 }
139 }