Commit | Line | Data |
---|---|---|
6a488035 TO |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
06b69b18 | 4 | | CiviCRM version 4.5 | |
6a488035 | 5 | +--------------------------------------------------------------------+ |
06b69b18 | 6 | | Copyright CiviCRM LLC (c) 2004-2014 | |
6a488035 TO |
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 | |
06b69b18 | 31 | * @copyright CiviCRM LLC (c) 2004-2014 |
6a488035 TO |
32 | * $Id: $ |
33 | * | |
34 | */ | |
35 | ||
36 | /** | |
37 | * class to provide simple static functions for file objects | |
38 | */ | |
39 | class CRM_Utils_File { | |
40 | ||
41 | /** | |
42 | * Given a file name, determine if the file contents make it an ascii file | |
43 | * | |
44 | * @param string $name name of file | |
45 | * | |
46 | * @return boolean true if file is ascii | |
47 | * @access public | |
48 | */ | |
49 | static function isAscii($name) { | |
50 | $fd = fopen($name, "r"); | |
51 | if (!$fd) { | |
52 | return FALSE; | |
53 | } | |
54 | ||
55 | $ascii = TRUE; | |
56 | while (!feof($fd)) { | |
57 | $line = fgets($fd, 8192); | |
58 | if (!CRM_Utils_String::isAscii($line)) { | |
59 | $ascii = FALSE; | |
60 | break; | |
61 | } | |
62 | } | |
63 | ||
64 | fclose($fd); | |
65 | return $ascii; | |
66 | } | |
67 | ||
68 | /** | |
69 | * Given a file name, determine if the file contents make it an html file | |
70 | * | |
71 | * @param string $name name of file | |
72 | * | |
73 | * @return boolean true if file is html | |
74 | * @access public | |
75 | */ | |
76 | static function isHtml($name) { | |
77 | $fd = fopen($name, "r"); | |
78 | if (!$fd) { | |
79 | return FALSE; | |
80 | } | |
81 | ||
82 | $html = FALSE; | |
83 | $lineCount = 0; | |
84 | while (!feof($fd) & $lineCount <= 5) { | |
85 | $lineCount++; | |
86 | $line = fgets($fd, 8192); | |
87 | if (!CRM_Utils_String::isHtml($line)) { | |
88 | $html = TRUE; | |
89 | break; | |
90 | } | |
91 | } | |
92 | ||
93 | fclose($fd); | |
94 | return $html; | |
95 | } | |
96 | ||
97 | /** | |
100fef9d | 98 | * Create a directory given a path name, creates parent directories |
6a488035 TO |
99 | * if needed |
100 | * | |
101 | * @param string $path the path name | |
102 | * @param boolean $abort should we abort or just return an invalid code | |
103 | * | |
104 | * @return void | |
105 | * @access public | |
106 | * @static | |
107 | */ | |
108 | static function createDir($path, $abort = TRUE) { | |
109 | if (is_dir($path) || empty($path)) { | |
110 | return; | |
111 | } | |
112 | ||
113 | CRM_Utils_File::createDir(dirname($path), $abort); | |
114 | if (@mkdir($path, 0777) == FALSE) { | |
115 | if ($abort) { | |
116 | $docLink = CRM_Utils_System::docURL2('Moving an Existing Installation to a New Server or Location', NULL, NULL, NULL, NULL, "wiki"); | |
117 | echo "Error: Could not create directory: $path.<p>If you have moved an existing CiviCRM installation from one location or server to another there are several steps you will need to follow. They are detailed on this CiviCRM wiki page - {$docLink}. A fix for the specific problem that caused this error message to be displayed is to set the value of the config_backend column in the civicrm_domain table to NULL. However we strongly recommend that you review and follow all the steps in that document.</p>"; | |
118 | ||
119 | CRM_Utils_System::civiExit(); | |
120 | } | |
121 | else { | |
122 | return FALSE; | |
123 | } | |
124 | } | |
125 | return TRUE; | |
126 | } | |
127 | ||
128 | /** | |
100fef9d | 129 | * Delete a directory given a path name, delete children directories |
6a488035 TO |
130 | * and files if needed |
131 | * | |
c490a46a | 132 | * @param string $target the path name |
f4aaa82a EM |
133 | * @param bool $rmdir |
134 | * @param bool $verbose | |
135 | * | |
136 | * @throws Exception | |
6a488035 TO |
137 | * @return void |
138 | * @access public | |
139 | * @static | |
140 | */ | |
141 | static function cleanDir($target, $rmdir = TRUE, $verbose = TRUE) { | |
142 | static $exceptions = array('.', '..'); | |
143 | if ($target == '' || $target == '/') { | |
144 | throw new Exception("Overly broad deletion"); | |
145 | } | |
146 | ||
147 | if ($sourcedir = @opendir($target)) { | |
148 | while (FALSE !== ($sibling = readdir($sourcedir))) { | |
149 | if (!in_array($sibling, $exceptions)) { | |
150 | $object = $target . DIRECTORY_SEPARATOR . $sibling; | |
151 | ||
152 | if (is_dir($object)) { | |
153 | CRM_Utils_File::cleanDir($object, $rmdir, $verbose); | |
154 | } | |
155 | elseif (is_file($object)) { | |
156 | if (!unlink($object)) { | |
157 | CRM_Core_Session::setStatus(ts('Unable to remove file %1', array(1 => $object)), ts('Warning'), 'error'); | |
158 | } | |
159 | } | |
160 | } | |
161 | } | |
162 | closedir($sourcedir); | |
163 | ||
164 | if ($rmdir) { | |
165 | if (rmdir($target)) { | |
166 | if ($verbose) { | |
450f494d | 167 | CRM_Core_Session::setStatus(ts('Removed directory %1', array(1 => $target)), '', 'success'); |
6a488035 TO |
168 | } |
169 | return TRUE; | |
170 | } | |
171 | else { | |
172 | CRM_Core_Session::setStatus(ts('Unable to remove directory %1', array(1 => $target)), ts('Warning'), 'error'); | |
173 | } | |
174 | } | |
175 | } | |
176 | } | |
177 | ||
5bc392e6 EM |
178 | /** |
179 | * @param $source | |
180 | * @param $destination | |
181 | */ | |
6a488035 TO |
182 | static function copyDir($source, $destination) { |
183 | $dir = opendir($source); | |
184 | @mkdir($destination); | |
185 | while (FALSE !== ($file = readdir($dir))) { | |
186 | if (($file != '.') && ($file != '..')) { | |
187 | if (is_dir($source . DIRECTORY_SEPARATOR . $file)) { | |
188 | CRM_Utils_File::copyDir($source . DIRECTORY_SEPARATOR . $file, $destination . DIRECTORY_SEPARATOR . $file); | |
189 | } | |
190 | else { | |
191 | copy($source . DIRECTORY_SEPARATOR . $file, $destination . DIRECTORY_SEPARATOR . $file); | |
192 | } | |
193 | } | |
194 | } | |
195 | closedir($dir); | |
196 | } | |
197 | ||
198 | /** | |
199 | * Given a file name, recode it (in place!) to UTF-8 | |
200 | * | |
201 | * @param string $name name of file | |
202 | * | |
203 | * @return boolean whether the file was recoded properly | |
204 | * @access public | |
205 | */ | |
206 | static function toUtf8($name) { | |
207 | static $config = NULL; | |
208 | static $legacyEncoding = NULL; | |
209 | if ($config == NULL) { | |
210 | $config = CRM_Core_Config::singleton(); | |
211 | $legacyEncoding = $config->legacyEncoding; | |
212 | } | |
213 | ||
214 | if (!function_exists('iconv')) { | |
215 | ||
216 | return FALSE; | |
217 | ||
218 | } | |
219 | ||
220 | $contents = file_get_contents($name); | |
221 | if ($contents === FALSE) { | |
222 | return FALSE; | |
223 | } | |
224 | ||
225 | $contents = iconv($legacyEncoding, 'UTF-8', $contents); | |
226 | if ($contents === FALSE) { | |
227 | return FALSE; | |
228 | } | |
229 | ||
230 | $file = fopen($name, 'w'); | |
231 | if ($file === FALSE) { | |
232 | return FALSE; | |
233 | } | |
234 | ||
235 | $written = fwrite($file, $contents); | |
236 | $closed = fclose($file); | |
237 | if ($written === FALSE or !$closed) { | |
238 | return FALSE; | |
239 | } | |
240 | ||
241 | return TRUE; | |
242 | } | |
243 | ||
244 | /** | |
5c8cb77f | 245 | * Appends a slash to the end of a string if it doesn't already end with one |
6a488035 | 246 | * |
5c8cb77f CW |
247 | * @param string $path |
248 | * @param string $slash | |
f4aaa82a | 249 | * |
6a488035 TO |
250 | * @return string |
251 | * @access public | |
252 | * @static | |
253 | */ | |
5c8cb77f CW |
254 | static function addTrailingSlash($path, $slash = NULL) { |
255 | if (!$slash) { | |
256 | // FIXME: Defaulting to backslash on windows systems can produce unexpected results, esp for URL strings which should always use forward-slashes. | |
257 | // I think this fn should default to forward-slash instead. | |
258 | $slash = DIRECTORY_SEPARATOR; | |
6a488035 | 259 | } |
5c8cb77f CW |
260 | if (!in_array(substr($path, -1, 1), array('/', '\\'))) { |
261 | $path .= $slash; | |
6a488035 | 262 | } |
5c8cb77f | 263 | return $path; |
6a488035 TO |
264 | } |
265 | ||
5bc392e6 EM |
266 | /** |
267 | * @param $dsn | |
100fef9d | 268 | * @param string $fileName |
5bc392e6 EM |
269 | * @param null $prefix |
270 | * @param bool $isQueryString | |
271 | * @param bool $dieOnErrors | |
272 | */ | |
6a488035 TO |
273 | static function sourceSQLFile($dsn, $fileName, $prefix = NULL, $isQueryString = FALSE, $dieOnErrors = TRUE) { |
274 | require_once 'DB.php'; | |
275 | ||
276 | $db = DB::connect($dsn); | |
277 | if (PEAR::isError($db)) { | |
278 | die("Cannot open $dsn: " . $db->getMessage()); | |
279 | } | |
280 | if (CRM_Utils_Constant::value('CIVICRM_MYSQL_STRICT', CRM_Utils_System::isDevelopment())) { | |
281 | $db->query('SET SESSION sql_mode = STRICT_TRANS_TABLES'); | |
282 | } | |
283 | ||
284 | if (!$isQueryString) { | |
285 | $string = $prefix . file_get_contents($fileName); | |
286 | } | |
287 | else { | |
288 | // use filename as query string | |
289 | $string = $prefix . $fileName; | |
290 | } | |
291 | ||
292 | //get rid of comments starting with # and -- | |
293 | ||
294 | $string = preg_replace("/^#[^\n]*$/m", "\n", $string); | |
295 | $string = preg_replace("/^(--[^-]).*/m", "\n", $string); | |
296 | ||
297 | $queries = preg_split('/;\s*$/m', $string); | |
298 | foreach ($queries as $query) { | |
299 | $query = trim($query); | |
300 | if (!empty($query)) { | |
301 | CRM_Core_Error::debug_query($query); | |
302 | $res = &$db->query($query); | |
303 | if (PEAR::isError($res)) { | |
304 | if ($dieOnErrors) { | |
305 | die("Cannot execute $query: " . $res->getMessage()); | |
306 | } | |
307 | else { | |
308 | echo "Cannot execute $query: " . $res->getMessage() . "<p>"; | |
309 | } | |
310 | } | |
311 | } | |
312 | } | |
313 | } | |
314 | ||
5bc392e6 EM |
315 | /** |
316 | * @param $ext | |
317 | * | |
318 | * @return bool | |
319 | */ | |
6a488035 TO |
320 | static function isExtensionSafe($ext) { |
321 | static $extensions = NULL; | |
322 | if (!$extensions) { | |
323 | $extensions = CRM_Core_OptionGroup::values('safe_file_extension', TRUE); | |
324 | ||
325 | //make extensions to lowercase | |
326 | $extensions = array_change_key_case($extensions, CASE_LOWER); | |
327 | // allow html/htm extension ONLY if the user is admin | |
328 | // and/or has access CiviMail | |
329 | if (!(CRM_Core_Permission::check('access CiviMail') || | |
330 | CRM_Core_Permission::check('administer CiviCRM') || | |
331 | (CRM_Mailing_Info::workflowEnabled() && | |
332 | CRM_Core_Permission::check('create mailings') | |
333 | ) | |
334 | )) { | |
335 | unset($extensions['html']); | |
336 | unset($extensions['htm']); | |
337 | } | |
338 | } | |
339 | //support lower and uppercase file extensions | |
340 | return isset($extensions[strtolower($ext)]) ? TRUE : FALSE; | |
341 | } | |
342 | ||
343 | /** | |
344 | * Determine whether a given file is listed in the PHP include path | |
345 | * | |
346 | * @param string $name name of file | |
347 | * | |
348 | * @return boolean whether the file can be include()d or require()d | |
349 | */ | |
350 | static function isIncludable($name) { | |
351 | $x = @fopen($name, 'r', TRUE); | |
352 | if ($x) { | |
353 | fclose($x); | |
354 | return TRUE; | |
355 | } | |
356 | else { | |
357 | return FALSE; | |
358 | } | |
359 | } | |
360 | ||
361 | /** | |
100fef9d | 362 | * Remove the 32 bit md5 we add to the fileName |
6a488035 TO |
363 | * also remove the unknown tag if we added it |
364 | */ | |
365 | static function cleanFileName($name) { | |
366 | // replace the last 33 character before the '.' with null | |
367 | $name = preg_replace('/(_[\w]{32})\./', '.', $name); | |
368 | return $name; | |
369 | } | |
370 | ||
5bc392e6 | 371 | /** |
100fef9d | 372 | * @param string $name |
5bc392e6 EM |
373 | * |
374 | * @return string | |
375 | */ | |
6a488035 TO |
376 | static function makeFileName($name) { |
377 | $uniqID = md5(uniqid(rand(), TRUE)); | |
378 | $info = pathinfo($name); | |
379 | $basename = substr($info['basename'], | |
380 | 0, -(strlen(CRM_Utils_Array::value('extension', $info)) + (CRM_Utils_Array::value('extension', $info) == '' ? 0 : 1)) | |
381 | ); | |
382 | if (!self::isExtensionSafe(CRM_Utils_Array::value('extension', $info))) { | |
383 | // munge extension so it cannot have an embbeded dot in it | |
384 | // The maximum length of a filename for most filesystems is 255 chars. | |
385 | // We'll truncate at 240 to give some room for the extension. | |
386 | return CRM_Utils_String::munge("{$basename}_" . CRM_Utils_Array::value('extension', $info) . "_{$uniqID}", '_', 240) . ".unknown"; | |
387 | } | |
388 | else { | |
389 | return CRM_Utils_String::munge("{$basename}_{$uniqID}", '_', 240) . "." . CRM_Utils_Array::value('extension', $info); | |
390 | } | |
391 | } | |
392 | ||
5bc392e6 EM |
393 | /** |
394 | * @param $path | |
395 | * @param $ext | |
396 | * | |
397 | * @return array | |
398 | */ | |
6a488035 TO |
399 | static function getFilesByExtension($path, $ext) { |
400 | $path = self::addTrailingSlash($path); | |
401 | $dh = opendir($path); | |
402 | $files = array(); | |
403 | while (FALSE !== ($elem = readdir($dh))) { | |
404 | if (substr($elem, -(strlen($ext) + 1)) == '.' . $ext) { | |
405 | $files[] .= $path . $elem; | |
406 | } | |
407 | } | |
408 | closedir($dh); | |
409 | return $files; | |
410 | } | |
411 | ||
412 | /** | |
413 | * Restrict access to a given directory (by planting there a restrictive .htaccess file) | |
414 | * | |
f4aaa82a EM |
415 | * @param string $dir the directory to be secured |
416 | * @param bool $overwrite | |
6a488035 | 417 | */ |
ea3b22b5 | 418 | static function restrictAccess($dir, $overwrite = FALSE) { |
6a488035 TO |
419 | // note: empty value for $dir can play havoc, since that might result in putting '.htaccess' to root dir |
420 | // of site, causing site to stop functioning. | |
421 | // FIXME: we should do more checks here - | |
ea3b22b5 | 422 | if (!empty($dir) && is_dir($dir)) { |
6a488035 TO |
423 | $htaccess = <<<HTACCESS |
424 | <Files "*"> | |
425 | Order allow,deny | |
426 | Deny from all | |
427 | </Files> | |
428 | ||
429 | HTACCESS; | |
430 | $file = $dir . '.htaccess'; | |
ea3b22b5 TO |
431 | if ($overwrite || !file_exists($file)) { |
432 | if (file_put_contents($file, $htaccess) === FALSE) { | |
433 | CRM_Core_Error::movedSiteError($file); | |
434 | } | |
6a488035 TO |
435 | } |
436 | } | |
437 | } | |
438 | ||
af5201d4 TO |
439 | /** |
440 | * Restrict remote users from browsing the given directory. | |
441 | * | |
442 | * @param $publicDir | |
443 | */ | |
444 | static function restrictBrowsing($publicDir) { | |
9404eeac TO |
445 | if (!is_dir($publicDir) || !is_writable($publicDir)) { |
446 | return; | |
447 | } | |
448 | ||
af5201d4 TO |
449 | // base dir |
450 | $nobrowse = realpath($publicDir) . '/index.html'; | |
451 | if (!file_exists($nobrowse)) { | |
452 | @file_put_contents($nobrowse, ''); | |
453 | } | |
454 | ||
455 | // child dirs | |
456 | $dir = new RecursiveDirectoryIterator($publicDir); | |
457 | foreach ($dir as $name => $object) { | |
458 | if (is_dir($name) && $name != '..') { | |
459 | $nobrowse = realpath($name) . '/index.html'; | |
460 | if (!file_exists($nobrowse)) { | |
461 | @file_put_contents($nobrowse, ''); | |
462 | } | |
463 | } | |
464 | } | |
465 | } | |
466 | ||
6a488035 TO |
467 | /** |
468 | * Create the base file path from which all our internal directories are | |
469 | * offset. This is derived from the template compile directory set | |
470 | */ | |
471 | static function baseFilePath($templateCompileDir = NULL) { | |
472 | static $_path = NULL; | |
473 | if (!$_path) { | |
474 | if ($templateCompileDir == NULL) { | |
475 | $config = CRM_Core_Config::singleton(); | |
476 | $templateCompileDir = $config->templateCompileDir; | |
477 | } | |
478 | ||
479 | $path = dirname($templateCompileDir); | |
480 | ||
481 | //this fix is to avoid creation of upload dirs inside templates_c directory | |
482 | $checkPath = explode(DIRECTORY_SEPARATOR, $path); | |
483 | ||
484 | $cnt = count($checkPath) - 1; | |
485 | if ($checkPath[$cnt] == 'templates_c') { | |
486 | unset($checkPath[$cnt]); | |
487 | $path = implode(DIRECTORY_SEPARATOR, $checkPath); | |
488 | } | |
489 | ||
490 | $_path = CRM_Utils_File::addTrailingSlash($path); | |
491 | } | |
492 | return $_path; | |
493 | } | |
494 | ||
5bc392e6 EM |
495 | /** |
496 | * @param $directory | |
497 | * | |
498 | * @return string | |
499 | */ | |
6a488035 TO |
500 | static function relativeDirectory($directory) { |
501 | // Do nothing on windows | |
502 | if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { | |
503 | return $directory; | |
504 | } | |
505 | ||
506 | // check if directory is relative, if so return immediately | |
507 | if (substr($directory, 0, 1) != DIRECTORY_SEPARATOR) { | |
508 | return $directory; | |
509 | } | |
510 | ||
511 | // make everything relative from the baseFilePath | |
512 | $basePath = self::baseFilePath(); | |
513 | // check if basePath is a substr of $directory, if so | |
514 | // return rest of string | |
515 | if (substr($directory, 0, strlen($basePath)) == $basePath) { | |
516 | return substr($directory, strlen($basePath)); | |
517 | } | |
518 | ||
519 | // return the original value | |
520 | return $directory; | |
521 | } | |
522 | ||
5bc392e6 EM |
523 | /** |
524 | * @param $directory | |
525 | * | |
526 | * @return string | |
527 | */ | |
6a488035 | 528 | static function absoluteDirectory($directory) { |
acc609a7 TO |
529 | // check if directory is already absolute, if so return immediately |
530 | // Note: Windows PHP accepts any mix of "/" or "\", so "C:\htdocs" or "C:/htdocs" would be a valid absolute path | |
531 | if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' && preg_match(';^[a-zA-Z]:[/\\\\];', $directory)) { | |
6a488035 TO |
532 | return $directory; |
533 | } | |
534 | ||
535 | // check if directory is already absolute, if so return immediately | |
536 | if (substr($directory, 0, 1) == DIRECTORY_SEPARATOR) { | |
537 | return $directory; | |
538 | } | |
539 | ||
540 | // make everything absolute from the baseFilePath | |
541 | $basePath = self::baseFilePath(); | |
542 | ||
543 | return $basePath . $directory; | |
544 | } | |
545 | ||
546 | /** | |
547 | * Make a file path relative to some base dir | |
548 | * | |
f4aaa82a EM |
549 | * @param $directory |
550 | * @param $basePath | |
551 | * | |
6a488035 TO |
552 | * @return string |
553 | */ | |
554 | static function relativize($directory, $basePath) { | |
555 | if (substr($directory, 0, strlen($basePath)) == $basePath) { | |
556 | return substr($directory, strlen($basePath)); | |
557 | } else { | |
558 | return $directory; | |
559 | } | |
560 | } | |
561 | ||
562 | /** | |
563 | * Create a path to a temporary file which can endure for multiple requests | |
564 | * | |
565 | * TODO: Automatic file cleanup using, eg, TTL policy | |
566 | * | |
567 | * @param $prefix string | |
568 | * | |
569 | * @return string, path to an openable/writable file | |
570 | * @see tempnam | |
571 | */ | |
572 | static function tempnam($prefix = 'tmp-') { | |
573 | //$config = CRM_Core_Config::singleton(); | |
574 | //$nonce = md5(uniqid() . $config->dsn . $config->userFrameworkResourceURL); | |
575 | //$fileName = "{$config->configAndLogDir}" . $prefix . $nonce . $suffix; | |
576 | $fileName = tempnam(sys_get_temp_dir(), $prefix); | |
577 | return $fileName; | |
578 | } | |
579 | ||
580 | /** | |
581 | * Create a path to a temporary directory which can endure for multiple requests | |
582 | * | |
583 | * TODO: Automatic file cleanup using, eg, TTL policy | |
584 | * | |
585 | * @param $prefix string | |
586 | * | |
587 | * @return string, path to an openable/writable directory; ends with '/' | |
588 | * @see tempnam | |
589 | */ | |
590 | static function tempdir($prefix = 'tmp-') { | |
591 | $fileName = self::tempnam($prefix); | |
592 | unlink($fileName); | |
593 | mkdir($fileName, 0700); | |
594 | return $fileName . '/'; | |
595 | } | |
596 | ||
597 | /** | |
d7166b43 TO |
598 | * Search directory tree for files which match a glob pattern. |
599 | * | |
600 | * Note: Dot-directories (like "..", ".git", or ".svn") will be ignored. | |
6a488035 TO |
601 | * |
602 | * @param $dir string, base dir | |
603 | * @param $pattern string, glob pattern, eg "*.txt" | |
604 | * @return array(string) | |
605 | */ | |
606 | static function findFiles($dir, $pattern) { | |
607 | $todos = array($dir); | |
608 | $result = array(); | |
609 | while (!empty($todos)) { | |
610 | $subdir = array_shift($todos); | |
0b72a00f TO |
611 | $matches = glob("$subdir/$pattern"); |
612 | if (is_array($matches)) { | |
613 | foreach ($matches as $match) { | |
002f1716 TO |
614 | if (!is_dir($match)) { |
615 | $result[] = $match; | |
616 | } | |
6a488035 TO |
617 | } |
618 | } | |
619 | $dh = opendir($subdir); | |
620 | if ($dh) { | |
621 | while (FALSE !== ($entry = readdir($dh))) { | |
622 | $path = $subdir . DIRECTORY_SEPARATOR . $entry; | |
d7166b43 TO |
623 | if ($entry{0} == '.') { |
624 | // ignore | |
6a488035 TO |
625 | } elseif (is_dir($path)) { |
626 | $todos[] = $path; | |
627 | } | |
628 | } | |
629 | closedir($dh); | |
630 | } | |
631 | } | |
632 | return $result; | |
633 | } | |
634 | ||
635 | /** | |
636 | * Determine if $child is a sub-directory of $parent | |
637 | * | |
638 | * @param string $parent | |
639 | * @param string $child | |
f4aaa82a EM |
640 | * @param bool $checkRealPath |
641 | * | |
6a488035 TO |
642 | * @return bool |
643 | */ | |
644 | static function isChildPath($parent, $child, $checkRealPath = TRUE) { | |
645 | if ($checkRealPath) { | |
646 | $parent = realpath($parent); | |
647 | $child = realpath($child); | |
648 | } | |
649 | $parentParts = explode('/', rtrim($parent, '/')); | |
650 | $childParts = explode('/', rtrim($child, '/')); | |
651 | while (($parentPart = array_shift($parentParts)) !== NULL) { | |
652 | $childPart = array_shift($childParts); | |
653 | if ($parentPart != $childPart) { | |
654 | return FALSE; | |
655 | } | |
656 | } | |
657 | if (empty($childParts)) { | |
658 | return FALSE; // same directory | |
659 | } else { | |
660 | return TRUE; | |
661 | } | |
662 | } | |
663 | ||
664 | /** | |
665 | * Move $fromDir to $toDir, replacing/deleting any | |
666 | * pre-existing content. | |
667 | * | |
668 | * @param string $fromDir the directory which should be moved | |
669 | * @param string $toDir the new location of the directory | |
f4aaa82a EM |
670 | * @param bool $verbose |
671 | * | |
6a488035 TO |
672 | * @return bool TRUE on success |
673 | */ | |
674 | static function replaceDir($fromDir, $toDir, $verbose = FALSE) { | |
675 | if (is_dir($toDir)) { | |
676 | if (!self::cleanDir($toDir, TRUE, $verbose)) { | |
677 | return FALSE; | |
678 | } | |
679 | } | |
680 | ||
681 | // return rename($fromDir, $toDir); // CRM-11987, https://bugs.php.net/bug.php?id=54097 | |
682 | ||
683 | CRM_Utils_File::copyDir($fromDir, $toDir); | |
684 | if (!CRM_Utils_File::cleanDir($fromDir, TRUE, FALSE)) { | |
685 | CRM_Core_Session::setStatus(ts('Failed to clean temp dir: %1', array(1 => $fromDir)), '', 'alert'); | |
686 | return FALSE; | |
687 | } | |
688 | return TRUE; | |
689 | } | |
690 | } | |
691 |