Overview

Packages

  • application
    • commands
    • components
      • actions
      • filters
      • leftWidget
      • permissions
      • sortableWidget
      • util
      • webupdater
      • x2flow
        • actions
        • triggers
      • X2GridView
      • X2Settings
    • controllers
    • models
      • embedded
    • modules
      • accounts
        • controllers
        • models
      • actions
        • controllers
        • models
      • calendar
        • controllers
        • models
      • charts
        • models
      • contacts
        • controllers
        • models
      • docs
        • components
        • controllers
        • models
      • groups
        • controllers
        • models
      • marketing
        • components
        • controllers
        • models
      • media
        • controllers
        • models
      • mobile
        • components
      • opportunities
        • controllers
        • models
      • products
        • controllers
        • models
      • quotes
        • controllers
        • models
      • services
        • controllers
        • models
      • template
        • models
      • users
        • controllers
        • models
      • workflow
        • controllers
        • models
      • x2Leads
        • controllers
        • models
  • None
  • system
    • base
    • caching
    • console
    • db
      • ar
      • schema
    • validators
    • web
      • actions
      • auth
      • helpers
      • widgets
        • captcha
        • pagers
  • zii
    • widgets
      • grid

Classes

  • AppFileUtil
  • CommandUtil
  • CrontabUtil
  • EncryptUtil
  • FileUtil
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /*****************************************************************************************
  3:  * X2Engine Open Source Edition is a customer relationship management program developed by
  4:  * X2Engine, Inc. Copyright (C) 2011-2016 X2Engine Inc.
  5:  * 
  6:  * This program is free software; you can redistribute it and/or modify it under
  7:  * the terms of the GNU Affero General Public License version 3 as published by the
  8:  * Free Software Foundation with the addition of the following permission added
  9:  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
 10:  * IN WHICH THE COPYRIGHT IS OWNED BY X2ENGINE, X2ENGINE DISCLAIMS THE WARRANTY
 11:  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 12:  * 
 13:  * This program is distributed in the hope that it will be useful, but WITHOUT
 14:  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 15:  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
 16:  * details.
 17:  * 
 18:  * You should have received a copy of the GNU Affero General Public License along with
 19:  * this program; if not, see http://www.gnu.org/licenses or write to the Free
 20:  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 21:  * 02110-1301 USA.
 22:  * 
 23:  * You can contact X2Engine, Inc. P.O. Box 66752, Scotts Valley,
 24:  * California 95067, USA. or at email address contact@x2engine.com.
 25:  * 
 26:  * The interactive user interfaces in modified source and object code versions
 27:  * of this program must display Appropriate Legal Notices, as required under
 28:  * Section 5 of the GNU Affero General Public License version 3.
 29:  * 
 30:  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
 31:  * these Appropriate Legal Notices must retain the display of the "Powered by
 32:  * X2Engine" logo. If the display of the logo is not reasonably feasible for
 33:  * technical reasons, the Appropriate Legal Notices must display the words
 34:  * "Powered by X2Engine".
 35:  *****************************************************************************************/
 36: 
 37: /**
 38:  * Originally a copy of FileUtil.php to remove app's dependencies on updater
 39:  *
 40:  * Miscellaneous file system utilities. It is not a child class of CComponent or
 41:  * the like in order to be portable/stand-alone (so it can be used outside the
 42:  * app, i.e. by the installer).
 43:  *
 44:  * @package application.components.util
 45:  * @author Demitri Morgan <demitri@x2engine.com>
 46:  */
 47: class AppFileUtil {
 48: 
 49:     const ERR_FTPOPER = 100;
 50:     const ERR_SCPOPER = 200;
 51: 
 52:     public static $_finfo;
 53: 
 54:     public static $alwaysCurl = false;
 55:     /**
 56:      * @var bool $neverCurl overrides $alwaysCurl when true 
 57:      */
 58:     public static $neverCurl = false;
 59: 
 60:     public static $fileOper = "php";
 61:     public static $ftpChroot = false;
 62:     private static $_ftpStream;
 63:     private static $_sshStream;
 64:     private static $_sftpStream;
 65: 
 66:     /**
 67:      * Copies a file or directory recursively.
 68:      *
 69:      * If the local filesystem directory to where the file will be copied does
 70:      * not exist yet, it will be created automatically. Furthermore, if a remote
 71:      * URL is being accessed and allow_url_fopen isn't set, it will attempt to
 72:      * use CURL instead.
 73:      *
 74:      * @param string $source The source file.
 75:      * @param string $target The destination path.
 76:      * @param boolean $relTarget Transform the target to a relative path. This
 77:      *  option must be false unless the target is an absolute path.
 78:      * @param boolean $contents If true, and the source is a directory, its
 79:      *  contents will be copied to the destination; otherwise, the whole directory
 80:      *  will be copied into the destination directory.
 81:      * @return boolean
 82:      */
 83:     public static function ccopy($source, $target, $relTarget = false, $contents = true){
 84:         $ds = DIRECTORY_SEPARATOR;
 85:         $remote = (bool) preg_match('%^https?://%', $source);
 86:         // Normalize target to the form where if it's a directory it doesn't have
 87:         // a trailing slash, and is platform-agnostic:
 88:         $target = rtrim(self::rpath($target), $ds);
 89:         // Make the target into a relative path:
 90:         if($relTarget && !in_array(self::$fileOper, array('ftp', 'scp')))
 91:             $target = self::relpath($target);
 92:         // Safeguard against overwriting files:
 93:         if(!$remote && is_dir($source) && !is_dir($target) && is_file($target))
 94:             throw new Exception("Cannot copy a directory ($source) into a file ($target).");
 95: 
 96:         // Modify file paths for scp and ftp file operations
 97:         if (self::$fileOper === 'scp') {
 98:             $target = self::scpXlateRelpath($target);
 99:         } elseif (self::$fileOper === 'ftp') {
100:             $target = self::ftpStripChroot($target);
101:         }
102: 
103:         // Create parent directories if they don't exist already.
104:         // 
105:         // If a file is being copied: the path to examine for directory creation
106:         //  is one lower than the target (the bottom-level parent).
107:         // If a directory is being copied: the same is true (to not create the
108:         //  top level node) even though it's a directory, because the target
109:         //  directory will be created anyway if necessary
110:         // If a directory is being copied and $contents is false: it's assumed
111:         //  that the target is a destination directory and not part of the tree
112:         //  to be copied.
113:         $pathNodes = explode($ds, $target);
114:         if($contents)
115:             array_pop($pathNodes);
116:         for($i = 0; $i <= count($pathNodes); $i++){
117:             $parent = implode($ds, array_slice($pathNodes, 0, $i));
118:             // If we are using an FTP chroot, prepend the $parent path with the chroot dir
119:             // so that is_dir() is accurate.
120:             if (self::$fileOper === 'ftp' && self::$ftpChroot !== false && !self::isRelative($parent))
121:                 $verifyDir = self::$ftpChroot.$parent;
122:             else
123:                 $verifyDir = $parent;
124:             if($parent != '' && !is_dir($verifyDir)){
125:                 self::mkdir ($parent);
126:             }
127:         }
128: 
129:         if($remote){
130:             if(self::tryCurl($source)){
131:                 // Fall back on the getContents method, which will try using CURL
132:                 $ch = self::curlInit($source);
133:                 $contents = curl_exec($ch);
134:                 if((bool) $contents)
135:                     return @file_put_contents($target, $contents) !== false;
136:                 else
137:                     return false;
138:             } else{
139:                 $context = stream_context_create(array(
140:                     'http' => array(
141:                         'timeout' => 15  // Timeout in seconds
142:                         )));
143:                 return @copy($source, $target, $context) !== false;
144:             }
145:         }else{
146:             // Recursively copy a whole folder
147:             $source = self::rpath($source);
148:             if(!is_dir($source) && !file_exists($source))
149:                 throw new Exception("Source file/directory to be copied ($source) not found.");
150: 
151:             if(is_dir($source)){
152:                 if(!$contents){
153:                     // Append the bottom level node in the source path to the
154:                     // target path.
155:                     //
156:                     // This ensures that we copy in the aptly-named target
157:                     // directory instead of dumping the contents of the source
158:                     // into the designated target.
159:                     $source = rtrim($source, $ds);
160:                     $sourceNodes = explode($ds, $source);
161:                     $target = $target.$ds.array_pop($sourceNodes);
162:                 }
163:                 if(!is_dir($target)){
164:                     self::mkdir ($target);
165:                 }
166:                 $return = true;
167:                 $files = scandir($source);
168:                 foreach($files as $file){
169:                     if($file != '.' && $file != '..'){
170:                         // Must be recursively called with $relTarget = false
171:                         // because if ccopy is called with $relTarget = true,
172:                         // then at this stage "$target" is already relative,
173:                         // and the argument passed to relpath must be absolute.
174:                         // It also must be called with contents=true because
175:                         // that option, if enabled at lower levels, will create
176:                         // the parent directory twice.
177:                         $return = $return && FileUtil::ccopy($source.$ds.$file, $target.$ds.$file);
178:                     }
179:                 }
180:                 return $return;
181:             }else{
182:                 switch (self::$fileOper) {
183:                     case 'ftp':
184:                         return @ftp_put(self::$_ftpStream, self::ftpStripChroot($target), $source, FTP_BINARY);
185:                     case 'scp':
186:                         return @ssh2_scp_send (self::$_sshStream, $source, self::scpXlateRelpath ($target));
187:                     case 'php':
188:                     default:
189:                         $retVal = @copy($source, $target) !== false;
190:                         self::caseInsensitiveCopyFix ($source, $target);
191:                         return $retVal;
192:                 }
193:             }
194:         }
195:     }
196: 
197:     /**
198:      * Create a new directory according to the current file operation method
199:      * @param string $path Path to new directory
200:      */
201:     public static function mkdir($path) {
202:         switch (self::$fileOper) {
203:             case 'ftp':
204:                 if (!@ftp_mkdir(self::$_ftpStream, self::ftpStripChroot($path)))
205:                     throw new Exception("Failed to create directory $path", self::ERR_FTPOPER);
206:                 break;
207:             case 'scp':
208:                 if (!ssh2_sftp_mkdir (self::$_sftpStream, self::scpXlateRelpath($path)))
209:                     throw new Exception("Failed to create directory $path", self::ERR_SCPOPER);
210:                 break;
211:             case 'php':
212:             default:
213:                 if(!@mkdir($path))
214:                     throw new Exception("Failed to create directory $path");
215:         }
216:     }
217: 
218:     /**
219:      * To be called after copying a file. If it's the case that source and target basenames differ 
220:      * by case, target will be renamed so that its basename matches the source's. Allows case
221:      * of source filename to be preserved in case insensitive file systems.
222:      * @return bool false if rename wasn't called, true otherwise (value used for testing purposes)
223:      */
224:     private static function caseInsensitiveCopyFix ($source, $target) {
225:         $sourceBasename = basename ($source);
226:         $targetBasename = basename ($target);
227:         // if basename of source and target params aren't the same, it means that case was changed
228:         // explicitly
229:         if ($sourceBasename !== $targetBasename) return false;
230: 
231:         // get path to file corresponding to target, so that we can get the basename of the actual
232:         // file
233:         $target = realpath ($target); 
234:         if (!$target) return false;
235: 
236:         $targetBasename = basename ($target);
237: 
238:         // source and target have the same case so renaming won't be necessary
239:         if ($targetBasename === $sourceBasename ||
240:             // or source and target base name differ by something other than case
241:             strtolower ($targetBasename) !== strtolower ($sourceBasename)) {
242: 
243:             return false;
244:         }
245: 
246:         // replace target basename with source basename
247:         $newTargetName = preg_replace (
248:             '/'.preg_quote ($targetBasename).'$/', $sourceBasename, $target); 
249:         if ($newTargetName !== $target) {
250:             @rename ($target, $newTargetName);
251:             return true;
252:         }
253:         return false;
254:     }
255: 
256:     /**
257:      * Change to a given directory relative to the FTP stream's
258:      * current working directory
259:      * @param string $target
260:      */
261:     public static function cdftp($target) {
262:         $target = self::ftpStripChroot($target);
263:         $src = ftp_pwd(self::$_ftpStream);
264:         if ($src === '/')
265:             $cd = $target;
266:         else {
267:             $cd = self::relpath($target, $src . DIRECTORY_SEPARATOR);
268:             if (empty($cd))
269:                 return;
270:         }
271:         if (!@ftp_chdir(self::$_ftpStream, $cd))
272:             throw new Exception("Unable to change to directory '$cd' from '$src'", self::ERR_FTPOPER);
273:     }
274: 
275:     /**
276:      * Removes DOS-related junk from an absolute path.
277:      *
278:      * Returns the path as an array of nodes.
279:      */
280:     public static function cleanDosPath($path){
281:         $a_dirty = explode('\\', $path);
282:         $a = array();
283:         foreach($a_dirty as $node){
284:             $a[] = $node;
285:         }
286:         $lastNode = array_pop($a);
287:         if(preg_match('%/%', $lastNode)){
288:             // The final part of the path might contain a relative path put
289:             // together with forward slashes (for the lazy developer)
290:             foreach(explode('/', $lastNode) as $node)
291:                 $a[] = $node;
292:         }else{
293:             $a[] = $lastNode;
294:         }
295:         return $a;
296:     }
297: 
298: 
299:     /**
300:      * Create a lockfile using the appropriate functionality, depending
301:      * on the selected file operation.
302:      * @param String $lockfile
303:      */
304:     public static function createLockfile($lockfile) {
305:         switch (self::$fileOper) {
306:             case 'ftp':
307:                 $stream = fopen('data://text/plain,'.time(), 'r');
308:                 if (!@ftp_fput(self::$_ftpStream, self::ftpStripChroot($lockfile), $stream, FTP_BINARY))
309:                     throw new Exception("Unable to create lockfile $lockfile", self::ERR_FTPOPER);
310:                 fclose($stream);
311:                 break;
312:             case 'scp':
313:                 $stream = @fopen ('ssh2.sftp://'.ssh2_sftp(self::$_sshStream).$lockfile, 'w');
314:                 if (!@fwrite ($stream, time()))
315:                     throw new Exception("Unable to create lockfile $lockfile", self::ERR_SCPOPER);
316:                 fclose ($stream);
317:                 break;
318:             case 'php':
319:             default:
320:                 file_put_contents($lockfile, time());
321:         }
322:     }
323: 
324:     /**
325:      * Initializes and returns a CURL resource handle
326:      * @param string $url
327:      * @param resource|null $context
328:      * @return resource
329:      */
330:     public static function curlInit($url){
331:         $ch = curl_init($url);
332:         $curlopt = array(
333:             CURLOPT_RETURNTRANSFER => 1,
334:             CURLOPT_BINARYTRANSFER => 1,
335:             CURLOPT_POST => 0,
336:             CURLOPT_TIMEOUT => 15
337:         );
338:         curl_setopt_array($ch, $curlopt);
339:         return $ch;
340:     }
341: 
342:     /**
343:      * Closes the current FTP stream and resets the file
344:      * operation method to 'php'
345:      */
346:     public static function ftpClose() {
347:         ftp_close(self::$_ftpStream);
348:         self::$ftpChroot = false;
349:         self::$fileOper = 'php';
350:     }
351: 
352:     /**
353:      * Initializes the FTP functionality. This connects to
354:      * the FTP server, logs in, and sets PASV mode.
355:      * Optionally, you can specify the chroot directory and a directory to
356:      * change to, e.g.: the web root or the test directory. This is recommended
357:      * if working with relative paths.
358:      * @param String $host The FTP server to connect to
359:      * @param String $user The FTP user.
360:      * @param String $pass Specified FTP user's password.
361:      * @param String $dir Initial directory to change to, or null by default
362:      * to disable.
363:      * @param String $chroot The chosen chroot directory for the user.
364:      */
365:     public static function ftpInit($host, $user, $pass, $dir = null, $chroot = null) {
366:         if (!self::$_ftpStream = ftp_connect($host))
367:             throw new Exception("The updater is unable to connect to $host. Please check your FTP connection settings.", self::ERR_FTPOPER);
368:         if (!@ftp_login(self::$_ftpStream, $user, $pass))
369:             throw new Exception("Unable to login as user $user", self::ERR_FTPOPER);
370:         ftp_pasv(self::$_ftpStream, true);
371:         if ($chroot !== null)
372:             self::$ftpChroot = $chroot;
373:         if ($dir !== null)
374:             self::cdftp($dir);
375:         self::$fileOper = "ftp";
376:     }
377: 
378:     public static function ftpStripChroot($dir) {
379:         if (self::$fileOper !== 'ftp' || self::$ftpChroot === false || self::isRelative($dir)) // Don't modify a relative path
380:             return $dir;
381:         else {
382:             $replaced = str_replace(self::$ftpChroot, '', $dir);
383:             // Add a leading slash if missing
384:             if (!preg_match('/^(\/|\\\)/', $replaced))
385:                     $replaced = DIRECTORY_SEPARATOR.$replaced;
386:             return $replaced;
387:         }
388:     }
389: 
390:     /**
391:      * Translate a relative path to absolute when using the SCP file operation
392:      */
393:     public static function scpXlateRelpath($path) {
394:         $oldpath = $path;
395:         if (preg_match ('/^\.\./', $path)) {
396:             $path = preg_replace ('/^\.\./', dirname (getcwd()), $path);
397:         } elseif (preg_match ('/^\./', $path)) {
398:             exit;
399:             $path = preg_replace ('/^\./', dirname (__FILE__), $path);
400:         }
401:         if ($oldpath !== $path)
402:             AuxLib::DebugLog ("Xlating $oldpath to $path");
403:         return $path;
404:     }
405: 
406:     /**
407:      * Initializes SCP file operation functionality. This connects to
408:      * the SSH server and authenticates as the provided user.
409:      * @param string $host The SSH server to connect to
410:      * @param string $user The SSH user.
411:      * @param string $pass Specified SSH user's password.
412:      */
413:     public static function sshInit($host, $user, $pass) {
414:         if (!self::$_sshStream = ssh2_connect ($host, 22))
415:             throw new Exception("Unable to connect to $host. Please check your SSH connection settings");
416:         if (!ssh2_auth_password (self::$_sshStream, $user, $pass))
417:             throw new Exception("Unable to login as user $user");
418:         self::$_sftpStream = ssh2_sftp(self::$_sshStream);
419:         self::$fileOper = "scp";
420:     }
421: 
422:     /**
423:      * Explicitly close the current SSH stream and resets the file
424:      * operation method to 'php'
425:      */
426:     public static function sshClose() {
427:         ssh2_exec (self::$_sshStream, 'exit;');
428:         self::$_sshStream = null;
429:         self::$fileOper = 'php';
430:     }
431:     /**
432:      * Wrapper for file_get_contents that attempts to use CURL if allow_url_fopen is disabled.
433:      *
434:      * @param string|RequestUtil $source
435:      * @param type $url
436:      * @return type
437:      * @throws Exception
438:      */
439:     public static function getContents($source, $use_include_path = false, $context = null){
440:         if(self::tryCurl(($source instanceof RequestUtil) ? $source->url : $source)){
441:             if ($source instanceof RequestUtil) {
442:                 $ch = $source->getCurlHandle ();
443:             } else {
444:                 $ch = self::curlInit($source, $context);
445:             }
446: 
447:             return @curl_exec($ch);
448:         }else{
449:             if ($source instanceof RequestUtil) {
450:                 $context = $source->getStreamContext ();
451:                 $source = $source->url;
452:             } 
453: 
454:             // Use the usual copy method
455:             return @file_get_contents($source, $use_include_path, $context);
456:         }
457:     }
458: 
459:     /**
460:      * Returns whether the given parameter is a relative path
461:      * @param string $path
462:      * @return boolean Whether the path is relative
463:      */
464:     public static function isRelative($path) {
465:         // Paths that start with .. or a word character, but not a Windows
466:         // drive specification (C:\).
467:         return preg_match('/^\.\./', $path) || preg_match('/^\w[^:]/', $path);
468:     }
469: 
470:     /**
471:      * Removes redundant up-one-level directory traversal from a path.
472:      *
473:      * Returns an array corresponding to each node in the path, with redundant
474:      *  directory traversal removed. For example, "items/files/stuff/../things"
475:      *  will be returned as array("items","files","things"). Note, however, that
476:      *  "../../stuff/things" will be returned as array("stuff","things"), which
477:      *  does not accurately reflect the actual path from the original working
478:      *  directory. The intention of this function was to clean up absolute paths.
479:      * @param array $path Path to clean
480:      */
481:     public static function noTraversal($path){
482:         $p2 = array();
483:         foreach($path as $node){
484:             if($node == '..'){
485:                 if(count($p2) > 0)
486:                     array_pop($p2);
487:             } else{
488:                 $p2[] = $node;
489:             }
490:         }
491:         return $p2;
492:     }
493: 
494:     /**
495:      * Remove a lockfile using the appropriate functionality, depending
496:      * on the selected file operation.
497:      * @param String $lockfile
498:      */
499:     public static function removeLockfile($lockfile) {
500:         switch (self::$fileOper) {
501:             case 'ftp':
502:                 $lockfile = self::ftpStripChroot($lockfile);
503:                 if (!@ftp_delete(self::$_ftpStream, $lockfile))
504:                     throw new Exception("Unable to delete the lockfile $lockfile", self::ERR_FTPOPER);
505:                 break;
506:             case 'scp':
507:                 if (!@ssh2_sftp_unlink (ssh2_sftp(self::$_sshStream), $lockfile))
508:                     throw new Exception("Unable to delete the lockfile $lockfile", self::ERR_SCPOPER);
509:                 break;
510:             case 'php':
511:             default:
512:                 unlink($lockfile);
513:         }
514:     }
515: 
516:     /**
517:      * Format a path so that it is platform-independent. Doesn't return false
518:      * if the path doesn't exist (so unlike realpath() it can be used to create
519:      * new files).
520:      *
521:      * @param string $path
522:      * @return string
523:      */
524:     public static function rpath($path){
525:         return implode(DIRECTORY_SEPARATOR, explode('/', $path));
526:     }
527: 
528:     /**
529:      * Calculates a relative path between two absolute paths.
530:      *
531:      * @param string $path The path to which the absolute path should be calculated.
532:      * @param string $start The starting path. Must be absolute, if specified, and
533:      *  must be specified if the path argument is not platform-agnostic.
534:      * @param string $ds Directory separator. If the two paths (path and start)
535:      *  use the (almost) ubiquitous convention of forward slashes, but the
536:      *  calculation is to take place on a Windows machine, this must be set to
537:      *  forward slash, so that
538:      */
539:     public static function relpath($path, $start = null, $ds = DIRECTORY_SEPARATOR){
540:         $thisPath = $start === null ? realpath('.').$ds : $start;
541:         // Get node arrays for each path:
542:         if(preg_match('/^([A-Z]):\\\\/', $thisPath, $match0)){ // Windows environment
543:             if(preg_match('/([A-Z]):\\\\/', $path, $match1)){ // Target path is absolute
544:                 if($match0[1] != $match1[1]) // Source and destination are on different drives. Regurgitate the absolute path.
545:                     return $path;
546:                 else{ // Source/destination on same drive.
547:                     $a1 = self::cleanDosPath($path);
548:                     array_shift($a1);
549:                     $a1 = self::noTraversal($a1);
550:                 }
551:             }else{ // Target path is relative
552:                 $a1 = self::noTraversal(explode($ds, $path));
553:             }
554:             $a0 = self::cleanDosPath($thisPath);
555:             array_shift($a0);
556:             $a0 = self::noTraversal($a0);
557:             array_pop($a0);
558:         }else{ // Unix environment. SO MUCH EASIER.
559:             $a0 = self::noTraversal(explode($ds, $thisPath));
560:             array_pop($a0);
561:             $a1 = self::noTraversal(explode($ds, $path));
562:         }
563:         // Find out what the paths have in common and calculate the number of levels to traverse up:
564:         $l = 0;
565:         while($l < count($a0) && $l < count($a1)){
566:             if($a0[$l] != $a1[$l])
567:                 break;
568:             $l++;
569:         }
570:         $lUp = count($a0) - $l;
571:         return str_repeat('..'.$ds, $lUp).implode($ds, array_slice($a1, $l));
572:     }
573: 
574:     /**
575:      * Recursively remove a directory and all its subdirectories.
576:      *
577:      * Walks a directory structure, removing files recursively. An optional
578:      * exclusion pattern can be included. If a directory contains a file that
579:      * matches the exclusion pattern, the directory and its ancestors will not
580:      * be deleted.
581:      *
582:      * @param string $path
583:      * @param string $noDelPat PCRE pattern for excluding files in deletion.
584:      */
585:     public static function rrmdir($path, $noDelPat = null){
586:         $useExclude = $noDelPat != null;
587:         $special = '/.*\/?\.+\/?$/';
588:         $excluded = false;
589:         if(!realpath($path))
590:             return false;
591:         $path = realpath($path);
592:         if(filetype($path) == 'dir'){
593:             $objects = scandir($path);
594:             foreach($objects as $object){
595:                 if(!preg_match($special, $object)){
596:                     if($useExclude){
597:                         if(!preg_match($noDelPat, $object)){
598:                             $excludeThis = self::rrmdir($path.DIRECTORY_SEPARATOR.$object, $noDelPat);
599:                             $excluded = $excluded || $excludeThis;
600:                         }else{
601:                             $excluded = true;
602:                         }
603:                     } else
604:                         self::rrmdir($path.DIRECTORY_SEPARATOR.$object, $noDelPat);
605:                 }
606:             }
607:             reset($objects);
608:             if(!$excluded)
609:                 if(!preg_match($special, $path))
610:                     switch (self::$fileOper) {
611:                         case 'ftp':
612:                             $path = self::ftpStripChroot($path);
613:                             ftp_rmdir(self::$_ftpStream, $path);
614:                             break;
615:                         case 'scp':
616:                             ssh2_sftp_rmdir (ssh2_sftp(self::$_sshStream), $path);
617:                             break;
618:                         case 'php':
619:                         default:
620:                             rmdir($path);
621:                     }
622:         } else
623:             switch (self::$fileOper) {
624:                 case 'ftp':
625:                     $path = self::ftpStripChroot($path);
626:                     ftp_delete(self::$_ftpStream, $path);
627:                     break;
628:                 case 'scp':
629:                     ssh2_sftp_unlink (ssh2_sftp(self::$_sshStream), $path);
630:                     break;
631:                 case 'php':
632:                 default:
633:                     unlink($path);
634:             }
635:         return $excluded;
636:     }
637: 
638:     /**
639:      * Create/return finfo resource handle
640:      *
641:      * @return resource
642:      */
643:     public static function finfo(){
644:         if(!isset(self::$_finfo))
645:             if(extension_loaded('fileinfo'))
646:                 self::$_finfo = finfo_open();
647:             else
648:                 self::$_finfo = false;
649:         return self::$_finfo;
650:     }
651: 
652:     /**
653:      * Create human-readable size string
654:      *
655:      * @param type $bytes
656:      * @return type
657:      */
658:     public static function formatSize($bytes, $places = 0){
659:         $sz = array('', 'K', 'M', 'G', 'T', 'P');
660:         $factor = floor((strlen($bytes) - 1) / 3);
661:         return sprintf("%.{$places}f ", $bytes / pow(1024, $factor)).@$sz[$factor]."B";
662:     }
663: 
664:     /**
665:      * The criteria for which CURL should be used.
666:      * @return type
667:      */
668:     public static function tryCurl($source){
669:         if (self::$neverCurl) return false;
670:         $try = preg_match('%^https?://%', $source) && (ini_get('allow_url_fopen') == 0 || self::$alwaysCurl);
671:         if($try)
672:             if(!extension_loaded('curl'))
673:                 throw new Exception('No HTTP methods available. Tried accessing a remote object, but allow_url_fopen is not enabled and the CURL extension is missing.', 500);
674:         return $try;
675:     }
676: 
677:     /**
678:      * Taken from CFileValidator
679:      * This method is Copyright (c) 2008-2014 by Yii Software LLC
680:      * http://www.yiiframework.com/license/ 
681:      */
682:     public static function sizeToBytes($sizeStr)
683:     {
684:         // get the latest character
685:         switch (strtolower(substr($sizeStr, -1)))
686:         {
687:             case 'm': return (int)$sizeStr * 1048576; // 1024 * 1024
688:             case 'k': return (int)$sizeStr * 1024; // 1024
689:             case 'g': return (int)$sizeStr * 1073741824; // 1024 * 1024 * 1024
690:             default: return (int)$sizeStr; // do nothing
691:         }
692:     }
693: 
694:     public static function sizeToMb ($sizeStr) {
695:         return self::sizeToBytes ($sizeStr) / 1048576;
696:     }
697: 
698: }
699: 
700: ?>
701: 
X2CRM Documentation API documentation generated by ApiGen 2.8.0