PageRenderTime 26ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/includes/class/download.class.php

http://viet-group.googlecode.com/
PHP | 546 lines | 497 code | 10 blank | 39 comment | 5 complexity | f396c14038294328e3c5180267d99516 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * @Project NUKEVIET 3.0
  4. * @Author VINADES.,JSC (contact@vinades.vn)
  5. * @Copyright (C) 2010 VINADES.,JSC. All rights reserved
  6. * @Createdate 17/8/2010, 0:16
  7. */
  8. /**********************************************************************
  9. **
  10. ** A class to download files
  11. ** Version 1.0
  12. ** Features :
  13. ** - hide the real path to the file
  14. ** - allow / disallow download resuming
  15. ** - partial download (useful for download managers)
  16. ** - rename the file on the fly
  17. ** - limit download speed
  18. **
  19. ** Author: Mourad Boufarguine / EPT <mourad.boufarguine@gmail.com>
  20. **
  21. ** License: Public Domain
  22. ** Warranty: None
  23. **
  24. ***********************************************************************/
  25. /**
  26. * include("download.class.php"); // load the class file
  27. * $fichier = new download("example.zip"); // use the original file name, disallow resuming, no speed limit
  28. * $fichier = new download("example.zip","My Example.zip") ; // rename the file, disallow resuming, no speed limit
  29. * $fichier = new download("example.zip","My Example.zip",true) ; // rename the file, allow resuming, no speed limit
  30. * $fichier = new download("example.zip","My Example.zip",true,80) ; // rename the file, allow resuming, speed limit 80ko/s
  31. * $fichier->download_file();
  32. */
  33. if ( ! defined( 'NV_MAINFILE' ) ) die( 'Stop!!!' );
  34. if ( ! defined( 'NV_ROOTDIR' ) )
  35. {
  36. define( 'NV_ROOTDIR', preg_replace( "/[\/]+$/", '', str_replace( '\\', '/', realpath( dirname( __file__ ) . '/../../' ) ) ) );
  37. }
  38. if ( ! defined( 'NV_UPLOADS_REAL_DIR' ) )
  39. {
  40. define( 'NV_UPLOADS_REAL_DIR', NV_ROOTDIR . '/uploads/' );
  41. }
  42. if ( ! defined( 'NV_MIME_INI_FILE' ) )
  43. {
  44. define( "NV_MIME_INI_FILE", str_replace( "\\", "/", realpath( dirname( __file__ ) . "/.." ) . '/ini/mime.ini' ) );
  45. }
  46. if ( ! defined( 'ALLOWED_SET_TIME_LIMIT' ) )
  47. {
  48. if ( $sys_info['allowed_set_time_limit'] )
  49. {
  50. define( 'ALLOWED_SET_TIME_LIMIT', true );
  51. }
  52. }
  53. /**
  54. * download
  55. *
  56. * @package
  57. * @author NUKEVIET 3.0
  58. * @copyright VINADES.,JSC
  59. * @version 2010
  60. * @access public
  61. */
  62. class download
  63. {
  64. private $properties = array( //
  65. "path" => "", //
  66. "name" => "", //
  67. "extension" => "", //
  68. "type" => "", //
  69. "size" => "", //
  70. "mtime" => 0, //
  71. "resume" => "", //
  72. "max_speed" => "", //
  73. "directory" => "" //
  74. );
  75. private $disable_functions = array();
  76. private $magic_path;
  77. /**
  78. * download::__construct()
  79. *
  80. * @param mixed $path
  81. * @param string $name
  82. * @param bool $resume
  83. * @param integer $max_speed
  84. * @return
  85. */
  86. public function __construct( $path, $directory, $name = '', $resume = false, $max_speed = 0, $magic_path = '' )
  87. {
  88. $directory = $this->real_dir( $directory );
  89. if ( empty( $directory ) or ! is_dir( $directory ) )
  90. {
  91. $directory = NV_UPLOADS_REAL_DIR;
  92. }
  93. $path = $this->real_path( $path, $directory );
  94. $extension = $this->getextension( $path );
  95. $this->properties = array( //
  96. "path" => $path, //
  97. "name" => ( $name == "" ) ? substr( strrchr( "/" . $path, "/" ), 1 ) : $name, //
  98. "extension" => $extension, //
  99. "type" => $this->my_mime_content_type( $path ), //
  100. "size" => intval( sprintf( "%u", filesize( $path ) ) ), //
  101. "mtime" => ( $mtime = filemtime( $path ) ) > 0 ? $mtime : time(), //
  102. "resume" => $resume, //
  103. "max_speed" => $max_speed, //
  104. "directory" => $directory //
  105. );
  106. $this->disable_functions = ( ini_get( "disable_functions" ) != "" and ini_get( "disable_functions" ) != false ) ? array_map( 'trim', preg_split( "/[\s,]+/", ini_get( "disable_functions" ) ) ) : array();
  107. $this->magic_path = $magic_path;
  108. }
  109. /**
  110. * download::real_dir()
  111. *
  112. * @param mixed $dir
  113. * @return
  114. */
  115. function real_dir( $dir )
  116. {
  117. if ( empty( $dir ) ) return false;
  118. $dir = realpath( $dir );
  119. if ( empty( $dir ) ) return false;
  120. $dir = str_replace( '\\', '/', $dir );
  121. $dir = rtrim( $dir, "\\/" );
  122. if ( ! preg_match( "/^(" . nv_preg_quote( NV_ROOTDIR ) . ")(\/[\S]+)/", $dir ) ) return false;
  123. return $dir;
  124. }
  125. /**
  126. * download::real_path()
  127. *
  128. * @param mixed $path
  129. * @return
  130. */
  131. function real_path( $path, $dir )
  132. {
  133. if ( empty( $path ) or ! is_readable( $path ) or ! is_file( $path ) )
  134. {
  135. return false;
  136. }
  137. $realpath = realpath( $path );
  138. if ( empty( $realpath ) )
  139. {
  140. return false;
  141. }
  142. $realpath = str_replace( '\\', '/', $realpath );
  143. $realpath = rtrim( $realpath, "\\/" );
  144. if ( ! preg_match( "/^(" . nv_preg_quote( $dir ) . ")(\/[\S]+)/", $realpath ) )
  145. {
  146. return false;
  147. }
  148. return $realpath;
  149. }
  150. /**
  151. * download::func_exists()
  152. *
  153. * @param mixed $funcName
  154. * @return
  155. */
  156. private function func_exists( $funcName )
  157. {
  158. return ( function_exists( $funcName ) and ! in_array( $funcName, $this->disable_functions ) );
  159. }
  160. /**
  161. * download::cl_exists()
  162. *
  163. * @param mixed $clName
  164. * @return
  165. */
  166. private function cl_exists( $clName )
  167. {
  168. return ( class_exists( $clName ) and ! in_array( $clName, $this->disable_classes ) );
  169. }
  170. /**
  171. * download::getextension()
  172. *
  173. * @param mixed $filename
  174. * @return
  175. */
  176. private function getextension( $filename )
  177. {
  178. if ( strpos( $filename, '.' ) === false ) return '';
  179. $filename = basename( strtolower( $filename ) );
  180. $filename = explode( '.', $filename );
  181. return array_pop( $filename );
  182. }
  183. /**
  184. * download::my_mime_content_type()
  185. *
  186. * @param mixed $path
  187. * @return
  188. */
  189. private function my_mime_content_type( $path )
  190. {
  191. $mime = '';
  192. if ( $this->func_exists( 'finfo_open' ) )
  193. {
  194. if ( empty( $this->magic_path ) )
  195. {
  196. $finfo = finfo_open( FILEINFO_MIME );
  197. } elseif ( $this->magic_path != "auto" )
  198. {
  199. $finfo = finfo_open( FILEINFO_MIME, $this->magic_path );
  200. }
  201. else
  202. {
  203. if ( ( $magic = getenv( 'MAGIC' ) ) !== false )
  204. {
  205. $finfo = finfo_open( FILEINFO_MIME, $magic );
  206. }
  207. else
  208. {
  209. if ( substr( PHP_OS, 0, 3 ) == 'WIN' )
  210. {
  211. $path = realpath( ini_get( 'extension_dir' ) . '/../' ) . 'extras/magic';
  212. $finfo = finfo_open( FILEINFO_MIME, $path );
  213. }
  214. else
  215. {
  216. $finfo = finfo_open( FILEINFO_MIME, '/usr/share/file/magic' );
  217. }
  218. }
  219. }
  220. if ( is_resource( $finfo ) )
  221. {
  222. $mime = finfo_file( $finfo, realpath( $path ) );
  223. finfo_close( $finfo );
  224. $mime = preg_replace( "/^([\.-\w]+)\/([\.-\w]+)(.*)$/i", '$1/$2', trim( $mime ) );
  225. }
  226. }
  227. if ( empty( $mime ) or $mime == "application/octet-stream" )
  228. {
  229. if ( $this->cl_exists( "finfo" ) )
  230. {
  231. $finfo = new finfo( FILEINFO_MIME );
  232. if ( $finfo )
  233. {
  234. $mime = $finfo->file( realpath( $path ) );
  235. $mime = preg_replace( "/^([\.-\w]+)\/([\.-\w]+)(.*)$/i", '$1/$2', trim( $mime ) );
  236. }
  237. }
  238. }
  239. if ( empty( $mime ) or $mime == "application/octet-stream" )
  240. {
  241. if ( substr( PHP_OS, 0, 3 ) != 'WIN' )
  242. {
  243. if ( $this->func_exists( 'system' ) )
  244. {
  245. ob_start();
  246. system( "file -i -b " . escapeshellarg( $path ) );
  247. $m = ob_get_clean();
  248. $m = trim( $m );
  249. if ( ! empty( $m ) )
  250. {
  251. $mime = preg_replace( "/^([\.-\w]+)\/([\.-\w]+)(.*)$/i", '$1/$2', $m );
  252. }
  253. } elseif ( $this->func_exists( 'exec' ) )
  254. {
  255. $m = @exec( "file -bi " . escapeshellarg( $path ) );
  256. $m = trim( $m );
  257. if ( ! empty( $m ) )
  258. {
  259. $mime = preg_replace( "/^([\.-\w]+)\/([\.-\w]+)(.*)$/i", '$1/$2', $m );
  260. }
  261. }
  262. }
  263. }
  264. if ( empty( $mime ) or $mime == "application/octet-stream" )
  265. {
  266. if ( $this->func_exists( 'mime_content_type' ) )
  267. {
  268. $mime = mime_content_type( $path );
  269. $mime = preg_replace( "/^([\.-\w]+)\/([\.-\w]+)(.*)$/i", '$1/$2', trim( $mime ) );
  270. }
  271. }
  272. if ( empty( $mime ) or $mime == "application/octet-stream" )
  273. {
  274. $img_exts = array( 'png', 'gif', 'jpg', 'bmp', 'tiff', 'swf', 'psd' );
  275. if ( in_array( $this->properties['extension'], $img_exts ) )
  276. {
  277. if ( ( $img_info = @getimagesize( $path ) ) !== false )
  278. {
  279. if ( array_key_exists( 'mime', $img_info ) and ! empty( $img_info['mime'] ) )
  280. {
  281. $mime = trim( $img_info['mime'] );
  282. $mime = preg_replace( "/^([\.-\w]+)\/([\.-\w]+)(.*)$/i", '$1/$2', $mime );
  283. }
  284. if ( empty( $mime ) and isset( $img_info[2] ) )
  285. {
  286. $mime = image_type_to_mime_type( $img_info[2] );
  287. }
  288. }
  289. }
  290. }
  291. if ( empty( $mime ) or $mime == "application/octet-stream" )
  292. {
  293. $mime_types = nv_parse_ini_file( NV_MIME_INI_FILE );
  294. if ( array_key_exists( $this->properties['extension'], $mime_types ) )
  295. {
  296. if ( is_string( $mime_types[$this->properties['extension']] ) ) return $mime_types[$this->properties['extension']];
  297. $mime = $mime_types[$this->properties['extension']][0];
  298. }
  299. }
  300. if ( preg_match( "/^application\/(?:x-)?zip(?:-compressed)?$/is", $mime ) )
  301. {
  302. if ( $this->properties['extension'] == "docx" ) $mime = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
  303. elseif ( $this->properties['extension'] == "dotx" ) $mime = "application/vnd.openxmlformats-officedocument.wordprocessingml.template";
  304. elseif ( $this->properties['extension'] == "potx" ) $mime = "application/vnd.openxmlformats-officedocument.presentationml.template";
  305. elseif ( $this->properties['extension'] == "ppsx" ) $mime = "application/vnd.openxmlformats-officedocument.presentationml.slideshow";
  306. elseif ( $this->properties['extension'] == "pptx" ) $mime = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
  307. elseif ( $this->properties['extension'] == "xlsx" ) $mime = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
  308. elseif ( $this->properties['extension'] == "xltx" ) $mime = "application/vnd.openxmlformats-officedocument.spreadsheetml.template";
  309. elseif ( $this->properties['extension'] == "docm" ) $mime = "application/vnd.ms-word.document.macroEnabled.12";
  310. elseif ( $this->properties['extension'] == "dotm" ) $mime = "application/vnd.ms-word.template.macroEnabled.12";
  311. elseif ( $this->properties['extension'] == "potm" ) $mime = "application/vnd.ms-powerpoint.template.macroEnabled.12";
  312. elseif ( $this->properties['extension'] == "ppam" ) $mime = "application/vnd.ms-powerpoint.addin.macroEnabled.12";
  313. elseif ( $this->properties['extension'] == "ppsm" ) $mime = "application/vnd.ms-powerpoint.slideshow.macroEnabled.12";
  314. elseif ( $this->properties['extension'] == "pptm" ) $mime = "application/vnd.ms-powerpoint.presentation.macroEnabled.12";
  315. elseif ( $this->properties['extension'] == "xlam" ) $mime = "application/vnd.ms-excel.addin.macroEnabled.12";
  316. elseif ( $this->properties['extension'] == "xlsb" ) $mime = "application/vnd.ms-excel.sheet.binary.macroEnabled.12";
  317. elseif ( $this->properties['extension'] == "xlsm" ) $mime = "application/vnd.ms-excel.sheet.macroEnabled.12";
  318. elseif ( $this->properties['extension'] == "xltm" ) $mime = "application/vnd.ms-excel.template.macroEnabled.12";
  319. }
  320. return ! empty( $mime ) ? $mime : 'application/force-download';
  321. }
  322. /**
  323. * download::nv_getenv()
  324. *
  325. * @param mixed $key
  326. * @return
  327. */
  328. private function nv_getenv( $key )
  329. {
  330. if ( isset( $_SERVER[$key] ) )
  331. {
  332. return $_SERVER[$key];
  333. } elseif ( isset( $_ENV[$key] ) )
  334. {
  335. return $_ENV[$key];
  336. } elseif ( @getenv( $key ) )
  337. {
  338. return @getenv( $key );
  339. } elseif ( function_exists( 'apache_getenv' ) && apache_getenv( $key, true ) )
  340. {
  341. return apache_getenv( $key, true );
  342. }
  343. return "";
  344. }
  345. /**
  346. * download::get_property()
  347. *
  348. * @param mixed $property
  349. * @return
  350. */
  351. public function get_property( $property )
  352. {
  353. if ( array_key_exists( $property, $this->properties ) ) return $this->properties[$property];
  354. else return null;
  355. }
  356. /**
  357. * download::set_property()
  358. *
  359. * @param mixed $property
  360. * @param mixed $value
  361. * @return
  362. */
  363. public function set_property( $property, $value )
  364. {
  365. if ( array_key_exists( $property, $this->properties ) )
  366. {
  367. $this->properties[$property] = $value;
  368. return true;
  369. }
  370. else return false;
  371. }
  372. /**
  373. * download::download_file()
  374. *
  375. * @return
  376. */
  377. public function download_file()
  378. {
  379. if ( ! $this->properties['path'] )
  380. {
  381. die( "Nothing to download!" );
  382. }
  383. $seek_start = 0;
  384. $seek_end = -1;
  385. $data_section = false;
  386. if ( ( $http_range = nv_getenv( 'HTTP_RANGE' ) ) != "" )
  387. {
  388. $seek_range = substr( $http_range, strlen( 'bytes=' ) );
  389. $range = explode( '-', $seek_range );
  390. if ( ! empty( $range[0] ) )
  391. {
  392. $seek_start = intval( $range[0] );
  393. }
  394. if ( isset( $range[1] ) and ! empty( $range[1] ) )
  395. {
  396. $seek_end = intval( $range[1] );
  397. }
  398. if ( ! $this->properties['resume'] )
  399. {
  400. $seek_start = 0;
  401. }
  402. else
  403. {
  404. $data_section = true;
  405. }
  406. }
  407. if ( @ob_get_length() )
  408. {
  409. @ob_end_clean();
  410. }
  411. $old_status = ignore_user_abort( true );
  412. if ( defined( 'ALLOWED_SET_TIME_LIMIT' ) )
  413. {
  414. set_time_limit( 0 );
  415. }
  416. if ( $seek_start > ( $this->properties['size'] - 1 ) )
  417. {
  418. $seek_start = 0;
  419. }
  420. $res = fopen( $this->properties['path'], 'rb' );
  421. if ( ! $res )
  422. {
  423. die( 'File error' );
  424. }
  425. if ( $seek_start ) fseek( $res, $seek_start );
  426. if ( $seek_end < $seek_start )
  427. {
  428. $seek_end = $this->properties['size'] - 1;
  429. }
  430. header( "Pragma: public" );
  431. header( "Expires: 0" );
  432. header( "Cache-Control:" );
  433. header( "Cache-Control: public" );
  434. header( "Content-Description: File Transfer" );
  435. header( "Content-Type: " . $this->properties['type'] );
  436. if ( strstr( $this->nv_getenv( 'HTTP_USER_AGENT' ), "MSIE" ) != false )
  437. {
  438. header( 'Content-Disposition: attachment; filename="' . urlencode( $this->properties['name'] ) . '";' );
  439. }
  440. else
  441. {
  442. header( 'Content-Disposition: attachment; filename="' . $this->properties['name'] . '";' );
  443. }
  444. header( 'Last-Modified: ' . date( 'D, d M Y H:i:s \G\M\T', $this->properties['mtime'] ) );
  445. if ( $data_section and $this->properties['resume'] )
  446. {
  447. header( "HTTP/1.1 206 Partial Content" );
  448. header( "Status: 206 Partial Content" );
  449. header( 'Accept-Ranges: bytes' );
  450. header( "Content-Range: bytes " . $seek_start . "-" . $seek_end . "/" . $this->properties['size'] );
  451. header( "Content-Length: " . ( $seek_end - $seek_start + 1 ) );
  452. }
  453. else
  454. {
  455. header( "Content-Length: " . $this->properties['size'] );
  456. }
  457. if ( function_exists( 'usleep' ) and ! in_array( 'usleep', $this->disable_functions ) and ( $speed = $this->properties['max_speed'] ) > 0 )
  458. {
  459. $sleep_time = ( 8 / $speed ) * 1e6;
  460. }
  461. else
  462. {
  463. $sleep_time = 0;
  464. }
  465. while ( ! ( connection_aborted() or connection_status() == 1 ) and ! feof( $res ) )
  466. {
  467. print ( fread( $res, 1024 * 8 ) );
  468. flush();
  469. if ( $sleep_time > 0 )
  470. {
  471. usleep( $sleep_time );
  472. }
  473. }
  474. fclose( $res );
  475. ignore_user_abort( $old_status );
  476. if ( defined( 'ALLOWED_SET_TIME_LIMIT' ) )
  477. {
  478. set_time_limit( ini_get( "max_execution_time" ) );
  479. }
  480. exit();
  481. }
  482. }
  483. ?>