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