files.inc

  1. nittany7 modules/contrib/backup_migrate/includes/files.inc
  2. mooc7 modules/contrib/backup_migrate/includes/files.inc

General file handling code for Backup and Migrate.

Functions

Namesort descending Description
backup_migrate_temp_files_add Add a file to the temporary files list for deletion when we're done.
_backup_migrate_construct_filename Construct a filename using token and some cleaning.
_backup_migrate_default_filename Construct a default filename using the site's name.
_backup_migrate_filename_append_prepare Adjust the length of a filename to allow for a string to be appended, staying within the maximum filename limit.
_backup_migrate_filetypes Return a list of backup filetypes.
_backup_migrate_file_dispose_buffer An output buffer callback which simply throws away the buffer instead of sending it to the browser.
_backup_migrate_temp_files_delete Delete all temporary files.

Constants

Classes

Namesort descending Description
backup_file A backup file which allows for saving to and reading from the server.

File

modules/contrib/backup_migrate/includes/files.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * General file handling code for Backup and Migrate.
  5. */
  6. define('BACKUP_MIGRATE_FILENAME_MAXLENGTH', 255);
  7. /**
  8. * Add a file to the temporary files list for deletion when we're done.
  9. */
  10. function backup_migrate_temp_files_add($filepath = NULL) {
  11. static $files = array();
  12. if (!$filepath) {
  13. return $files;
  14. }
  15. else {
  16. $files[] = $filepath;
  17. }
  18. }
  19. /**
  20. * Delete all temporary files.
  21. */
  22. function _backup_migrate_temp_files_delete() {
  23. // Delete the temp files created during this run.
  24. foreach (backup_migrate_temp_files_add() as $file) {
  25. $file = drupal_realpath($file);
  26. if (file_exists($file) && is_writable($file)) {
  27. unlink($file);
  28. }
  29. }
  30. // Delete temp files abandoned for 6 or more hours.
  31. $dir = file_stream_wrapper_get_instance_by_scheme('temporary')->getDirectoryPath();
  32. $expire = time() - variable_get('backup_migrate_cleanup_time', 21600);
  33. if (file_exists($dir) && is_dir($dir) && is_readable($dir) && $handle = opendir($dir)) {
  34. while (FALSE !== ($file = @readdir($handle))) {
  35. // Delete 'backup_migrate_' files in the temp directory that are older than the expire time.
  36. // We should only attempt to delete writable files to prevent errors in shared environments.
  37. // This could still cause issues in shared environments with poorly configured file permissions.
  38. if (strpos($file, 'backup_migrate_') === 0 && is_writable("$dir/$file") && @filectime("$dir/$file") < $expire) {
  39. unlink("$dir/$file");
  40. }
  41. }
  42. closedir($handle);
  43. }
  44. }
  45. /**
  46. * Return a list of backup filetypes.
  47. */
  48. function _backup_migrate_filetypes() {
  49. backup_migrate_include('filters');
  50. $out = backup_migrate_filters_file_types();
  51. foreach ($out as $key => $info) {
  52. $out[$key]['id'] = empty($info['id']) ? $key : $info['id'];
  53. }
  54. return $out;
  55. }
  56. /**
  57. * Adjust the length of a filename to allow for a string to be appended,
  58. * staying within the maximum filename limit.
  59. */
  60. function _backup_migrate_filename_append_prepare($filename, $append_str) {
  61. $max_name_len = BACKUP_MIGRATE_FILENAME_MAXLENGTH - drupal_strlen($append_str);
  62. if (drupal_strlen($filename) > $max_name_len) {
  63. $filename = drupal_substr($filename, 0, $max_name_len);
  64. }
  65. return $filename;
  66. }
  67. /**
  68. * Construct a filename using token and some cleaning.
  69. */
  70. function _backup_migrate_construct_filename($filename, $timestamp='') {
  71. if (module_exists('token')) {
  72. $filename = token_replace($filename);
  73. }
  74. $filename = preg_replace("/[^a-zA-Z0-9\.\-_]/", "", $filename);
  75. $filename = _backup_migrate_filename_append_prepare($filename, $timestamp);
  76. $filename .= '-' . $timestamp;
  77. $filename = trim($filename, '-');
  78. if (drupal_strlen($filename) == 0) {
  79. $filename = 'untitled';
  80. }
  81. return $filename;
  82. }
  83. /**
  84. * Construct a default filename using the site's name.
  85. */
  86. function _backup_migrate_default_filename() {
  87. if (module_exists('token')) {
  88. return '[site:name]';
  89. }
  90. else {
  91. // Cleaning the string isn't strictly necessary but it looks better in the settings field.
  92. return variable_get('site_name', 'backup_migrate');
  93. }
  94. }
  95. /**
  96. * An output buffer callback which simply throws away the buffer instead of sending it to the browser.
  97. */
  98. function _backup_migrate_file_dispose_buffer($buffer) {
  99. return "";
  100. }
  101. /**
  102. * A backup file which allows for saving to and reading from the server.
  103. */
  104. class backup_file {
  105. var $file_info = array();
  106. var $type = array();
  107. var $ext = array();
  108. var $path = "";
  109. var $name = "";
  110. var $handle = NULL;
  111. /**
  112. * Construct a file object given a file path, or create a temp file for writing.
  113. */
  114. function backup_file($params = array()) {
  115. if (isset($params['filepath']) && file_exists($params['filepath'])) {
  116. $this->set_filepath($params['filepath']);
  117. }
  118. else {
  119. $this->set_file_info($params);
  120. $this->temporary_file();
  121. }
  122. }
  123. /**
  124. * Get the file_id if the file has been saved to a destination.
  125. */
  126. function file_id() {
  127. // The default file_id is the filename. Destinations can override the file_id if needed.
  128. return isset($this->file_info['file_id']) ? $this->file_info['file_id'] : $this->filename();
  129. }
  130. /**
  131. * Get the current filepath.
  132. */
  133. function filepath() {
  134. return drupal_realpath($this->path);
  135. }
  136. /**
  137. * Get the final filename.
  138. */
  139. function filename($name = NULL) {
  140. if ($name) {
  141. $this->name = $name;
  142. }
  143. return $this->name .'.'. $this->extension();
  144. }
  145. /**
  146. * Set the current filepath.
  147. */
  148. function set_filepath($path) {
  149. $this->path = $path;
  150. $params = array(
  151. 'filename' => basename($path),
  152. );
  153. if (file_exists($path)) {
  154. $params['filesize'] = filesize($path);
  155. $params['filetime'] = filectime($path);
  156. }
  157. $this->set_file_info($params);
  158. }
  159. /**
  160. * Get one or all pieces of info for the file.
  161. */
  162. function info($key = NULL) {
  163. if ($key) {
  164. return @$this->file_info[$key];
  165. }
  166. return $this->file_info;
  167. }
  168. /**
  169. * Get the file extension.
  170. */
  171. function extension() {
  172. return implode(".", $this->ext);
  173. }
  174. /**
  175. * Get the file type.
  176. */
  177. function type() {
  178. return $this->type;
  179. }
  180. /**
  181. * Get the file mimetype.
  182. */
  183. function mimetype() {
  184. return @$this->type['filemime'] ? $this->type['filemime'] : 'application/octet-stream';
  185. }
  186. /**
  187. * Get the file mimetype.
  188. */
  189. function type_id() {
  190. return @$this->type['id'];
  191. }
  192. /**
  193. * Can this file be used to backup to.
  194. */
  195. function can_backup() {
  196. return @$this->type['backup'];
  197. }
  198. /**
  199. * Can this file be used to restore to.
  200. */
  201. function can_restore() {
  202. return @$this->type['restore'];
  203. }
  204. /**
  205. * Can this file be used to restore to.
  206. */
  207. function is_recognized_type() {
  208. return @$this->type['restore'] || @$this->type['backup'];
  209. }
  210. /**
  211. * Open a file for reading or writing.
  212. */
  213. function open($write = FALSE, $binary = FALSE) {
  214. if (!$this->handle) {
  215. $path = $this->filepath();
  216. // Check if the file can be read/written.
  217. if ($write && ((file_exists($path) && !is_writable($path)) || !is_writable(dirname($path)))) {
  218. _backup_migrate_message('The file %path cannot be written to.', array('%path' => $path), 'error');
  219. return FALSE;
  220. }
  221. if (!$write && !is_readable($path)) {
  222. _backup_migrate_message('The file %path cannot be read.', array('%path' => $path), 'error');
  223. return FALSE;
  224. }
  225. // Open the file.
  226. $mode = ($write ? "w" : "r") . ($binary ? "b" : "");
  227. $this->handle = fopen($path, $mode);
  228. return $this->handle;
  229. }
  230. return NULL;
  231. }
  232. /**
  233. * Close a file when we're done reading/writing.
  234. */
  235. function close() {
  236. fclose($this->handle);
  237. $this->handle = NULL;
  238. }
  239. /**
  240. * Write a line to the file.
  241. */
  242. function write($data) {
  243. if (!$this->handle) {
  244. $this->handle = $this->open(TRUE);
  245. }
  246. if ($this->handle) {
  247. fwrite($this->handle, $data);
  248. }
  249. }
  250. /**
  251. * Read a line from the file.
  252. */
  253. function read($size = NULL) {
  254. if (!$this->handle) {
  255. $this->handle = $this->open();
  256. }
  257. if ($this->handle && !feof($this->handle)) {
  258. return $size ? fread($this->handle, $size) : fgets($this->handle);
  259. }
  260. return NULL;
  261. }
  262. /**
  263. * Write data to the file.
  264. */
  265. function put_contents($data) {
  266. file_put_contents($this->filepath(), $data);
  267. }
  268. /**
  269. * Read data from the file.
  270. */
  271. function get_contents() {
  272. return file_get_contents($this->filepath());
  273. }
  274. /**
  275. * Transfer file using http to client. Similar to the built in file_transfer,
  276. * but it calls module_invoke_all('exit') so that temp files can be deleted.
  277. */
  278. function transfer() {
  279. $headers = array(
  280. array('key' => 'Content-Type', 'value' => $this->mimetype()),
  281. array('key' => 'Content-Disposition', 'value' => 'attachment; filename="'. $this->filename() .'"'),
  282. );
  283. if ($size = $this->info('filesize')) {
  284. $headers[] = array('key' => 'Content-Length', 'value' => $size);
  285. }
  286. // Suppress the warning you get when the buffer is empty.
  287. @ob_end_clean();
  288. if ($this->open(FALSE, TRUE)) {
  289. foreach ($headers as $header) {
  290. // To prevent HTTP header injection, we delete new lines that are
  291. // not followed by a space or a tab.
  292. // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
  293. $header['value'] = preg_replace('/\r?\n(?!\t| )/', '', $header['value']);
  294. drupal_add_http_header($header['key'], $header['value']);
  295. }
  296. // Transfer file in 1024 byte chunks to save memory usage.
  297. while ($data = $this->read(1024)) {
  298. print $data;
  299. }
  300. $this->close();
  301. // Ask devel.module not to print it's footer.
  302. $GLOBALS['devel_shutdown'] = FALSE;
  303. }
  304. else {
  305. drupal_not_found();
  306. }
  307. // Start buffering and throw away the results so that errors don't get appended to the file.
  308. ob_start('_backup_migrate_file_dispose_buffer');
  309. backup_migrate_cleanup();
  310. module_invoke_all('exit');
  311. exit();
  312. }
  313. /**
  314. * Push a file extension onto the file and return the previous file path.
  315. */
  316. function push_type($extension) {
  317. $types = _backup_migrate_filetypes();
  318. if ($type = @$types[$extension]) {
  319. $this->push_filetype($type);
  320. }
  321. $out = $this->filepath();
  322. $this->temporary_file();
  323. return $out;
  324. }
  325. /**
  326. * Push a file extension onto the file and return the previous file path.
  327. */
  328. function pop_type() {
  329. $out = new backup_file(array('filepath' => $this->filepath()));
  330. $this->pop_filetype();
  331. $this->temporary_file();
  332. return $out;
  333. }
  334. /**
  335. * Set the current file type.
  336. */
  337. function set_filetype($type) {
  338. $this->type = $type;
  339. $this->ext = array($type['extension']);
  340. }
  341. /**
  342. * Set the current file type.
  343. */
  344. function push_filetype($type) {
  345. $this->ext[] = $type['extension'];
  346. $this->type = $type;
  347. }
  348. /**
  349. * Pop the current file type.
  350. */
  351. function pop_filetype() {
  352. array_pop($this->ext);
  353. $this->detect_filetype_from_extension();
  354. }
  355. /**
  356. * Set the file info.
  357. */
  358. function set_file_info($file_info) {
  359. $this->file_info = $file_info;
  360. $this->ext = explode('.', @$this->file_info['filename']);
  361. // Remove the underscores added to file extensions by Drupal's upload security.
  362. foreach ($this->ext as $key => $val) {
  363. $this->ext[$key] = trim($val, '_');
  364. }
  365. $this->filename(array_shift($this->ext));
  366. $this->detect_filetype_from_extension();
  367. }
  368. /**
  369. * Get the filetype info of the given file, or false if the file is not a valid type.
  370. */
  371. function detect_filetype_from_extension() {
  372. $ext = end($this->ext);
  373. $this->type = array();
  374. $types = _backup_migrate_filetypes();
  375. foreach ($types as $key => $type) {
  376. if (trim($ext, "_0123456789") === $type['extension']) {
  377. $this->type = $type;
  378. $this->type['id'] = $key;
  379. }
  380. }
  381. }
  382. /**
  383. * Get a temporary file name with path.
  384. */
  385. function temporary_file() {
  386. $file = drupal_tempnam('temporary://', 'backup_migrate_');
  387. // Add the version without the extension. The tempnam function creates this for us.
  388. backup_migrate_temp_files_add($file);
  389. if ($this->extension()) {
  390. $file .= '.'. $this->extension();
  391. // Add the version with the extension. This is the one we will actually use.
  392. backup_migrate_temp_files_add($file);
  393. }
  394. $this->path = $file;
  395. }
  396. }