PageRenderTime 54ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/include/api/file.php

https://bitbucket.org/webop/webop-forum
PHP | 1382 lines | 613 code | 133 blank | 636 comment | 128 complexity | eded57ec950d1af17267760c310eeb8b MD5 | raw file
Possible License(s): LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. ////////////////////////////////////////////////////////////////////////////////
  3. // //
  4. // Copyright (C) 2010 Phorum Development Team //
  5. // http://www.phorum.org //
  6. // //
  7. // This program is free software. You can redistribute it and/or modify //
  8. // it under the terms of either the current Phorum License (viewable at //
  9. // phorum.org) or the Phorum License that was distributed with this file //
  10. // //
  11. // This program is distributed in the hope that it will be useful, //
  12. // but WITHOUT ANY WARRANTY, without even the implied warranty of //
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. //
  14. // //
  15. // You should have received a copy of the Phorum License //
  16. // along with this program. //
  17. // //
  18. ////////////////////////////////////////////////////////////////////////////////
  19. /**
  20. * This script implements the Phorum file storage API.
  21. *
  22. * A "Phorum file" is a file which is used from within Phorum as a
  23. * personal user file (which can be uploaded through the user's control
  24. * center) or as a message attachment.
  25. *
  26. * By default, the contents of a Phorum file are stored in the Phorum
  27. * database, but this API does support modules that change this behavior
  28. * (e.g. by storing file contents on a filesystem instead).
  29. *
  30. * @package PhorumAPI
  31. * @subpackage FileStorageAPI
  32. * @copyright 2010, Phorum Development Team
  33. * @license Phorum License, http://www.phorum.org/license.txt
  34. */
  35. if (!defined("PHORUM")) return;
  36. // {{{ Constant and variable definitions
  37. /**
  38. * Function call flag, which tells {@link phorum_api_file_retrieve()}
  39. * that the retrieved Phorum file data has to be returned to the caller.
  40. */
  41. define("PHORUM_FLAG_GET", 1);
  42. /**
  43. * Function call flag, which tells {@link phorum_api_file_retrieve()}
  44. * that the retrieved Phorum file can be sent to the browser directly.
  45. */
  46. define("PHORUM_FLAG_SEND", 2);
  47. /**
  48. * Function call flag, which tells the function to skip any
  49. * permission checks.
  50. */
  51. define("PHORUM_FLAG_IGNORE_PERMS", 4);
  52. /**
  53. * Function call flag, which tells {@link phorum_api_file_retrieve()}
  54. * to force a download by the browser by sending an application/octet-stream
  55. * Content-Type header. This flag will only have effect if the
  56. * {@link PHORUM_FLAG_SEND} flag is set as well.
  57. */
  58. define("PHORUM_FLAG_FORCE_DOWNLOAD", 8);
  59. /**
  60. * A mapping of file extensions to their MIME types.
  61. * Used by function {@link phorum_api_file_get_mimetype()}.
  62. */
  63. $GLOBALS["PHORUM"]["phorum_api_file_mimetypes"] = array
  64. (
  65. "pdf" => "application/pdf",
  66. "ps" => "application/postscript",
  67. "doc" => "application/msword",
  68. "xls" => "application/vnd.ms-excel",
  69. "gif" => "image/gif",
  70. "png" => "image/png",
  71. "jpg" => "image/jpeg",
  72. "jpeg" => "image/jpeg",
  73. "jpe" => "image/jpeg",
  74. "bmp" => "image/x-ms-bmp",
  75. "tiff" => "image/tiff",
  76. "tif" => "image/tiff",
  77. "xml" => "text/xml",
  78. "mpeg" => "video/mpeg",
  79. "mpg" => "video/mpeg",
  80. "mpe" => "video/mpeg",
  81. "qt" => "video/quicktime",
  82. "mov" => "video/quicktime",
  83. "avi" => "video/x-msvideo",
  84. "gz" => "application/x-gzip",
  85. "tgz" => "application/x-gzip",
  86. "zip" => "application/zip",
  87. "tar" => "application/x-tar",
  88. "exe" => "application/octet-stream",
  89. "rar" => "application/octet-stream",
  90. "wma" => "application/octet-stream",
  91. "wmv" => "application/octet-stream",
  92. "mp3" => "audio/mpeg",
  93. );
  94. // }}}
  95. // {{{ Function: phorum_api_file_get_mimetype
  96. /**
  97. * Lookup the MIME type for a given filename.
  98. *
  99. * This will use an internal lookup list of known file extensions
  100. * to find the correct content type for a filename. If no content type
  101. * is known, then "application/octet-stream" will be used as the
  102. * MIME type (causing the browser to download the file, instead of
  103. * opening it).
  104. *
  105. * @param string $filename
  106. * The filename for which to lookup the MIME type.
  107. *
  108. * @return string
  109. * The MIME type for the given filename.
  110. */
  111. function phorum_api_file_get_mimetype($filename)
  112. {
  113. $types = $GLOBALS["PHORUM"]["phorum_api_file_mimetypes"];
  114. $extension = "";
  115. $dotpos = strrpos($filename, ".");
  116. if ($dotpos !== FALSE) {
  117. $extension = strtolower(substr($filename, $dotpos+1));
  118. }
  119. $mime_type = isset($types[$extension])
  120. ? $types[$extension]
  121. : "application/octet-stream";
  122. return $mime_type;
  123. }
  124. // }}}
  125. // {{{ Function: phorum_api_file_check_write_access
  126. /**
  127. * Check if the active user has permissions to store a personal
  128. * file or a message attachment.
  129. *
  130. * Note that the checks for message attachments aren't all checks that are
  131. * done by Phorum. The attachment posting script does run some additional
  132. * checks on the message level (e.g. to see if the maximum cumulative
  133. * attachment size is not exceeded).
  134. *
  135. * @example file_store.php Store a personal file.
  136. *
  137. * @param array $file
  138. * This is an array, containing information about the
  139. * file that will be uploaded. The array should contain at least the
  140. * "link" field. That field will be used to handle checking for personal
  141. * uploaded files in the control center (PHORUM_LINK_USER) or message
  142. * attachments (PHORUM_LINK_MESSAGE). Next to that, interesting file
  143. * fields to pass to this function are "filesize" (to check maximum size)
  144. * and "filename" (to check allowed file type extensions). A "user_id"
  145. * field can either be provided or the user_id of the active Phorum
  146. * user will be used.
  147. *
  148. * @return array
  149. * If access is allowed, then TRUE will be returned. If access is denied,
  150. * then FALSE will be returned. The functions {@link phorum_api_strerror()}
  151. * and {@link phorum_api_errno()} can be used to retrieve information
  152. * about the error which occurred.
  153. */
  154. function phorum_api_file_check_write_access($file)
  155. {
  156. $PHORUM = $GLOBALS["PHORUM"];
  157. // Reset error storage.
  158. $GLOBALS["PHORUM"]["API"]["errno"] = NULL;
  159. $GLOBALS["PHORUM"]["API"]["error"] = NULL;
  160. if (!isset($file["link"])) trigger_error(
  161. "phorum_api_file_check_write_access(): \$file parameter needs a " .
  162. "\"link\" field.",
  163. E_USER_ERROR
  164. );
  165. if (empty($file["user_id"])) {
  166. $file["user_id"] = $PHORUM["user"]["user_id"];
  167. }
  168. // ---------------------------------------------------------------------
  169. // Handle write access checks for uploading user files.
  170. // ---------------------------------------------------------------------
  171. if ($file["link"] == PHORUM_LINK_USER)
  172. {
  173. // If file uploads are enabled, then access is granted. Access
  174. // is always granted to administrator users.
  175. if (!$PHORUM["file_uploads"] && !$PHORUM["user"]["admin"]) {
  176. return phorum_api_error_set(
  177. PHORUM_ERRNO_NOACCESS,
  178. $PHORUM["DATA"]["LANG"]["UploadNotAllowed"]
  179. );
  180. }
  181. // Check if the file doesn't exceed the maximum allowed file size.
  182. if (isset($file["filesize"]) && $PHORUM["max_file_size"] > 0 &&
  183. $file["filesize"] > $PHORUM["max_file_size"]*1024) {
  184. return phorum_api_error_set(
  185. PHORUM_ERRNO_NOACCESS,
  186. $PHORUM["DATA"]["LANG"]["FileTooLarge"]
  187. );
  188. }
  189. // Check if the user won't exceed the file quota when storing the file.
  190. if(isset($file["filesize"]) && $PHORUM["file_space_quota"] > 0) {
  191. $sz = phorum_db_get_user_filesize_total($PHORUM["user"]["user_id"]);
  192. $sz += $file["filesize"];
  193. if ($sz > $PHORUM["file_space_quota"]*1024) {
  194. return phorum_api_error_set(
  195. PHORUM_ERRNO_NOACCESS,
  196. $PHORUM["DATA"]["LANG"]["FileOverQuota"]
  197. );
  198. }
  199. }
  200. // Check if the file type is allowed.
  201. if (isset($file["filename"]) &&
  202. isset($PHORUM["file_types"]) && trim($PHORUM["file_types"]) != '')
  203. {
  204. // Determine the file extension for the file.
  205. $pos = strrpos($file["filename"], ".");
  206. if ($pos !== FALSE) {
  207. $ext = strtolower(substr($file["filename"], $pos + 1));
  208. } else {
  209. $ext = strtolower($file["filename"]);
  210. }
  211. // Create an array of allowed file extensions.
  212. $allowed_exts = explode(";", strtolower($PHORUM["file_types"]));
  213. // Check if the extension for the file is an allowed extension.
  214. if (!in_array($ext, $allowed_exts)) {
  215. return phorum_api_error_set(
  216. PHORUM_ERRNO_NOACCESS,
  217. $PHORUM["DATA"]["LANG"]["FileWrongType"]
  218. );
  219. }
  220. }
  221. }
  222. // ---------------------------------------------------------------------
  223. // Handle write access checks for uploading message attachment files.
  224. // ---------------------------------------------------------------------
  225. elseif ($file["link"] == PHORUM_LINK_EDITOR ||
  226. $file["link"] == PHORUM_LINK_MESSAGE) {
  227. // Check if the file doesn't exceed the maximum allowed file size
  228. // for the active forum.
  229. if (isset($file["filesize"]))
  230. {
  231. // Find the maximum allowed attachment size. This depends on
  232. // both the settings for the current forum and the limits
  233. // that are enforced by the system.
  234. require_once('./include/upload_functions.php');
  235. $max_upload = phorum_get_system_max_upload();
  236. $max_forum = $PHORUM["max_attachment_size"] * 1024;
  237. if ($max_forum > 0 && $max_forum < $max_upload)
  238. $max_upload = $max_forum;
  239. // Check if the file doesn't exceed the maximum allowed size.
  240. if ($max_upload > 0 && $file["filesize"] > $max_upload) {
  241. return phorum_api_error_set(
  242. PHORUM_ERRNO_NOACCESS,
  243. str_replace(
  244. '%size%', phorum_filesize($max_upload),
  245. $PHORUM["DATA"]["LANG"]["AttachFileSize"]
  246. )
  247. );
  248. }
  249. }
  250. // Check if the file type is allowed for the active forum.
  251. if (isset($file["filename"]) &&
  252. isset($PHORUM["allow_attachment_types"]) &&
  253. trim($PHORUM["allow_attachment_types"]) != '')
  254. {
  255. // Determine the file extension for the file.
  256. $pos = strrpos($file["filename"], ".");
  257. if ($pos !== FALSE) {
  258. $ext = strtolower(substr($file["filename"], $pos + 1));
  259. } else {
  260. $ext = strtolower($file["filename"]);
  261. }
  262. // Create an array of allowed file extensions.
  263. $allowed_exts = explode(";", strtolower($PHORUM["allow_attachment_types"]));
  264. // Check if the extension for the file is an allowed extension.
  265. if (!in_array($ext, $allowed_exts)) {
  266. return phorum_api_error_set(
  267. PHORUM_ERRNO_NOACCESS,
  268. $PHORUM["DATA"]["LANG"]["AttachInvalidType"] . " ".
  269. str_replace(
  270. '%types%', implode(", ", $allowed_exts),
  271. $PHORUM["DATA"]["LANG"]["AttachFileTypes"]
  272. )
  273. );
  274. }
  275. }
  276. }
  277. return TRUE;
  278. }
  279. // }}}
  280. // {{{ Function: phorum_api_file_store
  281. /**
  282. * Store or update a file.
  283. *
  284. * @example file_store.php Store a personal file.
  285. *
  286. * @param array $file
  287. * An array, containing information for the file.
  288. * This array has to contain the following fields:
  289. * <ul>
  290. * <li>filename: The name of the file.</li>
  291. * <li>file_data: The file data.</li>
  292. * <li>filesize: The size of the file data in bytes.</li>
  293. * <li>link: A value describing to what type of entity the file is
  294. * linked. The following values are available:
  295. * <ul>
  296. * <li>PHORUM_LINK_USER</li>
  297. * <li>PHORUM_LINK_MESSAGE</li>
  298. * <li>PHORUM_LINK_EDITOR</li>
  299. * <li>PHORUM_LINK_TEMPFILE</li>
  300. * </ul>
  301. * </li>
  302. * <li>user_id: The user to link a file to. If none is provided, then
  303. the user_id of the active Phorum user will be used.</li>
  304. * <li>message_id: The message to link a file to or 0 if it's no
  305. * message attachment.</li>
  306. * </ul>
  307. *
  308. * Additionally, the "file_id" field can be set. If it is set,
  309. * then the existing file will be updated. If it is not set,
  310. * a new file will be created.
  311. *
  312. * @return mixed
  313. * On error, this function will return FALSE. The functions
  314. * {@link phorum_api_strerror()} and {@link phorum_api_errno()} can
  315. * be used to retrieve information about the error which occurred.
  316. *
  317. * On success, an array containing the data for the stored file
  318. * will be returned. If the function is called with no "file_id"
  319. * in the {@link $file} argument (when a new file is stored),
  320. * then the new "file_id" and "add_datetime" fields will be
  321. * included in the return variable as well.
  322. */
  323. function phorum_api_file_store($file)
  324. {
  325. $PHORUM = $GLOBALS["PHORUM"];
  326. // Check if we really got an array argument for $file.
  327. if (!is_array($file)) trigger_error(
  328. "phorum_api_file_store(): \$file parameter must be an array.",
  329. E_USER_ERROR
  330. );
  331. // Check and preprocess the data from the $file argument.
  332. // First we create a new empty file structure to fill.
  333. $checkfile = array(
  334. "file_id" => NULL,
  335. "user_id" => NULL,
  336. "filename" => NULL,
  337. "filesize" => NULL,
  338. "file_data" => NULL,
  339. "message_id" => NULL,
  340. "link" => NULL,
  341. );
  342. // Go over all fields in the $file argument and add these to
  343. // the $checkfile array.
  344. foreach ($file as $k => $v)
  345. {
  346. switch ($k)
  347. {
  348. case "file_id":
  349. case "user_id":
  350. case "message_id":
  351. case "filesize":
  352. if ($v !== NULL) settype($v, "int");
  353. $checkfile[$k] = $v;
  354. break;
  355. case "filename":
  356. $v = basename($v);
  357. $checkfile[$k] = $v;
  358. break;
  359. case "link":
  360. case "file_data":
  361. $checkfile[$k] = $v;
  362. break;
  363. case "add_datetime":
  364. $checkfile[$k] = (int)$v;
  365. break;
  366. case "result":
  367. case "mime_type":
  368. // These are some dynamic fields which might be present
  369. // in the data, when storing file data that was returned
  370. // by the file retrieve function. We simply skip these here.
  371. break;
  372. default:
  373. trigger_error(
  374. "phorum_api_file_store(): \$file parameter contains " .
  375. 'an illegal field "'.htmlspecialchars($k).'".',
  376. E_USER_ERROR
  377. );
  378. }
  379. }
  380. // Set user_id to current user if one was not passed in
  381. if (empty($checkfile["user_id"]) && isset($PHORUM["user"]["user_id"])) {
  382. $checkfile["user_id"] = $PHORUM["user"]["user_id"];
  383. }
  384. // Force the message_id to 0, depending on the
  385. // link type. Also check if the required id field (user or
  386. // message) is set for the used link type.
  387. switch ($checkfile["link"])
  388. {
  389. case PHORUM_LINK_EDITOR:
  390. $checkfile["message_id"] = 0;
  391. break;
  392. case PHORUM_LINK_USER:
  393. $checkfile["message_id"] = 0;
  394. if (empty($checkfile["user_id"])) trigger_error (
  395. "phorum_api_file_store(): \$file set the link type to " .
  396. "PHORUM_LINK_USER, but the user_id was not set.",
  397. E_USER_ERROR
  398. );
  399. break;
  400. case PHORUM_LINK_MESSAGE:
  401. if (empty($checkfile["message_id"])) trigger_error (
  402. "phorum_api_file_store(): \$file set the link type to " .
  403. "PHORUM_LINK_MESSAGE, but the message_id was not set.",
  404. E_USER_ERROR
  405. );
  406. break;
  407. default:
  408. if (empty($checkfile["message_id"])) {
  409. $checkfile["message_id"] = 0;
  410. }
  411. break;
  412. }
  413. // See if all required values are set.
  414. foreach ($checkfile as $k => $v) {
  415. if ($k == 'file_id') continue; // is NULL for new files.
  416. if ($v === NULL) trigger_error(
  417. "phorum_api_file_store(): \$file parameter misses the " .
  418. '"' . htmlspecialchars($k) . '" field.',
  419. E_USER_ERROR
  420. );
  421. }
  422. // All data was checked, so now we can continue with the checked data.
  423. $file = $checkfile;
  424. // New files need a file_id and an add_datetime timestamp.
  425. $created_skeleton_file = FALSE;
  426. if (empty($file["file_id"]))
  427. {
  428. $add_datetime = time();
  429. // Insert a skeleton file record in the database. We do this, to
  430. // get hold of a new file_id. That file_id can be passed on to
  431. // the hook below, so alternative storage systems know directly
  432. // for what file_id they will have to store data, without having
  433. // to store the full data in the database already.
  434. $file_id = phorum_db_file_save(array(
  435. "filename" => $file["filename"],
  436. "filesize" => 0,
  437. "file_data" => "",
  438. "user_id" => 0,
  439. "message_id" => 0,
  440. "link" => PHORUM_LINK_TEMPFILE,
  441. "add_datetime" => $add_datetime
  442. ));
  443. $file["file_id"] = $file_id;
  444. $file["add_datetime"] = $add_datetime;
  445. $created_skeleton_file = TRUE;
  446. }
  447. // Allow modules to handle file data storage. If a module implements
  448. // a different data storage method, it can store the file data in its
  449. // own way and set the "file_data" field to an empty string in $file
  450. // (it is not mandatory to do so, but it is adviceable, since it
  451. // would make no sense to store the file data both in an alternative
  452. // storage and the database at the same time).
  453. // The hook can use phorum_api_error_set() to return an error.
  454. // Hooks should be aware that their input might not be $file, but
  455. // FALSE instead, in which case they should immediately return
  456. // FALSE themselves.
  457. if (isset($PHORUM["hooks"]["file_store"]))
  458. {
  459. $hook_result = phorum_hook("file_store", $file);
  460. // Return if a module returned an error.
  461. if ($hook_result === FALSE)
  462. {
  463. // Cleanup the skeleton file from the database.
  464. if ($created_skeleton_file) {
  465. phorum_db_file_delete($file["file_id"]);
  466. $file["file_id"] = NULL;
  467. }
  468. return FALSE;
  469. }
  470. $file = $hook_result;
  471. }
  472. // Phorum stores the files in base64 format in the database, to
  473. // prevent problems with upgrading and migrating database servers.
  474. // The ASCII representation for the files will always be safe to dump
  475. // and restore. So here we will base64 encode the file data.
  476. //
  477. // If the file_data field is an empty string by now, then either the
  478. // file data was really empty to start with or a module handled the
  479. // storage. In both cases it's fine to keep the data field empty.
  480. if ($file["file_data"] != '') {
  481. $file["file_data"] = base64_encode($file["file_data"]);
  482. }
  483. // Update the (skeleton) file record to match the real file data.
  484. // This acts like a commit action for the file storage.
  485. phorum_db_file_save ($file);
  486. return $file;
  487. }
  488. // }}}
  489. // {{{ Function: phorum_api_file_check_read_access
  490. /**
  491. * Check if a file exists and if the active user has permission to read it.
  492. *
  493. * The function will return either an array containing descriptive data
  494. * for the file or FALSE, in case access was not granted.
  495. *
  496. * Note that the file_data field is not available in the return array.
  497. * That data can be retrieved by the {@link phorum_api_file_retrieve()}
  498. * function.
  499. *
  500. * @param integer $file_id
  501. * The file_id of the file for which to check read access.
  502. *
  503. * @param integer $flags
  504. * If the {@link PHORUM_FLAG_IGNORE_PERMS} flag is used, then permission
  505. * checks are fully bypassed. In this case, the function will only check
  506. * if the file exists or not.
  507. *
  508. * @return mixed
  509. * On error, this function will return FALSE.
  510. * The functions {@link phorum_api_strerror()} and
  511. * {@link phorum_api_errno()} can be used to retrieve information about
  512. * the error which occurred.
  513. *
  514. * On success, it returns an array containing descriptive data for
  515. * the file. The following fields are available in this array:
  516. * <ul>
  517. * <li>file_id: The file_id for the requested file.</li>
  518. * <li>filename: The name of the file.</li>
  519. * <li>filesize: The size of the file in bytes.</li>
  520. * <li>add_datetime: Epoch timestamp describing at what time
  521. * the file was stored.</li>
  522. * <li>message_id: The message to which a message is linked
  523. * (in case it is a message attachment).</li>
  524. * <li>user_id: The user to which a message is linked
  525. * (in case it is a private user file).</li>
  526. * <li>link: A value describing to what type of entity the file is
  527. * linked. One of {@link PHORUM_LINK_USER},
  528. * {@link PHORUM_LINK_MESSAGE}, {@link PHORUM_LINK_EDITOR} and
  529. * {@link PHORUM_LINK_TEMPFILE}.</li>
  530. * </ul>
  531. */
  532. function phorum_api_file_check_read_access($file_id, $flags = 0)
  533. {
  534. global $PHORUM;
  535. settype($file_id, "int");
  536. // Reset error storage.
  537. $GLOBALS["PHORUM"]["API"]["errno"] = NULL;
  538. $GLOBALS["PHORUM"]["API"]["error"] = NULL;
  539. // Check if the active user has read access for the active forum_id.
  540. if (!($flags & PHORUM_FLAG_IGNORE_PERMS) && !phorum_check_read_common()) {
  541. return phorum_api_error_set(
  542. PHORUM_ERRNO_NOACCESS,
  543. "Read permission for file (id $file_id) denied."
  544. );
  545. }
  546. // Retrieve the descriptive file data for the file from the database.
  547. // Return an error if the file does not exist.
  548. $file = phorum_db_file_get($file_id, FALSE);
  549. if (empty($file)) return phorum_api_error_set(
  550. PHORUM_ERRNO_NOTFOUND,
  551. "The requested file (id $file_id) was not found."
  552. );
  553. // For the standard database based file storage, we do not have to
  554. // do checks for checking file existence (since the data is in the
  555. // database and we found the record for it). Storage modules might
  556. // have to do additional checks though (e.g. to see if the file data
  557. // exists on disk), so here we give them a chance to check for it.
  558. // This hook can also be used for implementing additional access
  559. // rules. The hook can use phorum_api_error_set() to return an error.
  560. // Hooks should be aware that their input might not be $file, but
  561. // FALSE instead, in which case they should immediately return
  562. // FALSE themselves.
  563. if (isset($PHORUM["hooks"]["file_check_read_access"])) {
  564. $file = phorum_hook("file_check_read_access", $file, $flags);
  565. if ($file === FALSE) return FALSE;
  566. }
  567. // If we do not do any permission checking, then we are done.
  568. if ($flags & PHORUM_FLAG_IGNORE_PERMS) return $file;
  569. // If PHORUM_ADMIN is defined, we don't need to check permissions
  570. if (defined("PHORUM_ADMIN")) return $file;
  571. // Is the file linked to a forum message? In that case, we have to
  572. // check if the message does really belong to the requested forum_id.
  573. if ($file["link"] == PHORUM_LINK_MESSAGE && !empty($file["message_id"]))
  574. {
  575. // Retrieve the message. If retrieving the message is not possible
  576. // or if the forum_id of the message is different from the requested
  577. // forum_id, then return an error.
  578. $message = phorum_db_get_message($file["message_id"],"message_id",TRUE);
  579. if (empty($message)) return phorum_api_error_set(
  580. PHORUM_ERRNO_INTEGRITY,
  581. "An integrity problem was detected in the database: " .
  582. "file id $file_id is linked to non existent " .
  583. "message_id {$file["message_id"]}."
  584. );
  585. if ($message["forum_id"] != $PHORUM["forum_id"]) {
  586. return phorum_api_error_set(
  587. PHORUM_ERRNO_NOACCESS,
  588. "Permission denied for reading the file: it does not " .
  589. "belong to the requested forum_id {$PHORUM["forum_id"]}."
  590. );
  591. }
  592. }
  593. // A general purpose URL host matching regexp, that we'll use below.
  594. $matchhost = '!^https?://([^/]+)/!i';
  595. // See if off site links are allowed. If this is not the case, then
  596. // check if an off site link is requested. We use the HTTP_REFERER for
  597. // doing the off site link check. This is not a water proof solution
  598. // (since HTTP referrers can be faked), but it will be good enough for
  599. // stopping the majority of the off site requests.
  600. if (isset($_SERVER["HTTP_REFERER"]) &&
  601. $PHORUM["file_offsite"] != PHORUM_OFFSITE_ANYSITE &&
  602. preg_match($matchhost, $_SERVER["HTTP_REFERER"])) {
  603. // Generate the base URL for the Phorum.
  604. $base = strtolower(phorum_get_url(PHORUM_BASE_URL));
  605. // Strip query string from the base URL. We mainly want to
  606. // check if the location matches the Phorum location.
  607. // Otherwise, we might get in troubles with things like
  608. // URI authentication, where a session id is added to the URL.
  609. $base = preg_replace('/\?.*$/', '', $base);
  610. // FORUMONLY: Links to forum files are only allowed from the forum.
  611. // Check if the referrer URL starts with the base Phorum URL.
  612. if ($PHORUM["file_offsite"] == PHORUM_OFFSITE_FORUMONLY) {
  613. $refbase = substr($_SERVER["HTTP_REFERER"], 0, strlen($base));
  614. if (strcasecmp($base, $refbase) != 0) return phorum_api_error_set(
  615. PHORUM_ERRNO_NOACCESS,
  616. "Permission denied: links to files in the forum are " .
  617. "only allowed from the forum itself."
  618. );
  619. }
  620. // THISSITE: Links to forum files are allowed from anywhere on
  621. // the website where Phorum is hosted.
  622. elseif ($PHORUM["file_offsite"] == PHORUM_OFFSITE_THISSITE) {
  623. if (preg_match($matchhost, $_SERVER["HTTP_REFERER"], $rm) &&
  624. preg_match($matchhost, $base, $bm) &&
  625. strcasecmp($rm[1], $bm[1]) != 0) return phorum_api_error_set(
  626. PHORUM_ERRNO_NOACCESS,
  627. "Permission denied: links to files in the forum are " .
  628. "only allowed from this web site."
  629. );
  630. }
  631. }
  632. return $file;
  633. }
  634. // }}}
  635. // {{{ Function: phorum_api_file_retrieve
  636. /**
  637. * Retrieve a Phorum file.
  638. *
  639. * This function can handle Phorum file retrieval in multiple ways:
  640. * either return the file to the caller or send it directly to the user's
  641. * browser (based on the $flags parameter). Sending it directly to the
  642. * browser allows for the implementation of modules that don't have to buffer
  643. * the full file data before sending it (a.k.a. streaming, which provides the
  644. * advantage of using less memory for sending files).
  645. *
  646. * @param mixed $file
  647. * This is either an array containing at least the fields "file_id"
  648. * and "filename" or a numerical file_id value. Note that you can
  649. * use the return value of the function
  650. * {@link phorum_api_file_check_read_access()} as input for this function.
  651. *
  652. * @param integer $flags
  653. * These are flags which influence aspects of the function call. It is
  654. * a bitflag value, so you can OR multiple flags together. Available
  655. * flags for this function are: {@link PHORUM_FLAG_IGNORE_PERMS},
  656. * {@link PHORUM_FLAG_GET}, {@link PHORUM_FLAG_SEND} and
  657. * {@link PHORUM_FLAG_FORCE_DOWNLOAD}. The SEND flag has precedence
  658. * over the GET flag.
  659. *
  660. * @return mixed
  661. * On error, this function will return FALSE.
  662. * The functions {@link phorum_api_strerror()} and
  663. * {@link phorum_api_errno()} can be used to retrieve information about
  664. * the error which occurred.
  665. *
  666. * If the {@link PHORUM_FLAG_SEND} flag is used, then the function will
  667. * return NULL.
  668. *
  669. * If the {@link PHORUM_FLAG_GET} flag is used, then the function
  670. * will return a file description array, containing the fields "file_id",
  671. * "username", "file_data", "mime_type".
  672. * If the {@link $file} parameter was an array, then all fields from that
  673. * array will be included as well.
  674. */
  675. function phorum_api_file_retrieve($file, $flags = PHORUM_FLAG_GET)
  676. {
  677. $PHORUM = $GLOBALS["PHORUM"];
  678. // Reset error storage.
  679. $GLOBALS["PHORUM"]["API"]["errno"] = NULL;
  680. $GLOBALS["PHORUM"]["API"]["error"] = NULL;
  681. // If $file is not an array, we are handling a numerical file_id.
  682. // In that case, first retrieve the file data through the access check
  683. // function. All the function flags are passed on to that function,
  684. // so the PHORUM_FLAG_IGNORE_PERMS flag can be set for ignoring access
  685. // permissions.
  686. if (!is_array($file))
  687. {
  688. $file_id = (int) $file;
  689. $file = phorum_api_file_check_read_access($file_id, $flags);
  690. // Return in case of errors.
  691. if ($file === FALSE) return FALSE;
  692. }
  693. // A small basic check to see if we have a proper $file array.
  694. if (!isset($file["file_id"])) trigger_error(
  695. "phorum_api_file_get(): \$file parameter needs a \"file_id\" field.",
  696. E_USER_ERROR
  697. );
  698. if (!isset($file["filename"])) trigger_error(
  699. "phorum_api_file_get(): \$file parameter needs a \"filename\" field.",
  700. E_USER_ERROR
  701. );
  702. settype($file["file_id"], "int");
  703. /*
  704. * [hook]
  705. * file_retrieve
  706. *
  707. * [description]
  708. * This hook allows modules to handle the file data retrieval.
  709. * The hook can use <literal>phorum_api_error_set()</literal>
  710. * to return an error. Hooks should be aware that their input might
  711. * not be <literal>$file</literal>, but <literal>FALSE</literal>
  712. * instead, in which case they should immediately return
  713. * <literal>FALSE</literal> themselves.
  714. *
  715. * [category]
  716. * File storage
  717. *
  718. * [when]
  719. * In <filename>include/api/file_storage.php</filename>,
  720. * right before a file attachment is retrieved from the database.
  721. *
  722. * [input]
  723. * Two part array where the first element is an empty file array
  724. * and the second element is the flags variable.
  725. *
  726. * [output]
  727. * Same as input with file_data filled in.
  728. */
  729. $file["result"] = 0;
  730. $file["mime_type"] = NULL;
  731. $file["file_data"] = NULL;
  732. if (isset($PHORUM["hooks"]["file_retrieve"])) {
  733. list($file,$flags) = phorum_hook("file_retrieve", array($file,$flags));
  734. if ($file === FALSE) return FALSE;
  735. // If a module sent the file data to the browser, then we are done.
  736. if ($file["result"] == PHORUM_FLAG_SEND) return NULL;
  737. }
  738. // If no module handled file retrieval, we will retrieve the
  739. // file from the Phorum database.
  740. if ($file["file_data"] === NULL)
  741. {
  742. $dbfile = phorum_db_file_get($file["file_id"], TRUE);
  743. if (empty($dbfile)) return phorum_api_error_set(
  744. PHORUM_ERRNO_NOTFOUND,
  745. "Phorum file (id {$file["file_id"]}) could not be " .
  746. "retrieved from the database."
  747. );
  748. // Phorum stores the files in base64 format in the database, to
  749. // prevent problems with dumping and restoring databases.
  750. $file["file_data"] = base64_decode($dbfile["file_data"]);
  751. }
  752. $mime_type_verified = FALSE;
  753. // Set the MIME type information if it was not set by a module.
  754. if ($file["mime_type"] === NULL)
  755. {
  756. $extension_mime_type = phorum_api_file_get_mimetype($file["filename"]);
  757. // mime magic file in case its needed
  758. if(!empty($PHORUM['mime_magic_file'])) {
  759. $mime_magic_file = $PHORUM['mime_magic_file'];
  760. } else {
  761. $mime_magic_file = NULL;
  762. }
  763. // retrieve the mime-type using the fileinfo extension if its available and enabled
  764. if(function_exists("finfo_open") &&
  765. (!isset($PHORUM['file_fileinfo_ext']) || !empty($PHORUM['file_fileinfo_ext'])) &&
  766. $finfo = @finfo_open(FILEINFO_MIME,$mime_magic_file)) {
  767. $file["mime_type"] = finfo_buffer($finfo,$file['file_data']);
  768. finfo_close($finfo);
  769. if ($file["mime_type"] === FALSE) return phorum_api_error_set(
  770. PHORUM_ERRNO_ERROR,
  771. "The mime-type of file {$file["file_id"]} couldn't be determined through the" .
  772. "fileinfo-extension"
  773. );
  774. // extension mime-type doesn't fit the signature mime-type
  775. // make it a download then
  776. if($extension_mime_type != $file["mime_type"]) {
  777. $flags = $flags | PHORUM_FLAG_FORCE_DOWNLOAD;
  778. }
  779. $mime_type_verified = TRUE;
  780. } else {
  781. $file["mime_type"] = $extension_mime_type;
  782. }
  783. }
  784. // If the file is not requested for downloading, then check if it is
  785. // safe for the browser to view this file. If it is not, then
  786. // enable the force download flag to make sure that the browser will
  787. // download the file.
  788. $safe_to_cache = TRUE;
  789. $safe_to_view = TRUE;
  790. if (!($flags & PHORUM_FLAG_FORCE_DOWNLOAD) && !$mime_type_verified)
  791. {
  792. list ($safe_to_view, $safe_to_cache) =
  793. phorum_api_file_safe_to_view($file);
  794. if (!$safe_to_view) {
  795. $flags = $flags | PHORUM_FLAG_FORCE_DOWNLOAD;
  796. }
  797. }
  798. // Allow for post processing on the retrieved file.
  799. list($file,$flags) = phorum_hook("file_after_retrieve", array($file,$flags));
  800. // In "send" mode, we directly send the file contents to the browser.
  801. if ($flags & PHORUM_FLAG_SEND)
  802. {
  803. // Get rid of any buffered output so far.
  804. phorum_ob_clean();
  805. // Avoid using any output compression or handling on the sent data.
  806. ini_set("zlib.output_compression", "0");
  807. ini_set("output_handler", "");
  808. $time = (int)$file['add_datetime'];
  809. // Handle client side caching.
  810. if ($safe_to_cache)
  811. {
  812. if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']))
  813. {
  814. $header = preg_replace('/;.*$/','',$_SERVER['HTTP_IF_MODIFIED_SINCE']);
  815. $modified_since = strtotime($header);
  816. if ($modified_since >= $time)
  817. {
  818. $proto = empty($_SERVER['SERVER_PROTOCOL'])
  819. ? 'HTTP/1.0' : $_SERVER['SERVER_PROTOCOL'];
  820. header("$proto 304 Not Modified");
  821. header('Status: 304');
  822. exit(0);
  823. }
  824. }
  825. header("Last-Modified: " . gmdate('D, d M Y H:i:s \G\M\T', $time));
  826. header('Cache-Control: max-age=5184000'); // 60 days
  827. header('Expires: '.gmdate('D, d M Y H:i:s \G\M\T', time()+5184000));
  828. }
  829. else
  830. {
  831. // Expire in the past.
  832. header('Expires: '.gmdate('D, d M Y H:i:s \G\M\T', time() - 99999));
  833. // Always modified.
  834. header('Last-Modified: '.gmdate('D, d M Y H:i:s \G\M\T', time()));
  835. // HTTP/1.1
  836. header('cache-Control: no-store, no-cache, must-revalidate');
  837. header('cache-Control: post-check=0, pre-check=0', FALSE);
  838. // HTTP/1.0
  839. header('Pragma: no-cache');
  840. }
  841. if ($flags & PHORUM_FLAG_FORCE_DOWNLOAD) {
  842. header("Content-Type: application/octet-stream");
  843. header("Content-Disposition: attachment; filename=\"{$file["filename"]}\"");
  844. } else {
  845. header("Content-Type: " . $file["mime_type"]);
  846. header("Content-Disposition: filename=\"{$file["filename"]}\"");
  847. }
  848. header('Content-Length: ' . strlen($file['file_data']));
  849. print $file["file_data"];
  850. return NULL;
  851. }
  852. // In "get" mode, we return the full file data array to the caller.
  853. elseif ($flags & PHORUM_FLAG_GET) {
  854. return $file;
  855. }
  856. // Safety net.
  857. else trigger_error(
  858. "phorum_api_file_retrieve(): no retrieve mode specified in the " .
  859. "flags (either use PHORUM_FLAG_GET or PHORUM_FLAG_SEND).",
  860. E_USER_ERROR
  861. );
  862. }
  863. // }}}
  864. // {{{ Function: phorum_api_file_check_delete_access
  865. /**
  866. * Check if the active user has permission to delete a file.
  867. *
  868. * @example file_delete.php Delete a file.
  869. *
  870. * @param integer $file_id
  871. * The file_id of the file for which to check the delete access.
  872. *
  873. * @return boolean
  874. * TRUE if the user has rights to delete the file, FALSE otherwise.
  875. */
  876. function phorum_api_file_check_delete_access($file_id)
  877. {
  878. global $PHORUM;
  879. settype($file_id, "int");
  880. // Administrator users always have rights to delete files.
  881. if ($PHORUM["user"]["admin"]) {
  882. return TRUE;
  883. }
  884. // Anonymous users never have rights to delete files.
  885. if (empty($PHORUM["user"]["user_id"])) {
  886. return FALSE;
  887. }
  888. // For other users, the file information has to be retrieved
  889. // to be able to check the delete access.
  890. $file = phorum_api_file_check_read_access(
  891. $file_id,
  892. PHORUM_FLAG_IGNORE_PERMS
  893. );
  894. // To prevent permission errors after deleting the same file twice,
  895. // we'll return TRUE if we did not find a file (if the file is not found,
  896. // then there's no harm in deleting it; the file storage API will
  897. // silently ignore deleting non-existent files). If some other error
  898. // occurred, then we return FALSE (most likely, the user does not
  899. // even have read permission for the file, so delete access would
  900. // be out of the question too).
  901. if ($file === FALSE) {
  902. if (phorum_api_errno() == PHORUM_ERRNO_NOTFOUND) {
  903. return TRUE;
  904. } else {
  905. return FALSE;
  906. }
  907. }
  908. // We don't care about deleting temporary files and files that
  909. // are linked to the posting editor (during writing a post).
  910. // Those are both intermediate states for files, without them
  911. // being available on the forum. So for those, we always grant
  912. // delete access.
  913. if ($file["link"] == PHORUM_LINK_TEMPFILE ||
  914. $file["link"] == PHORUM_LINK_EDITOR) {
  915. return TRUE;
  916. }
  917. // If the file is owned by the user, then the user has rights
  918. // to delete the file (this would be a personal user file).
  919. if (!empty($file["user_id"]) &&
  920. $file["user_id"] == $PHORUM["user"]["user_id"]) {
  921. return TRUE;
  922. }
  923. // The file is not owned by the user. In that case, the user only has
  924. // rights to delete it if it is a file that is linked to a message which
  925. // the user posted himself of which was posted in a forum for which
  926. // the user is a moderator.
  927. if ($file["link"] == PHORUM_LINK_MESSAGE)
  928. {
  929. // Retrieve the message to which the file is linked.
  930. $message = phorum_db_get_message($file["message_id"]);
  931. // If the message cannot be found, we do not care if the linked
  932. // file is deleted. It's clearly an orphin file.
  933. if (! $message) {
  934. return TRUE;
  935. }
  936. // Check if the user posted the message himself.
  937. if (!empty($message["user_id"]) &&
  938. $message["user_id"] == $PHORUM["user"]["user_id"]) {
  939. return TRUE;
  940. }
  941. // Check if the user is moderator for the forum_id of the message.
  942. if (phorum_api_user_check_access(PHORUM_USER_ALLOW_MODERATE_MESSAGES, $message["forum_id"])) {
  943. return TRUE;
  944. }
  945. }
  946. // The default policy for any unhandled case is to deny access.
  947. return FALSE;
  948. }
  949. // }}}
  950. // {{{ Function: phorum_api_file_delete
  951. /**
  952. * Delete a Phorum file.
  953. *
  954. * @example file_delete.php Delete a file.
  955. *
  956. * @param mixed $file
  957. * This is either an array containing at least the field "file_id"
  958. * or a numerical file_id value.
  959. */
  960. function phorum_api_file_delete($file)
  961. {
  962. $PHORUM = $GLOBALS["PHORUM"];
  963. // Find the file_id parameter to use.
  964. if (is_array($file)) {
  965. if (!isset($file["file_id"])) trigger_error(
  966. "phorum_api_file_delete(): \$file parameter needs a " .
  967. "\"file_id\" field.",
  968. E_USER_ERROR
  969. );
  970. $file_id = (int) $file["file_id"];
  971. } else {
  972. $file_id = (int) $file;
  973. }
  974. // Allow storage modules to handle the file data removal.
  975. // Modules should be aware of the fact that files don't have to
  976. // exist. The Phorum core does not throw errors when deleting a
  977. // non existent file. Therefore modules should accept that case
  978. // as well, without throwing errors.
  979. if (isset($PHORUM["hooks"]["file_delete"]))
  980. phorum_hook("file_delete", $file_id);
  981. // Delete the file from the Phorum database.
  982. phorum_db_file_delete($file_id);
  983. }
  984. // }}}
  985. // {{{ Function: phorum_api_file_list()
  986. /**
  987. * Retrieve a list of files.
  988. *
  989. * @param string $link_type
  990. * The type of link to retrieve from the database. Normally this is one
  991. * of the Phorum built-in link types, but it can also be a custom
  992. * link type (e.g. if a module uses the file storage on its own).
  993. * This parameter can be NULL to retrieve any link type.
  994. *
  995. * @param integer $user_id
  996. * The user_id to retrieve files for or NULL to retrieve files for
  997. * any user_id.
  998. *
  999. * @param integer $message_id
  1000. * The message_id to retrieve files for or NULL to retrieve files for
  1001. * any message_id.
  1002. *
  1003. * @return array
  1004. * An array of files, indexed by file_id.
  1005. * The array elements are arrays containing the fields:
  1006. * file_id, filename, filesize and add_datetime.
  1007. */
  1008. function phorum_api_file_list($link_type = NULL, $user_id = NULL, $message_id = NULL)
  1009. {
  1010. return phorum_db_get_file_list($link_type, $user_id, $message_id);
  1011. }
  1012. // }}}
  1013. // {{{ Function: phorum_api_file_purge_stale()
  1014. /**
  1015. * This function is used for purging stale files from the Phorum system.
  1016. *
  1017. * @param boolean $do_purge
  1018. * If this parameter is set to a false value (the default), then no
  1019. * actual purging will take place. The function will only return an
  1020. * array of stale files. If the parameter is set to a true value,
  1021. * then the stale files will be purged for real.
  1022. *
  1023. * @return array
  1024. * An array of stale Phorum files, indexed by file_id. Every item in
  1025. * this array is an array on its own, containing the fields:
  1026. * - file_id: the file id of the stale file
  1027. * - filename: the name of the stale file
  1028. * - filesize: the size of the file in bytes
  1029. * - add_datetime: the time (epoch) at which the file was added
  1030. * - reason: the reason why it's a stale file
  1031. * This array will be returned, regardless of the $do_purge parameter.
  1032. */
  1033. function phorum_api_file_purge_stale($do_purge)
  1034. {
  1035. $stale_files = phorum_db_list_stale_files();
  1036. /**
  1037. * [hook]
  1038. * file_purge_stale
  1039. *
  1040. * [description]
  1041. * This hook can be used to feed the file storage API function
  1042. * phorum_api_file_purge_stale() extra stale files. This can be
  1043. * useful for modules that handle their own files, using a
  1044. * custom link type.
  1045. *
  1046. * [category]
  1047. * File storage
  1048. *
  1049. * [when]
  1050. * Right after Phorum created its own list of stale files.
  1051. *
  1052. * [input]
  1053. * An array containing stale files, indexed by file_id. Each item
  1054. * in this array is an array on its own, containing the following
  1055. * fields:
  1056. * <ul>
  1057. * <li>file_id:
  1058. * the file id of the stale file</li>
  1059. * <li>filename:
  1060. * the name of the stale file</li>
  1061. * <li>filesize:
  1062. * the size of the file in bytes</li>
  1063. * <li>add_datetime:
  1064. * the time (epoch) at which the file was added</li>
  1065. * <li>reason:
  1066. * the reason why it's a stale file</li>
  1067. * </ul>
  1068. *
  1069. * [output]
  1070. * The same array as the one that was used for the hook call
  1071. * argument, possibly extended with extra files that are
  1072. * considered to be stale.
  1073. */
  1074. if (isset($GLOBALS['PHORUM']['hooks']['file_purge_stale']))
  1075. $stale_files = phorum_hook('file_purge_stale', $stale_files);
  1076. // Delete the files if requested.
  1077. if ($do_purge) {
  1078. foreach ($stale_files as $file) {
  1079. phorum_api_file_delete($file);
  1080. }
  1081. }
  1082. return $stale_files;
  1083. }
  1084. // }}}
  1085. // ------------------------------------------------------------------------
  1086. // File security checking
  1087. // ------------------------------------------------------------------------
  1088. // {{{ Function: phorum_api_file_safe_to_view()
  1089. /**
  1090. * Check if the file is safe to view in the browser.
  1091. *
  1092. * This will emulate MIME-sniffing as done by browsers to see if
  1093. * the file could be interpreted as an HTML file by the browser.
  1094. *
  1095. * @param array $file
  1096. * An array, containing information for the file.
  1097. * This array has to contain at least the file_data field.
  1098. *
  1099. * @return boolean
  1100. * TRUE if the browser might qualify the file as HTML code,
  1101. * FALSE otherwise.
  1102. *
  1103. * @return boolean
  1104. * TRUE if it is safe to cache the file in the browser, FALSE otherwise.
  1105. */
  1106. function phorum_api_file_safe_to_view($file)
  1107. {
  1108. if (!isset($file['file_data'])) trigger_error(
  1109. "phorum_api_file_safe_to_view(): \$file parameter needs a " .
  1110. "\"file_data\" field.",
  1111. E_USER_ERROR
  1112. );
  1113. $safe_to_cache = TRUE;
  1114. $safe_to_view = TRUE;
  1115. // Based on info from:
  1116. // http://webblaze.cs.berkeley.edu/2009/content-sniffing/
  1117. //
  1118. // Sniffing buffer in various browsers:
  1119. // - MSIE7 = 256 Bytes
  1120. // - FF3 & Safari 3.1 = 1024 Bytes
  1121. // - Google Chrome & HTML5 spec = 512 Bytes
  1122. // A conservative approach requires checking of 1024 Bytes.
  1123. //
  1124. // The trim() call is used, because some browser checks
  1125. // look at the first non-whitespace byte of the file data.
  1126. //
  1127. $chunk = trim(substr($file['file_data'], 0, 1024));
  1128. if (preg_match('/
  1129. ^<!| # FF3 CHROME HTML5
  1130. ^<\?| # FF3
  1131. <html| # FF3 IE7 SAF3.1 CHROME HTML5
  1132. <script| # FF3 IE7 SAF3.1 CHROME HTML5
  1133. <title| # FF3 IE7 SAF3.1 CHROME
  1134. <body| # FF3 IE7 CHROME
  1135. <head| # FF3 IE7 CHROME HTML5
  1136. <plaintext| # IE7
  1137. <table[ >]| # FF3 IE7 CHROME
  1138. <img[ >]| # FF3 IE7
  1139. <pre[ >]| # FF3 IE7
  1140. text\/html| # SAF3.1
  1141. <a[ >]| # FF3 IE7 SAF3.1 CHROME
  1142. ^<frameset[ >]| # FF3
  1143. ^<iframe[ >]| # FF3 CHROME
  1144. ^<link[ >]| # FF3
  1145. ^<base[ >]| # FF3
  1146. ^<style[ >]| # FF3 CHROME
  1147. ^<div[ >]| # FF3 CHROME
  1148. ^<p[ >]| # FF3 CHROME
  1149. ^<font[ >]| # FF3 CHROME
  1150. ^<applet[ >]| # FF3
  1151. ^<meta[ >]| # FF3
  1152. ^<center[ >]| # FF3
  1153. ^<form[ >]| # FF3
  1154. ^<isindex[ >]| # FF3
  1155. ^<h1[ >]| # FF3 CHROME
  1156. ^<h2[ >]| # FF3
  1157. ^<h3[ >]| # FF3
  1158. ^<h4[ >]| # FF3
  1159. ^<h5[ >]| # FF3
  1160. ^<h6[ >]| # FF3
  1161. ^<b[ >]| # FF3 CHROME
  1162. ^<br[ >] # CHROME
  1163. /xi', $chunk, $m)) {
  1164. $safe_to_view = FALSE;
  1165. // The file could be interpreted as HTML by the browser.
  1166. // As an additional check, we check if MSIE 6 or lower is in use.
  1167. // For those, it is not safe to cache the file. In some cases,
  1168. // they could interpret the file from cache, even when we tell
  1169. // the browser that the file should be downloaded.
  1170. if (!empty($_SERVER['HTTP_USER_AGENT']) &&
  1171. preg_match('/MSIE [654]\D/', $_SERVER['HTTP_USER_AGENT'])) {
  1172. $safe_to_cache = FALSE;
  1173. }
  1174. }
  1175. return array($safe_to_view, $safe_to_cache);
  1176. }
  1177. // }}}
  1178. // ------------------------------------------------------------------------
  1179. // Alias functions (useful shortcut calls to the main file api functions).
  1180. // ------------------------------------------------------------------------
  1181. // {{{ Function: phorum_api_file_exists
  1182. /**
  1183. * Check if a Phorum file exists.
  1184. *
  1185. * (this is a simple wrapper function around the
  1186. * {@link phorum_api_file_check_read_access()} function)
  1187. *
  1188. * @param integer $file_id
  1189. * The file_id of the Phorum file to check.
  1190. *
  1191. * @return bool
  1192. * TRUE in case the file exists or FALSE if it doesn't.
  1193. */
  1194. function phorum_api_file_exists($file_id) {
  1195. $file = phorum_api_file_check_read_access($file_id, PHORUM_FLAG_IGNORE_PERMS);
  1196. $exists = empty($file) ? FALSE : TRUE;
  1197. return $exists;
  1198. }
  1199. // }}}
  1200. // {{{ Function: phorum_api_file_send
  1201. /**
  1202. * Send a file to the browser.
  1203. *
  1204. * (this is a simple wr…

Large files files are truncated, but you can click here to view the full file