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