PageRenderTime 51ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/atk4-addons/misc/lib/Controller/Filestore.php

https://github.com/git86/todo
PHP | 431 lines | 298 code | 78 blank | 55 comment | 52 complexity | f0d123bcab54beae23a00edb05c14c28 MD5 | raw file
Possible License(s): AGPL-3.0
  1. <?php
  2. class Controller_Filestore extends Controller {
  3. var $_id;
  4. var $_filenum;
  5. var $filename;
  6. var $filesize;
  7. var $mime_type;
  8. var $_filespace_id;
  9. var $_dirname;
  10. var $_backup_file_id; // source file for converted audio
  11. var $mError;
  12. var $error_message;
  13. /**
  14. * for check prohibition action (try to get audio without advertise as picture)
  15. */
  16. var $allowed_mime_type;
  17. function mFile() {
  18. global $mError;
  19. $this->mError = $mError;
  20. }
  21. function _error($mes) {
  22. global $db;
  23. if (!($db->getOne("/* hack */select IS_FREE_LOCK('fs_fnum_lock')")))
  24. $db->getOne("/* hack */select release_lock('fs_fnum_lock')");
  25. $this->error_message = $mes;
  26. return false;
  27. }
  28. function getRec($id, $ignore_errors = false) {
  29. global $db;
  30. if (($rec = $db->getRow('select f.id, f.filename, f.filesize, f.filenum, ft.mime_type, ' .
  31. ' f.filespace_id, fs.dirname, f.backup_file_id '.
  32. ' from '.tbn('file').' f '.
  33. ' inner join '.tbn('filetype').' ft on ft.id = f.filetype_id '.
  34. ' inner join '.tbn('filespace').' fs on fs.id = f.filespace_id '.
  35. ' where f.id = '.$id,
  36. DBL_FETCHMODE_ASSOC
  37. ))===false) return $this->_error($this->mError->db(__FILE__,__LINE__));
  38. if (empty($rec['id'])) {
  39. $msg = 'Error get file properties for file '.$id;
  40. if ($ignore_errors)
  41. return $this->_error($msg);
  42. else
  43. return $this->_error($this->mError->internal($msg,__FILE__,__LINE__));
  44. }
  45. $this->_id = $id;
  46. $this->_filenum = $rec['filenum'];
  47. $this->filename = $rec['filename'];
  48. $this->filesize = $rec['filesize'];
  49. $this->mime_type = $rec['mime_type'];
  50. $this->_filespace_id = $rec['filespace_id'];
  51. $this->_dirname = $rec['dirname'];
  52. $this->_backup_file_id = $rec['backup_file_id'];
  53. if ((!empty($this->allowed_mime_type)) and (strpos($this->allowed_mime_type,$this->mime_type)===false)) {
  54. $this->error_message = 'Unallowed file mime type!';
  55. return false;
  56. }
  57. else
  58. return true;
  59. }
  60. function directory_exists() {
  61. return file_exists($this->_dirname.'/000');
  62. }
  63. function _get_filespace($filesize) {
  64. global $db;
  65. if (($res = $db->getRow('select id, dirname '.
  66. ' from '.tbn('filespace').
  67. ' where enabled = \'1\' and (used_space + :filesize) <= total_space ' .
  68. ' and stored_files_cnt < 4096*256*256 '.
  69. ' limit 1',
  70. array('int filesize'=>$filesize),
  71. DBL_FETCHMODE_ASSOC))===false) {
  72. return $this->_error($this->mError->db(__FILE__,__LINE__));}
  73. if (empty($res) or (disk_free_space($res['dirname'])<$filesize)) {
  74. return $this->_error($this->mError->internal('Unavailable filespace for save file!'));
  75. }
  76. $this->_filespace_id = $res['id'];
  77. $this->_dirname = $res['dirname'];
  78. return true;
  79. }
  80. function _get_filetype_id($mime_type) {
  81. global $db;
  82. if (($res = $db->getOne('select id '.
  83. ' from '.tbn('filetype').
  84. ' where mime_type = :mime_type ',
  85. array('mime_type'=>$mime_type)
  86. ))===false) return $this->_error($this->mError->db(__FILE__,__LINE__));
  87. if (empty($res)) {
  88. if ($db->query('insert into '.tbn('filetype').' (mime_type) values (:mime_type)',
  89. array('mime_type'=>$mime_type)
  90. )===false) return $this->_error($this->mError->db(__FILE__,__LINE__));
  91. $res = $db->lastId();
  92. }
  93. return $res;
  94. }
  95. /**
  96. * Save filename in available filespace and return id from "file" table
  97. */
  98. function save($filename, $orig_filename, $mime_type, $adv_spots = null) {
  99. global $db;
  100. $this->error_message = '';
  101. $this->filesize = filesize($filename);
  102. if ($this->_get_filespace($this->filesize)==false) return false;
  103. /*
  104. $fext = array_pop(explode('.', $filename));
  105. $fname = basename($filename, '.'.$fext);
  106. */
  107. $this->mime_type = $mime_type;
  108. $this->filename = $orig_filename;
  109. // get lock for calc next filenum (timeout 5 seconds)
  110. if (($res_op = $db->getOne("/* hack */select get_lock('fs_fnum_lock',5)"
  111. ))===false)
  112. return $this->_error($this->mError->db(__FILE__,__LINE__));
  113. if (empty($res_op)) return $this->_error('Error get lock for calculate next filenumber.');
  114. if (($filenum = $db->getOne(
  115. 'select filenum from '.tbn('filedelnum').
  116. ' where filespace_id = '.$this->_filespace_id.' limit 1'
  117. ))===false)
  118. return $this->_error($this->mError->db(__FILE__,__LINE__));
  119. if (!empty($filenum)) {
  120. if ($db->query('delete from '.tbn('filedelnum').
  121. ' where filespace_id = '.$this->_filespace_id.
  122. ' and filenum = '.$filenum)===false)
  123. return $this->_error($this->mError->db(__FILE__,__LINE__));
  124. }
  125. else {
  126. if (($filenum = $db->getOne(
  127. 'select max(filenum)+1 from '.tbn('file').
  128. ' where filespace_id = '.$this->_filespace_id
  129. ))===false)
  130. return $this->_error($this->mError->db(__FILE__,__LINE__));
  131. $filenum = (empty($filenum))?1:$filenum;
  132. }
  133. if ($db->query('insert into '.tbn('file').' (filespace_id, filetype_id, filenum, filename, filesize, adv_spots, ins_dts) '.
  134. ' values (:filespace_id, :filetype_id, :filenum, :filename, :filesize, :adv_spots, :ins_dts)',
  135. array(
  136. 'int filespace_id'=>$this->_filespace_id,
  137. 'int filetype_id'=>$this->_get_filetype_id($mime_type),
  138. 'int filenum'=>$filenum,
  139. 'filename'=>$this->filename,
  140. 'int filesize'=>$this->filesize,
  141. 'adv_spots'=>$adv_spots,
  142. 'date ins_dts'=>time()
  143. )
  144. )===false) return $this->_error($this->mError->db(__FILE__,__LINE__));
  145. $this->_filenum = $filenum;
  146. $this->_id = $db->lastId();
  147. // release lock
  148. if (($res_op = $db->getOne("/* hack */select release_lock('fs_fnum_lock')"
  149. ))===false)
  150. return $this->_error($this->mError->db(__FILE__,__LINE__));
  151. // move file
  152. if (!@rename($filename,$this->_real_filename())) {
  153. $db->query('delete from '.tbn('file').' where id = '.$this->_id);
  154. $this->_id = null;
  155. return $this->_error($this->mError->internal('Error move file '.$filename.' into filespace ('.$this->_real_filename().')!',__FILE__,__LINE__));
  156. }
  157. else {
  158. // increase used space
  159. if ($db->query('update '.tbn('filespace').
  160. ' set used_space = used_space + :filesize, stored_files_cnt = stored_files_cnt + 1 '.
  161. ' where id = :id',
  162. array('int filesize'=>$this->filesize,
  163. 'int id'=>$this->_filespace_id))===false)
  164. return $this->_error($this->mError->db(__FILE__,__LINE__));
  165. /*
  166. // disabled filespace if used space more than total space
  167. if ($db->query('update '.tbn('filespace').
  168. ' set enabled = \'0\' '.
  169. ' where enabled = \'1\' and used_space >= total_space and id = :id',
  170. array('int id'=>$this->_filespace_id))===false) db_error(__FILE__,__LINE__);
  171. */
  172. }
  173. return $this->_id;
  174. }
  175. // delete current file
  176. function delete() {
  177. global $db;
  178. if (empty($this->_id)) return false;
  179. if ($res = @unlink($this->_real_filename())) {
  180. // update filespace record (decrease used space)
  181. if ($db->query('update '.tbn('filespace').
  182. ' set used_space = used_space - :filesize, stored_files_cnt = stored_files_cnt - 1 '.
  183. ' where id = :id',
  184. array('int filesize'=>$this->filesize,
  185. 'int id'=>$this->_filespace_id))===false)
  186. return $this->_error($this->mError->db(__FILE__,__LINE__));
  187. /*
  188. // enabled filespace if used space less than total space
  189. if ($db->query('update '.tbn('filespace').
  190. ' set enabled = \'1\' '.
  191. ' where enabled = \'0\' and used_space < total_space and id = :id',
  192. array('int id'=>$this->_filespace_id))===false) db_error(__FILE__,__LINE__);
  193. */
  194. // delete record desctibe currect file
  195. if ($db->query('delete from '.tbn('file').
  196. ' where id = :id',
  197. array('int id'=>$this->_id))===false)
  198. return $this->_error($this->mError->db(__FILE__,__LINE__));
  199. if ($db->query('insert into '.tbn('filedelnum').' (filespace_id, filenum) '.
  200. ' values (:filespace_id, :filenum)',
  201. array('int filespace_id'=>$this->_filespace_id,
  202. 'int filenum'=>$this->_filenum))===false)
  203. return $this->_error($this->mError->db(__FILE__,__LINE__));
  204. if (!empty($this->_backup_file_id)) {
  205. $backup_file_id = $this->_backup_file_id; $this->_backup_file_id = null;
  206. if ($this->getRec($backup_file_id))
  207. $res = $this->delete();
  208. }
  209. }
  210. return $res;
  211. }
  212. // return filename in filespace, create directories if necessary
  213. function _real_filename() {
  214. $path = str_pad(strtoupper(dechex($this->_filenum)), 7, '0', STR_PAD_LEFT);
  215. $res = $this->_dirname.DIRECTORY_SEPARATOR.substr($path,0,3);
  216. if (!file_exists($res))
  217. if (!@mkdir($res))
  218. return $this->_error($this->mError->internal('Error create directory '.$res,__FILE__,__LINE__));
  219. $res .= DIRECTORY_SEPARATOR.substr($path,3,2);
  220. if (!file_exists($res))
  221. if (!@mkdir($res))
  222. return $this->_error($this->mError->internal('Error create directory '.$res,__FILE__,__LINE__));
  223. $res .= DIRECTORY_SEPARATOR.substr($path,5,2);
  224. return $res;
  225. }
  226. function read($inline = true) {
  227. $real_filename = $this->_real_filename();
  228. /*
  229. global $trace;
  230. $trace->p('$real_filename: '.$real_filename);
  231. $trace->p('this: '.print_r($this,true));
  232. */
  233. header('Last-Modified: '.gmdate('D, d M Y H:i:s',filemtime($real_filename)) . ' GMT');
  234. header('Content-Transfer-Encoding: none');
  235. header('Content-Type: '.$this->mime_type.'; name="' . $this->filename . '"');
  236. header('Content-Disposition: '.($inline?'inline':'attachment').'; filename="' . $this->filename . '"');
  237. header('Content-length: '. $this->filesize);
  238. @readfile($real_filename);
  239. }
  240. function _sendHeaders($datetime, $mime_type, $name) {
  241. header('Last-Modified: '.date( 'r', $datetime));
  242. header('Content-Transfer-Encoding: none');
  243. header('Content-Type: '.$mime_type.'; name="' . $name . '"');
  244. header('Content-Disposition: inline; filename="' . $name . '"');
  245. header('Expires: '.date( 'r', time()+3600*24*30));
  246. header('Cache-control: Public');
  247. header('Pragma: cache');
  248. }
  249. function _cached_filename($dirname, $hash_filename) {
  250. if (!file_exists($dirname))
  251. if (!mkdir($dirname)) error_log('Can\'t make directory '.$dirname);
  252. $dirname .= '/'.substr($hash_filename,0,3);
  253. if (!file_exists($dirname))
  254. if (!mkdir($dirname)) error_log('Can\'t make directory '.$dirname);
  255. $dirname .= '/'.substr($hash_filename,3,3);
  256. if (!file_exists($dirname))
  257. if (!mkdir($dirname)) error_log('Can\'t make directory '.$dirname);
  258. $dirname .= '/'.substr($hash_filename,6,3);
  259. if (!file_exists($dirname))
  260. if (!mkdir($dirname)) error_log('Can\'t make directory '.$dirname);
  261. return $dirname.'/'.substr($hash_filename,9,23);
  262. }
  263. /**
  264. * read image from filespace and output to browser
  265. * size_limit - on pixels, image crop for square with parts equal size
  266. */
  267. function readImagePreview($dirname, $name, $mime_type, $file_num, $size_limit, $cache_dir = null) {
  268. if (!is_null($cache_dir)) {
  269. $cache_filename = $this->_cached_filename($cache_dir,md5($dirname. $name. $mime_type. $file_num. $size_limit));
  270. if (file_exists($cache_filename)) {
  271. $this->_sendHeaders(filemtime($cache_filename), $mime_type, $name);
  272. readfile($cache_filename);
  273. exit;
  274. }
  275. }
  276. else
  277. $cache_filename = null;
  278. $this->filename = $name;
  279. $this->_filenum = $file_num;
  280. $this->_dirname = $dirname;
  281. $this->mime_type = $mime_type;
  282. if (($real_filename = $this->_real_filename())===false) {
  283. error_log('Error: '.$this->error_message.' at file '.__FILE__.' line '.__LINE__);
  284. exit;
  285. }
  286. $size = getimagesize($real_filename);
  287. list($orig_width,$orig_height,$type) = $size;
  288. $this->_sendHeaders(filemtime($real_filename), $mime_type, $name);
  289. if (
  290. ((($orig_width>$size_limit))) // or ($orig_height>$size_limit)))
  291. and
  292. (($type==1) or ($type==2) or ($type==3)) // supported image types
  293. ) {
  294. // need resize
  295. if ($orig_width >= $orig_height) {
  296. $ratio = $orig_height / $orig_width;
  297. $new_width = $size_limit;
  298. $new_height = (int) $size_limit*$ratio;
  299. }
  300. else {
  301. $ratio = $orig_width / $orig_height;
  302. if ($orig_width >= $size_limit) {
  303. $new_width = $size_limit;
  304. $new_height = (int) $size_limit/$ratio;
  305. }
  306. else {
  307. $new_height = $size_limit;
  308. $new_width = (int) $size_limit*$ratio;
  309. }
  310. }
  311. /*
  312. if ($orig_width >= $orig_height) {
  313. $ratio = $orig_height / $orig_width;
  314. $new_width = $size_limit;
  315. $new_height = (int) $size_limit*$ratio;
  316. }
  317. else {
  318. $ratio = $orig_width / $orig_height;
  319. $new_height = $size_limit;
  320. $new_width = (int) $size_limit*$ratio;
  321. }
  322. */
  323. switch ((int) $type) {
  324. case 1:
  325. $imagecreate_func = 'imagecreatefromgif';
  326. $imagesave_func = 'imagegif';
  327. break;
  328. case 2:
  329. $imagecreate_func = 'imagecreatefromjpeg';
  330. $imagesave_func = 'imagejpeg';
  331. break;
  332. case 3:
  333. $imagecreate_func = 'imagecreatefrompng';
  334. $imagesave_func = 'imagepng';
  335. break;
  336. }
  337. $orig_img = $imagecreate_func($real_filename);
  338. $new_img = ImageCreateTrueColor($new_width, $new_height);
  339. ImageCopyResampled($new_img, $orig_img, 0, 0, 0, 0, $new_width, $new_height,
  340. $orig_width, $orig_height);
  341. if ((!$imagesave_func($new_img,$cache_filename)) and (!is_null($cache_filename))) {
  342. $imagesave_func($new_img);
  343. }
  344. elseif (!is_null($cache_filename))
  345. readfile($cache_filename);
  346. ImageDestroy($orig_img);
  347. ImageDestroy($new_img);
  348. }
  349. else
  350. readfile($real_filename);
  351. }
  352. }