PageRenderTime 21ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/trunk/squirrelmail/functions/files.php

#
PHP | 295 lines | 111 code | 56 blank | 128 comment | 41 complexity | af5955e7cf94d0fb19422e2010a4f599 MD5 | raw file
Possible License(s): AGPL-1.0, GPL-2.0
  1. <?php
  2. /**
  3. * files.php
  4. *
  5. * This file includes various helper functions for working
  6. * with the server filesystem.
  7. *
  8. * @copyright 2008-2012 The SquirrelMail Project Team
  9. * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  10. * @version $Id: files.php 14249 2012-01-02 02:09:17Z pdontthink $
  11. * @package squirrelmail
  12. */
  13. /**
  14. * Generates a unique file in a specific directory and
  15. * returns the file name (without the path).
  16. *
  17. * @param directory The directory within which to create the file
  18. *
  19. * @return mixed FALSE when a failure occurs, otherwise a string
  20. * containing the filename of the file only (not
  21. * its full path)
  22. *
  23. * @since 1.5.2
  24. *
  25. */
  26. function sq_create_tempfile($directory)
  27. {
  28. // give up after 1000 tries
  29. $maximum_tries = 1000;
  30. // using PHP >= 4.3.2 we can be truly atomic here
  31. $filemods = check_php_version(4, 3, 2) ? 'x' : 'w';
  32. for ($try = 0; $try < $maximum_tries; ++$try) {
  33. $localfilename = GenerateRandomString(32, '', 7);
  34. $full_localfilename = $directory . DIRECTORY_SEPARATOR . $localfilename;
  35. // filename collision. try again
  36. if ( file_exists($full_localfilename) ) {
  37. continue;
  38. }
  39. // try to open for (binary) writing
  40. $fp = @fopen( $full_localfilename, $filemods);
  41. if ($fp !== FALSE) {
  42. // success! make sure it's not readable, close and return filename
  43. chmod($full_localfilename, 0600);
  44. fclose($fp);
  45. return $localfilename;
  46. }
  47. }
  48. // we tried as many times as we could but didn't succeed.
  49. return FALSE;
  50. }
  51. /**
  52. * PHP's is_writable() is broken in some versions due to either
  53. * safe_mode or because of problems correctly determining the
  54. * actual file permissions under Windows. Under safe_mode or
  55. * Windows, we'll try to actually write something in order to
  56. * see for sure...
  57. *
  58. * @param string $path The full path to the file or directory to
  59. * be tested
  60. *
  61. * @return boolean Whether or not the file or directory exists
  62. * and is writable
  63. *
  64. * @since 1.5.2
  65. *
  66. **/
  67. function sq_is_writable($path) {
  68. global $server_os;
  69. // under *nix with safe_mode off, use the native is_writable()
  70. //
  71. if ($server_os == '*nix' && !(bool)ini_get('safe_mode'))
  72. return is_writable($path);
  73. // if it's a directory, that means we have to create a temporary
  74. // file therein
  75. //
  76. $delete_temp_file = FALSE;
  77. if (@is_dir($path) && ($temp_filename = @sq_create_tempfile($path)))
  78. {
  79. $path .= DIRECTORY_SEPARATOR . $temp_filename;
  80. $delete_temp_file = TRUE;
  81. }
  82. // try to open the file for writing (without trying to create it)
  83. //
  84. if (!@is_dir($path) && ($FILE = @fopen($path, 'r+')))
  85. {
  86. @fclose($FILE);
  87. // delete temp file if needed
  88. //
  89. if ($delete_temp_file)
  90. @unlink($path);
  91. return TRUE;
  92. }
  93. // delete temp file if needed
  94. //
  95. if ($delete_temp_file)
  96. @unlink($path);
  97. return FALSE;
  98. }
  99. /**
  100. * Find files and/or directories in a given directory optionally
  101. * limited to only those with the given file extension. If the
  102. * directory is not found or cannot be opened, no error is generated;
  103. * only an empty file list is returned.
  104. FIXME: do we WANT to throw an error or a notice or... or return FALSE?
  105. *
  106. * @param string $directory_path The path (relative or absolute)
  107. * to the desired directory.
  108. * @param mixed $extension The file extension filter - either
  109. * an array of desired extension(s),
  110. * or a comma-separated list of same
  111. * (optional; default is to return
  112. * all files (dirs).
  113. * @param boolean $return_filenames_only When TRUE, only file/dir names
  114. * are returned, otherwise the
  115. * $directory_path string is
  116. * prepended to each file/dir in
  117. * the returned list (optional;
  118. * default is filename/dirname only)
  119. * @param boolean $include_directories When TRUE, directories are
  120. * included (optional; default
  121. * DO include directories).
  122. * @param boolean $directories_only When TRUE, ONLY directories
  123. * are included (optional; default
  124. * is to include files too).
  125. * @param boolean $separate_files_and_directories When TRUE, files and
  126. * directories are returned
  127. * in separate lists, so
  128. * the return value is
  129. * formatted as a two-element
  130. * array with the two keys
  131. * "FILES" and "DIRECTORIES",
  132. * where corresponding values
  133. * are lists of either all
  134. * files or all directories
  135. * (optional; default do not
  136. * split up return array).
  137. * @param boolean $only_sm When TRUE, a security check will
  138. * limit directory access to only
  139. * paths within the SquirrelMail
  140. * installation currently being used
  141. * (optional; default TRUE)
  142. *
  143. * @return array The requested file/directory list(s).
  144. *
  145. * @since 1.5.2
  146. *
  147. */
  148. function list_files($directory_path, $extensions='', $return_filenames_only=TRUE,
  149. $include_directories=TRUE, $directories_only=FALSE,
  150. $separate_files_and_directories=FALSE, $only_sm=TRUE) {
  151. $files = array();
  152. $directories = array();
  153. // make sure requested path is under SM_PATH if needed
  154. //
  155. if ($only_sm) {
  156. if (strpos(realpath($directory_path), realpath(SM_PATH)) !== 0) {
  157. //plain_error_message(_("Illegal filesystem access was requested"));
  158. echo _("Illegal filesystem access was requested");
  159. exit;
  160. }
  161. }
  162. // validate given directory
  163. //
  164. if (empty($directory_path)
  165. || !is_dir($directory_path)
  166. || !($DIR = opendir($directory_path))) {
  167. return $files;
  168. }
  169. // ensure extensions is an array and is properly formatted
  170. //
  171. if (!empty($extensions)) {
  172. if (!is_array($extensions))
  173. $extensions = explode(',', $extensions);
  174. $temp_extensions = array();
  175. foreach ($extensions as $ext)
  176. $temp_extensions[] = '.' . trim(trim($ext), '.');
  177. $extensions = $temp_extensions;
  178. } else $extensions = array();
  179. $directory_path = rtrim($directory_path, '/');
  180. // parse through the files
  181. //
  182. while (($file = readdir($DIR)) !== false) {
  183. if ($file == '.' || $file == '..') continue;
  184. if (!empty($extensions))
  185. foreach ($extensions as $ext)
  186. if (strrpos($file, $ext) !== (strlen($file) - strlen($ext)))
  187. continue 2;
  188. // only use is_dir() if we really need to (be as efficient as possible)
  189. //
  190. $is_dir = FALSE;
  191. if (!$include_directories || $directories_only
  192. || $separate_files_and_directories) {
  193. if (is_dir($directory_path . '/' . $file)) {
  194. if (!$include_directories) continue;
  195. $is_dir = TRUE;
  196. $directories[] = ($return_filenames_only
  197. ? $file
  198. : $directory_path . '/' . $file);
  199. }
  200. if ($directories_only) continue;
  201. }
  202. if (!$separate_files_and_directories
  203. || ($separate_files_and_directories && !$is_dir)) {
  204. $files[] = ($return_filenames_only
  205. ? $file
  206. : $directory_path . '/' . $file);
  207. }
  208. }
  209. closedir($DIR);
  210. if ($directories_only) return $directories;
  211. if ($separate_files_and_directories) return array('FILES' => $files,
  212. 'DIRECTORIES' => $directories);
  213. return $files;
  214. }
  215. /**
  216. * Determine if there are lines in a file longer than a given length
  217. *
  218. * @param string $filename The full file path of the file to inspect
  219. * @param int $max_length If any lines in the file are GREATER THAN
  220. * this number, this function returns TRUE.
  221. *
  222. * @return boolean TRUE as explained above, otherwise, (no long lines
  223. * found) FALSE is returned.
  224. *
  225. */
  226. function file_has_long_lines($filename, $max_length) {
  227. $FILE = @fopen($filename, 'rb');
  228. if ($FILE) {
  229. while (!feof($FILE)) {
  230. $buffer = fgets($FILE, 4096);
  231. if (strlen($buffer) > $max_length) {
  232. fclose($FILE);
  233. return TRUE;
  234. }
  235. }
  236. fclose($FILE);
  237. }
  238. return FALSE;
  239. }