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