Http.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_File_Transfer
  17. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. * @version $Id$
  20. */
  21. /**
  22. * @see Zend_File_Transfer_Adapter_Abstract
  23. */
  24. require_once 'Zend/File/Transfer/Adapter/Abstract.php';
  25. /**
  26. * File transfer adapter class for the HTTP protocol
  27. *
  28. * @category Zend
  29. * @package Zend_File_Transfer
  30. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  31. * @license http://framework.zend.com/license/new-bsd New BSD License
  32. */
  33. class Zend_File_Transfer_Adapter_Http extends Zend_File_Transfer_Adapter_Abstract
  34. {
  35. protected static $_callbackApc = 'apc_fetch';
  36. protected static $_callbackUploadProgress = 'uploadprogress_get_info';
  37. /**
  38. * Constructor for Http File Transfers
  39. *
  40. * @param array $options OPTIONAL Options to set
  41. */
  42. public function __construct($options = array())
  43. {
  44. if (ini_get('file_uploads') == false) {
  45. require_once 'Zend/File/Transfer/Exception.php';
  46. throw new Zend_File_Transfer_Exception('File uploads are not allowed in your php config!');
  47. }
  48. $this->_prepareFiles();
  49. $this->addValidator('Upload', false, $this->_files);
  50. if (is_array($options)) {
  51. $this->setOptions($options);
  52. }
  53. }
  54. /**
  55. * Sets a validator for the class, erasing all previous set
  56. *
  57. * @param string|array $validator Validator to set
  58. * @param string|array $files Files to limit this validator to
  59. * @return Zend_File_Transfer_Adapter
  60. */
  61. public function setValidators(array $validators, $files = null)
  62. {
  63. $this->clearValidators();
  64. return $this->addValidators($validators, $files);
  65. }
  66. /**
  67. * Remove an individual validator
  68. *
  69. * @param string $name
  70. * @return Zend_File_Transfer_Adapter_Abstract
  71. */
  72. public function removeValidator($name)
  73. {
  74. if ($name == 'Upload') {
  75. return $this;
  76. }
  77. return parent::removeValidator($name);
  78. }
  79. /**
  80. * Remove an individual validator
  81. *
  82. * @param string $name
  83. * @return Zend_File_Transfer_Adapter_Abstract
  84. */
  85. public function clearValidators()
  86. {
  87. parent::clearValidators();
  88. $this->addValidator('Upload', false, $this->_files);
  89. return $this;
  90. }
  91. /**
  92. * Send the file to the client (Download)
  93. *
  94. * @param string|array $options Options for the file(s) to send
  95. * @return void
  96. * @throws Zend_File_Transfer_Exception Not implemented
  97. */
  98. public function send($options = null)
  99. {
  100. require_once 'Zend/File/Transfer/Exception.php';
  101. throw new Zend_File_Transfer_Exception('Method not implemented');
  102. }
  103. /**
  104. * Checks if the files are valid
  105. *
  106. * @param string|array $files (Optional) Files to check
  107. * @return boolean True if all checks are valid
  108. */
  109. public function isValid($files = null)
  110. {
  111. // Workaround for WebServer not conforming HTTP and omitting CONTENT_LENGTH
  112. $content = 0;
  113. if (isset($_SERVER['CONTENT_LENGTH'])) {
  114. $content = $_SERVER['CONTENT_LENGTH'];
  115. } else if (!empty($_POST)) {
  116. $content = serialize($_POST);
  117. }
  118. // Workaround for a PHP error returning empty $_FILES when form data exceeds php settings
  119. if (empty($this->_files) && ($content > 0)) {
  120. if (is_array($files)) {
  121. $files = current($files);
  122. }
  123. $temp = array($files => array(
  124. 'name' => $files,
  125. 'error' => 1));
  126. $validator = $this->_validators['Zend_Validate_File_Upload'];
  127. $validator->setFiles($temp)
  128. ->isValid($files, null);
  129. $this->_messages += $validator->getMessages();
  130. return false;
  131. }
  132. return parent::isValid($files);
  133. }
  134. /**
  135. * Receive the file from the client (Upload)
  136. *
  137. * @param string|array $files (Optional) Files to receive
  138. * @return bool
  139. */
  140. public function receive($files = null)
  141. {
  142. if (!$this->isValid($files)) {
  143. return false;
  144. }
  145. $check = $this->_getFiles($files);
  146. foreach ($check as $file => $content) {
  147. if (!$content['received']) {
  148. $directory = '';
  149. $destination = $this->getDestination($file);
  150. if ($destination !== null) {
  151. $directory = $destination . DIRECTORY_SEPARATOR;
  152. }
  153. $filename = $directory . $content['name'];
  154. $rename = $this->getFilter('Rename');
  155. if ($rename !== null) {
  156. $tmp = $rename->getNewName($content['tmp_name']);
  157. if ($tmp != $content['tmp_name']) {
  158. $filename = $tmp;
  159. }
  160. if (dirname($filename) == '.') {
  161. $filename = $directory . $filename;
  162. }
  163. $key = array_search(get_class($rename), $this->_files[$file]['filters']);
  164. unset($this->_files[$file]['filters'][$key]);
  165. }
  166. // Should never return false when it's tested by the upload validator
  167. if (!move_uploaded_file($content['tmp_name'], $filename)) {
  168. if ($content['options']['ignoreNoFile']) {
  169. $this->_files[$file]['received'] = true;
  170. $this->_files[$file]['filtered'] = true;
  171. continue;
  172. }
  173. $this->_files[$file]['received'] = false;
  174. return false;
  175. }
  176. if ($rename !== null) {
  177. $this->_files[$file]['destination'] = dirname($filename);
  178. $this->_files[$file]['name'] = basename($filename);
  179. }
  180. $this->_files[$file]['tmp_name'] = $filename;
  181. $this->_files[$file]['received'] = true;
  182. }
  183. if (!$content['filtered']) {
  184. if (!$this->_filter($file)) {
  185. $this->_files[$file]['filtered'] = false;
  186. return false;
  187. }
  188. $this->_files[$file]['filtered'] = true;
  189. }
  190. }
  191. return true;
  192. }
  193. /**
  194. * Checks if the file was already sent
  195. *
  196. * @param string|array $file Files to check
  197. * @return bool
  198. * @throws Zend_File_Transfer_Exception Not implemented
  199. */
  200. public function isSent($files = null)
  201. {
  202. require_once 'Zend/File/Transfer/Exception.php';
  203. throw new Zend_File_Transfer_Exception('Method not implemented');
  204. }
  205. /**
  206. * Checks if the file was already received
  207. *
  208. * @param string|array $files (Optional) Files to check
  209. * @return bool
  210. */
  211. public function isReceived($files = null)
  212. {
  213. $files = $this->_getFiles($files, false, true);
  214. if (empty($files)) {
  215. return false;
  216. }
  217. foreach ($files as $content) {
  218. if ($content['received'] !== true) {
  219. return false;
  220. }
  221. }
  222. return true;
  223. }
  224. /**
  225. * Checks if the file was already filtered
  226. *
  227. * @param string|array $files (Optional) Files to check
  228. * @return bool
  229. */
  230. public function isFiltered($files = null)
  231. {
  232. $files = $this->_getFiles($files, false, true);
  233. if (empty($files)) {
  234. return false;
  235. }
  236. foreach ($files as $content) {
  237. if ($content['filtered'] !== true) {
  238. return false;
  239. }
  240. }
  241. return true;
  242. }
  243. /**
  244. * Has a file been uploaded ?
  245. *
  246. * @param array|string|null $file
  247. * @return bool
  248. */
  249. public function isUploaded($files = null)
  250. {
  251. $files = $this->_getFiles($files, false, true);
  252. if (empty($files)) {
  253. return false;
  254. }
  255. foreach ($files as $file) {
  256. if (empty($file['name'])) {
  257. return false;
  258. }
  259. }
  260. return true;
  261. }
  262. /**
  263. * Returns the actual progress of file up-/downloads
  264. *
  265. * @param string $id The upload to get the progress for
  266. * @return array|null
  267. */
  268. public static function getProgress($id = null)
  269. {
  270. if (!function_exists('apc_fetch') and !function_exists('uploadprogress_get_info')) {
  271. require_once 'Zend/File/Transfer/Exception.php';
  272. throw new Zend_File_Transfer_Exception('Wether APC nor uploadprogress extension installed');
  273. }
  274. $session = 'Zend_File_Transfer_Adapter_Http_ProgressBar';
  275. $status = array(
  276. 'total' => 0,
  277. 'current' => 0,
  278. 'rate' => 0,
  279. 'message' => '',
  280. 'done' => false
  281. );
  282. if (is_array($id)) {
  283. if (isset($id['progress'])) {
  284. $adapter = $id['progress'];
  285. }
  286. if (isset($id['session'])) {
  287. $session = $id['session'];
  288. }
  289. if (isset($id['id'])) {
  290. $id = $id['id'];
  291. } else {
  292. unset($id);
  293. }
  294. }
  295. if (!empty($id) && (($id instanceof Zend_ProgressBar_Adapter) || ($id instanceof Zend_ProgressBar))) {
  296. $adapter = $id;
  297. unset($id);
  298. }
  299. if (empty($id)) {
  300. if (!isset($_GET['progress_key'])) {
  301. $status['message'] = 'No upload in progress';
  302. $status['done'] = true;
  303. } else {
  304. $id = $_GET['progress_key'];
  305. }
  306. }
  307. if (!empty($id)) {
  308. if (self::isApcAvailable()) {
  309. $call = call_user_func(self::$_callbackApc, ini_get('apc.rfc1867_prefix') . $id);
  310. if (is_array($call)) {
  311. $status = $call + $status;
  312. }
  313. } else if (self::isUploadProgressAvailable()) {
  314. $call = call_user_func(self::$_callbackUploadProgress, $id);
  315. if (is_array($call)) {
  316. $status = $call + $status;
  317. $status['total'] = $status['bytes_total'];
  318. $status['current'] = $status['bytes_uploaded'];
  319. $status['rate'] = $status['speed_average'];
  320. if ($status['total'] == $status['current']) {
  321. $status['done'] = true;
  322. }
  323. }
  324. }
  325. if (!is_array($call)) {
  326. $status['done'] = true;
  327. $status['message'] = 'Failure while retrieving the upload progress';
  328. } else if (!empty($status['cancel_upload'])) {
  329. $status['done'] = true;
  330. $status['message'] = 'The upload has been canceled';
  331. } else {
  332. $status['message'] = self::_toByteString($status['current']) . " - " . self::_toByteString($status['total']);
  333. }
  334. $status['id'] = $id;
  335. }
  336. if (isset($adapter) && isset($status['id'])) {
  337. if ($adapter instanceof Zend_ProgressBar_Adapter) {
  338. require_once 'Zend/ProgressBar.php';
  339. $adapter = new Zend_ProgressBar($adapter, 0, $status['total'], $session);
  340. }
  341. if (!($adapter instanceof Zend_ProgressBar)) {
  342. require_once 'Zend/File/Transfer/Exception.php';
  343. throw new Zend_File_Transfer_Exception('Unknown Adapter given');
  344. }
  345. if ($status['done']) {
  346. $adapter->finish();
  347. } else {
  348. $adapter->update($status['current'], $status['message']);
  349. }
  350. $status['progress'] = $adapter;
  351. }
  352. return $status;
  353. }
  354. /**
  355. * Checks the APC extension for progress information
  356. *
  357. * @return boolean
  358. */
  359. public static function isApcAvailable()
  360. {
  361. return (bool) ini_get('apc.enabled') && (bool) ini_get('apc.rfc1867') && is_callable(self::$_callbackApc);
  362. }
  363. /**
  364. * Checks the UploadProgress extension for progress information
  365. *
  366. * @return boolean
  367. */
  368. public static function isUploadProgressAvailable()
  369. {
  370. return is_callable(self::$_callbackUploadProgress);
  371. }
  372. /**
  373. * Prepare the $_FILES array to match the internal syntax of one file per entry
  374. *
  375. * @param array $files
  376. * @return array
  377. */
  378. protected function _prepareFiles()
  379. {
  380. $this->_files = array();
  381. foreach ($_FILES as $form => $content) {
  382. if (is_array($content['name'])) {
  383. foreach ($content as $param => $file) {
  384. foreach ($file as $number => $target) {
  385. $this->_files[$form . '_' . $number . '_'][$param] = $target;
  386. $this->_files[$form . '_' . $number . '_']['options'] = $this->_options;
  387. $this->_files[$form . '_' . $number . '_']['validated'] = false;
  388. $this->_files[$form . '_' . $number . '_']['received'] = false;
  389. $this->_files[$form . '_' . $number . '_']['filtered'] = false;
  390. $this->_files[$form]['multifiles'][$number] = $form . '_' . $number . '_';
  391. $this->_files[$form]['name'] = $form;
  392. $mimetype = $this->_detectMimeType($this->_files[$form . '_' . $number . '_']);
  393. $this->_files[$form . '_' . $number . '_']['type'] = $mimetype;
  394. $filesize = $this->_detectFileSize($this->_files[$form . '_' . $number . '_']);
  395. $this->_files[$form . '_' . $number . '_']['size'] = $filesize;
  396. if ($this->_options['detectInfos']) {
  397. $_FILES[$form]['type'][$number] = $mimetype;
  398. $_FILES[$form]['size'][$number] = $filesize;
  399. }
  400. }
  401. }
  402. } else {
  403. $this->_files[$form] = $content;
  404. $this->_files[$form]['options'] = $this->_options;
  405. $this->_files[$form]['validated'] = false;
  406. $this->_files[$form]['received'] = false;
  407. $this->_files[$form]['filtered'] = false;
  408. $mimetype = $this->_detectMimeType($this->_files[$form]);
  409. $this->_files[$form]['type'] = $mimetype;
  410. $filesize = $this->_detectFileSize($this->_files[$form]);
  411. $this->_files[$form]['size'] = $filesize;
  412. if ($this->_options['detectInfos']) {
  413. $_FILES[$form]['type'] = $mimetype;
  414. $_FILES[$form]['size'] = $filesize;
  415. }
  416. }
  417. }
  418. return $this;
  419. }
  420. }