wCMF  3.6
 All Classes Namespaces Files Functions Variables Groups Pages
class.FileUtil.php
Go to the documentation of this file.
1 <?php
2 /**
3  * wCMF - wemove Content Management Framework
4  * Copyright (C) 2005-2014 wemove digital solutions GmbH
5  *
6  * Licensed under the terms of any of the following licenses
7  * at your choice:
8  *
9  * - GNU Lesser General Public License (LGPL)
10  * http://www.gnu.org/licenses/lgpl.html
11  * - Eclipse Public License (EPL)
12  * http://www.eclipse.org/org/documents/epl-v10.php
13  *
14  * See the license.txt file distributed with this work for
15  * additional information.
16  *
17  * $Id: class.FileUtil.php 1462 2014-02-04 23:52:27Z iherwig $
18  */
19 require_once(BASE."wcmf/lib/core/class.WCMFException.php");
20 require_once(BASE."wcmf/lib/util/class.Message.php");
21 if (file_exists(BASE."wcmf/lib/util/class.InifileParser.php"))
22  require_once(BASE."wcmf/lib/util/class.InifileParser.php");
23 
24 /**
25  * @class FileUtil
26  * @ingroup Util
27  * @brief FileUtil provides basic support for file functionality like HTTP file upload.
28  *
29  * @author ingo herwig <ingo@wemove.com>
30  */
31 class FileUtil
32 {
33  var $_errorMsg = '';
34 
35  /**
36  * Get last error message.
37  * @return The error string
38  */
39  function getErrorMsg()
40  {
41  return $this->_errorMsg;
42  }
43  /**
44  * Copy an uploaded file to a given destination (only if the mime type mathes the given one).
45  * @param mediaFile An assoziative array with the following keys: 'name', 'type', 'size', 'tmp_name' (typically a $HTTP_POST_FILES entry)
46  * @param destName The destination file name
47  * @param mimeType An array holding the allowed mimetypes, null if arbitrary [default: null]
48  * @param maxSize The maximum size of the file (if -1 it's not limited) [default: -1]
49  * @param override True/False whether an existing file should be overridden, if false an unque id will be placed in the filename to prevent overriding [default: true]
50  * @return The filename of the uploaded file or null if failed, error string provided by getErrorMsg()
51  */
52  function uploadFile($mediaFile, $destName, $mimeType=null, $maxSize=-1, $override=true)
53  {
54  $this->_errorMsg = '';
55  $filename = null;
56  if (is_uploaded_file($mediaFile['tmp_name']))
57  {
58  if ($mimeType == null || in_array($mediaFile['type'], $mimeType))
59  {
60  if ($mediaFile['size'] <= $maxSize || $maxSize == -1)
61  {
62  if ($override == false && file_exists($destName))
63  {
64  $pieces = split('\.', basename($destName));
65  $extension = array_pop($pieces);
66  $name = join('.', $pieces);
67  $destName = dirname($destName)."/".$name.uniqid(rand()).".".$extension;
68  }
69  $result = move_uploaded_file($mediaFile['tmp_name'], $destName);
70  if ($result === false)
71  {
72  $this->_errorMsg = Message::get("Failed to move %1% to %2%.", array($mediaFile['tmp_name'], $destName));
73  $this->_errorMsg .= "\n";
74  }
75  chmod($destName, 0644);
76  $filename = basename($destName);
77  }
78  else
79  {
80  $this->_errorMsg = Message::get("File too big: %1%. Allowed size: %2% bytes.", array($mediaFile['name'], $maxSize));
81  $this->_errorMsg .= "\n";
82  }
83  }
84  else
85  {
86  $this->_errorMsg .= Message::get("File '%1%' has wrong mime type: %2%. Allowed types: %3%.",
87  array($mediaFile['name'], $mediaFile['type'], join(", ", $mimeTypes)));
88  $this->_errorMsg .= "\n";
89  }
90  }
91  else
92  {
93  $this->_errorMsg = Message::get("Possible file upload attack: filename %1%.", array($mediaFile['name']));
94  if (!file_exists(BASE."wcmf/lib/util/class.InifileParser.php"))
95  {
96  $parser = &InifileParser::getInstance();
97  if(($maxFileSize = $parser->getValue('maxFileSize', 'htmlform')) !== false)
98  {
99  $this->_errorMsg .= Message::get("A possible reason is that the file size is too big (maximum allowed: %1% bytes).", array($maxFileSize));
100  $this->_errorMsg .= "\n";
101  }
102  }
103  }
104  return $filename;
105  }
106  /**
107  * Write unicode to file.
108  * @param fp File Handle
109  * @param str String to write
110  */
111  function fputsUnicode($fp, $str)
112  {
113  fputs($fp, utf8_encode($str));
114  }
115  /*
116  * Get the files in a directory that match a pattern
117  * @param dir The directory to search in
118  * @param pattern The pattern (regexp) to match [default: /./]
119  * @param prependDirectoryName True/False whether to prepend the directory name to each file [default: false]
120  * @param recursive True/False whether to recurse into subdirectories [default: false]
121  * @result An array containing the filenames sorted by modification date or null if failed, error string provided by getErrorMsg()
122  */
123  function getFiles($directory, $pattern='/./', $prependDirectoryName=false, $recursive=false)
124  {
125  $result = null;
126  if (strrpos($directory, '/') != strlen($directory)-1)
127  $directory .= '/';
128  if (is_dir($directory))
129  {
130  $result = array();
131  $d = dir($directory);
132  $d->rewind();
133  while(false !== ($file = $d->read()))
134  {
135  if($file != '.' && $file != '..')
136  {
137  if ($recursive && is_dir($directory.$file))
138  {
139  $files = FileUtil::getFiles($directory.$file, $pattern, $prependDirectoryName, $recursive);
140  $result = array_merge($result, $files);
141  }
142  else if(is_file($directory.$file) && preg_match($pattern, $file))
143  {
144  $sortkey = filectime($directory.$file).',';
145  if ($prependDirectoryName)
146  $file = $directory.$file;
147  $sortkey .= $file;
148  $result[$sortkey] = $file;
149  }
150  }
151  }
152  $d->close();
153  }
154  else
155  $this->_errorMsg = Message::get("The directory '%1%' does not exist.", array($directory));
156  krsort($result);
157  return array_values($result);
158  }
159  /*
160  * Get the directories in a directory that match a pattern
161  * @param dir The directory to search in
162  * @param pattern The pattern (regexp) to match [default: /./]
163  * @param prependDirectoryName True/False whether to prepend the directory name to each directory [default: false]
164  * @param recursive True/False whether to recurse into subdirectories [default: false]
165  * @result An array containing the directory names or null if failed, error string provided by getErrorMsg()
166  */
167  function getDirectories($directory, $pattern='/./', $prependDirectoryName=false, $recursive=false)
168  {
169  $result = null;
170  if (strrpos($directory, '/') != strlen($directory)-1)
171  $directory .= '/';
172  if (is_dir($directory))
173  {
174  $result = array();
175  $d = dir($directory);
176  $d->rewind();
177  while(false !== ($file = $d->read()))
178  {
179  if($file != '.' && $file != '..')
180  {
181  if (is_dir($directory.$file))
182  {
183  if ($recursive)
184  {
185  $dirs = FileUtil::getDirectories($directory.$file, $pattern, $prependDirectoryName, $recursive);
186  $result = array_merge($result, $dirs);
187  }
188  if(preg_match($pattern, $file))
189  {
190  if ($prependDirectoryName)
191  $file = $directory.$file;
192  array_push($result, $file);
193  }
194  }
195  }
196  }
197  $d->close();
198  }
199  else
200  $this->_errorMsg = Message::get("The directory '%1%' does not exist.", array($directory));
201  return $result;
202  }
203  /**
204  * Recursive copy for files/directories.
205  * @param source The name of the source directory/file
206  * @param dest The name of the destination directory/file
207  */
208  function copyRec($source, $dest)
209  {
210  if (is_file($source))
211  {
212  $perms = fileperms($source);
213  return copy($source, $dest) && chmod($dest, $perms);
214  }
215  else if (is_dir($source))
216  FileUtil::copyRecDir($source, $dest);
217 
218  else
219  WCMFException::throwEx(Message::get("Cannot copy %1% (it's neither a file nor a directory).", array($source)), __FILE__, __LINE__);
220  }
221  /**
222  * Recursive copy for directories.
223  * @param source The name of the source directory
224  * @param dest The name of the destination directory
225  */
226  function copyRecDir($source, $dest)
227  {
228  if (!is_dir($dest))
229  FileUtil::mkdirRec($dest);
230 
231  $dir = opendir($source);
232  while ($file = readdir($dir))
233  {
234  if ($file == "." || $file == "..")
235  continue;
236  FileUtil::copyRec("$source/$file", "$dest/$file");
237  }
238  closedir($dir);
239  }
240  /**
241  * Recursive directory creation.
242  * @param dirname The name of the directory
243  */
244  function mkdirRec($dirname)
245  {
246  $folder_list = split("/", $dirname);
247  $len = sizeof($folder_list);
248  for( $i=0; $i<$len; $i++ )
249  {
250  $tmp .= $folder_list[$i] . '/';
251  @mkdir($tmp);
252  chmod($tmp, 0755);
253  }
254  }
255  /**
256  * Empty a directory.
257  * @param dirname The name of the directory
258  */
259  function emptyDir($dirname)
260  {
261  $files = FileUtil::getFiles($dirname, '/./', true, true);
262  foreach ($files as $file)
263  unlink($file);
264  $dirs = FileUtil::getDirectories($dirname, '/./', true, true);
265  foreach ($dirs as $dir)
266  rmdir($dir);
267  }
268  /**
269  * Get the relative path between two paths
270  * code from http://php.net/manual/en/ref.filesystem.php
271  * @param path1 The first path
272  * @param path2 The second path
273  * @return string
274  */
275  function getRelativePath($path1, $path2)
276  {
277  $path1 = str_replace('\\', '/', $path1);
278  $path2 = str_replace('\\', '/', $path2);
279 
280  // remove starting, ending, and double / in paths
281  $path1 = trim($path1,'/');
282  $path2 = trim($path2,'/');
283  while (substr_count($path1, '//')) $path1 = str_replace('//', '/', $path1);
284  while (substr_count($path2, '//')) $path2 = str_replace('//', '/', $path2);
285 
286  // create arrays
287  $arr1 = explode('/', $path1);
288  if ($arr1 == array('')) $arr1 = array();
289  $arr2 = explode('/', $path2);
290  if ($arr2 == array('')) $arr2 = array();
291  $size1 = count($arr1);
292  $size2 = count($arr2);
293 
294  // now the hard part :-p
295  $path='';
296  for($i=0; $i<min($size1,$size2); $i++)
297  {
298  if ($arr1[$i] == $arr2[$i]) continue;
299  else $path = '../'.$path.$arr2[$i].'/';
300  }
301  if ($size1 > $size2)
302  for ($i = $size2; $i < $size1; $i++)
303  $path = '../'.$path;
304  else if ($size2 > $size1)
305  for ($i = $size1; $i < $size2; $i++)
306  $path .= $arr2[$i].'/';
307 
308  return $path;
309  }
310 
311  /**
312  * Sanitize a given filename (code from http://drupal.org/node/106377)
313  * @param name The file name
314  * @return The sanitized name
315  */
316  function sanitizeFilename($name)
317  {
318  $special_chars = array ("#","$","%","^","&","*","!","~","‘","\"","’","'","=","?","/","[","]","(",")","|","<",">",";","\\",",",".");
319  $name = preg_replace("/^[.]*/","",$name); // remove leading dots
320  $name = preg_replace("/[.]*$/","",$name); // remove trailing dots
321 
322  $lastdotpos=strrpos($name, "."); // save last dot position
323 
324  $name = str_replace($special_chars, "", $name); // remove special characters
325  $name = str_replace(' ','_',$name); // replace spaces with _
326 
327  $afterdot = "";
328  if ($lastdotpos !== false)
329  { // Split into name and extension, if any.
330  if ($lastdotpos < (strlen($name) - 1))
331  $afterdot = substr($name, $lastdotpos);
332 
333  $extensionlen = strlen($afterdot);
334 
335  if ($lastdotpos < (50 - $extensionlen) )
336  $beforedot = substr($name, 0, $lastdotpos);
337  else
338  $beforedot = substr($name, 0, (50 - $extensionlen));
339  }
340  else // no extension
341  $beforedot = substr($name,0,50);
342 
343  if ($afterdot)
344  $name = $beforedot . "." . $afterdot;
345  else
346  $name = $beforedot;
347 
348  return $name;
349  }
350 }
351 ?>
getRelativePath($path1, $path2)
get($message, $parameters=null, $domain='', $lang='')
uploadFile($mediaFile, $destName, $mimeType=null, $maxSize=-1, $override=true)
mkdirRec($dirname)
emptyDir($dirname)
copyRec($source, $dest)
throwEx($message, $file='', $line='')
getFiles($directory, $pattern='/./', $prependDirectoryName=false, $recursive=false)
copyRecDir($source, $dest)
sanitizeFilename($name)
fputsUnicode($fp, $str)
getDirectories($directory, $pattern='/./', $prependDirectoryName=false, $recursive=false)
FileUtil provides basic support for file functionality like HTTP file upload.