PageRenderTime 55ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/ic2.php

http://github.com/unpush/p2-php
PHP | 1072 lines | 840 code | 117 blank | 115 comment | 230 complexity | dfef672bedd44e3ddac6fa6243d71ddc MD5 | raw file
  1. <?php
  2. /**
  3. * ImageCache2 - 画像のダウンロード・サムネイル作成
  4. */
  5. // {{{ p2基本設定読み込み&認証
  6. define('P2_OUTPUT_XHTML', 1);
  7. require_once './conf/conf.inc.php';
  8. $_login->authorize();
  9. if (!$_conf['expack.ic2.enabled']) {
  10. p2die('ImageCache2は無効です。', 'conf/conf_admin_ex.inc.php の設定を変えてください。');
  11. }
  12. // }}}
  13. // {{{ 初期化
  14. // ライブラリ読み込み
  15. require_once 'HTTP/Client.php';
  16. require_once P2EX_LIB_DIR . '/ic2/bootstrap.php';
  17. // 受け付けるMIMEタイプ
  18. $mimemap = array('image/jpeg' => '.jpg', 'image/png' => '.png', 'image/gif' => '.gif');
  19. // 設定ファイル読み込み
  20. $ini = ic2_loadconfig();
  21. // }}}
  22. // {{{ prepare
  23. // パラメータを設定
  24. $id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : null;
  25. $uri = isset($_REQUEST['uri']) ? $_REQUEST['uri'] : (isset($_REQUEST['url']) ? $_REQUEST['url'] : null);
  26. $file = isset($_REQUEST['file']) ? $_REQUEST['file'] : null;
  27. $force = !empty($_REQUEST['f']); // 強制更新
  28. $thumb = isset($_REQUEST['t']) ? intval($_REQUEST['t']) : IC2_Thumbnailer::SIZE_SOURCE; // サムネイルタイプ
  29. $redirect = isset($_REQUEST['r']) ? intval($_REQUEST['r']) : 1; // 表示方法
  30. $rank = isset($_REQUEST['rank']) ? intval($_REQUEST['rank']) : 0; // レーティング
  31. $memo = (isset($_REQUEST['memo']) && strlen($_REQUEST['memo']) > 0) ? $_REQUEST['memo'] : null; // メモ
  32. $referer = (isset($_REQUEST['ref']) && strlen($_REQUEST['ref']) > 0) ? $_REQUEST['ref'] : null; // リファラ
  33. /*if (!isset($uri) && false !== ($url = getenv('PATH_INFO'))) {
  34. $uri = 'http:/' . $url;
  35. }*/
  36. if (empty($id) && empty($uri) && empty($file)) {
  37. ic2_error('x06', 'URLまたはファイル名がありません。', false);
  38. }
  39. if (!is_dir($_conf['tmp_dir'])) {
  40. FileCtl::mkdirRecursive($_conf['tmp_dir']);
  41. }
  42. if (!empty($uri)) {
  43. $uri = preg_replace('{^(https?://)ime\\.(?:nu|st)/}', '\\1', $uri);
  44. $pURL = @parse_url($uri);
  45. if (!$pURL || ($pURL['scheme'] != 'http' && $pURL['scheme'] != 'https') ||
  46. empty($pURL['host']) || empty($pURL['path']))
  47. {
  48. ic2_error('x06', '不正なURLです。', false);
  49. }
  50. // 強制あぼーんホストのとき
  51. if ($ini['Getter']['reject_hosts']) {
  52. $pattern = preg_quote($ini['Getter']['reject_hosts'], '/');
  53. $pattern = str_replace(',', '|', $pattern);
  54. $pattern = '/(' . $pattern . ')$/i';
  55. if (preg_match($pattern, $pURL['host'])) {
  56. ic2_error('x01', 'あぼーん対象ホストです。');
  57. }
  58. }
  59. // 強制あぼーんURLのとき
  60. if ($ini['Getter']['reject_regex']) {
  61. $pattern = str_replace('/', '\\/', $ini['Getter']['reject_regex']);
  62. $pattern = '/(' . $pattern . ')$/i';
  63. if (preg_match($pattern, $uri)) {
  64. ic2_error('x01', 'あぼーん対象URLです。');
  65. }
  66. }
  67. $doDL = true;
  68. } else {
  69. if (isset($file) && !preg_match('/^(?P<size>[1-9][0-9]*)_(?P<md5>[0-9a-f]{32})(?:\.(?P<ext>jpg|png|gif))?$/', $file, $fdata)) {
  70. ic2_error('x06', '不正なファイル名です。', false);
  71. }
  72. $doDL = false;
  73. }
  74. // 値の調整
  75. if (!in_array($thumb, array(IC2_Thumbnailer::SIZE_SOURCE,
  76. IC2_Thumbnailer::SIZE_PC,
  77. IC2_Thumbnailer::SIZE_MOBILE,
  78. IC2_Thumbnailer::SIZE_INTERMD)))
  79. {
  80. $thumb = IC2_Thumbnailer::SIZE_DEFAULT;
  81. }
  82. if ($rank < -1) {
  83. $rank = -1;
  84. } elseif ($rank > 5) {
  85. $rank = 5;
  86. }
  87. if ($memo === '') {
  88. $memo = null;
  89. }
  90. $thumbnailer = new IC2_Thumbnailer($thumb);
  91. // }}}
  92. // {{{ IC2TempFile
  93. class IC2TempFile
  94. {
  95. private $_filename = null;
  96. public function __construct($filename)
  97. {
  98. if (touch($filename)) {
  99. $this->_filename = realpath($filename);
  100. }
  101. }
  102. public function __destruct()
  103. {
  104. if ($this->_filename !== null) {
  105. if (file_exists($this->_filename)) {
  106. unlink($this->_filename);
  107. }
  108. }
  109. }
  110. }
  111. // }}}
  112. // {{{ sleep
  113. if ($doDL) {
  114. // 同じ画像のURIに対するクエリが(ほぼ)同時に発行されたときの重複GETを防ぐ
  115. // sleepした時間はプロセスの実行時間に含まれないので独自にタイマーを用意する(無限ループ回避)
  116. $dl_lock_file = $_conf['tmp_dir'] . DIRECTORY_SEPARATOR . 'ic2_lck_' . md5($uri);
  117. if (file_exists($dl_lock_file)) {
  118. $offtimer = ini_get('max_execution_time');
  119. if ($offtimer == 0) {
  120. $offtimer = 30;
  121. }
  122. while (file_exists($dl_lock_file)) {
  123. sleep(1); // 1秒停止
  124. $offtimer--;
  125. if ($offtimer < 0) {
  126. ic2_error(504);
  127. }
  128. }
  129. }
  130. // テンポラリファイルを作成、終了時に自動削除
  131. $dl_lock_obj = new IC2TempFile($dl_lock_file);
  132. }
  133. // }}}
  134. // {{{ search
  135. // 画像がキャッシュされているか確認
  136. $search = new IC2_DataObject_Images;
  137. $retry = false;
  138. if ($memo !== null) {
  139. $memo = $search->uniform($memo, 'CP932');
  140. }
  141. if ($doDL) {
  142. $result = $search->get($uri);
  143. } else {
  144. if (isset($id)) {
  145. $search->whereAddQuoted('id', '=', $id);
  146. } else {
  147. $search->whereAddQuoted('size', '=', $fdata['size']);
  148. $search->whereAddQuoted('md5', '=', $fdata['md5']);
  149. }
  150. $result = $search->find(true);
  151. if (!$result) {
  152. ic2_error('404');
  153. }
  154. $force = false;
  155. }
  156. if ($result) {
  157. // ウィルススキャンにひっかかったファイルだったら終了。
  158. if (!$force && $search->mime == 'clamscan/infected') {
  159. ic2_error('x04', '', false);
  160. }
  161. // あぼーんフラグ(rankが負)が立っていたら終了。
  162. if (!$force && $search->rank < 0 && !isset($_REQUEST['rank'])) {
  163. ic2_error('x01', '', false);
  164. }
  165. $filepath = $thumbnailer->srcPath($search->size, $search->md5, $search->mime);
  166. $params = array('uri' => $search->uri, 'name' => $search->name, 'size' => $search->size,
  167. 'md5' => $search->md5, 'width' => $search->width, 'height' => $search->height,
  168. 'mime' => $search->mime, 'memo' => $search->memo, 'rank' => $search->rank);
  169. // 自動メモ機能が有効のとき
  170. if ($ini['General']['automemo'] && !is_null($memo) && strpos($search->memo, $memo) === false) {
  171. if (is_string($search->memo) && strlen($search->memo) > 0) {
  172. $memo .= ' ' . $search->memo;
  173. }
  174. $update = new IC2_DataObject_Images;
  175. $update->memo = $params['memo'] = $memo;
  176. $update->whereAddQuoted('uri', '=', $search->uri);
  177. $update->update();
  178. unset($update);
  179. }
  180. // ランク変更
  181. if (isset($_REQUEST['rank'])) {
  182. $update = new IC2_DataObject_Images;
  183. $update->rank = $params['rank'] = $rank;
  184. $update->whereAddQuoted('size', '=', $search->size);
  185. $update->whereAddQuoted('md5', '=', $search->md5);
  186. $update->whereAddQuoted('mime', '=', $search->mime);
  187. $update->update();
  188. unset($update);
  189. }
  190. // ファイルが保存されていればそれでよし、保存されていなければレコードを削除する。
  191. if (file_exists($filepath)) {
  192. if ($force) {
  193. $_size = $search->size;
  194. $_md5 = $search->md5;
  195. $_mime = $search->mime;
  196. $time = $search->time;
  197. } else {
  198. ic2_finish($filepath, $thumb, $params, false);
  199. }
  200. } else {
  201. $retry = true;
  202. $force = false;
  203. $_size = $search->size;
  204. $_md5 = $search->md5;
  205. $_mime = $search->mime;
  206. }
  207. } else {
  208. $filepath = '';
  209. }
  210. // 画像がブラックリストにあるか確認
  211. $blacklist = new IC2_DataObject_BlackList;
  212. if ($blacklist->get($uri)) {
  213. switch ($blacklist->type) {
  214. case 0:
  215. $errcode = 'x05'; // お腹いっぱい
  216. break;
  217. case 1:
  218. $errcode = 'x01'; // あぼーん
  219. break;
  220. case 2:
  221. $errcode = 'x04'; // ウィルス感染
  222. break;
  223. default:
  224. $errcode = 'x06'; // ???
  225. }
  226. ic2_error($errcode, '', false);
  227. }
  228. // 画像がエラーログにあるか確認
  229. if (!$force && $ini['Getter']['checkerror']) {
  230. $errlog = new IC2_DataObject_Errors;
  231. if ($errlog->get($uri)) {
  232. ic2_error($errlog->errcode, '', false);
  233. }
  234. }
  235. // }}}
  236. // {{{ init http-client
  237. // 設定を確認
  238. $conn_timeout = (isset($ini['Getter']['conn_timeout']) && $ini['Getter']['conn_timeout'] > 0)
  239. ? (float) $ini['Getter']['conn_timeout'] : 60.0;
  240. $read_timeout = (isset($ini['Getter']['read_timeout']) && $ini['Getter']['read_timeout'] > 0)
  241. ? (int) $ini['Getter']['read_timeout'] : 60;
  242. $ic2_ua = (!empty($_conf['expack.user_agent']))
  243. ? $_conf['expack.user_agent'] : $_SERVER['HTTP_USER_AGENT'];
  244. // キャッシュされていなければ、取得を試みる
  245. $client = new HTTP_Client;
  246. $client->setRequestParameter('timeout', $conn_timeout);
  247. $client->setRequestParameter('readTimeout', array($read_timeout, 0));
  248. $client->setMaxRedirects(3);
  249. $client->setDefaultHeader('User-Agent', $ic2_ua);
  250. if ($force && $time) {
  251. $client->setDefaultHeader('If-Modified-Since', http_date($time));
  252. }
  253. // プロキシ設定
  254. if ($ini['Proxy']['enabled'] && $ini['Proxy']['host'] && $ini['Proxy']['port']) {
  255. $client->setRequestParameter('proxy_host', $ini['Proxy']['host']);
  256. $client->setRequestParameter('proxy_port', $ini['Proxy']['port']);
  257. if ($ini['Proxy']['user']) {
  258. $client->setRequestParameter('proxy_user', $ini['Proxy']['user']);
  259. $client->setRequestParameter('proxy_pass', $ini['Proxy']['pass']);
  260. $proxy_auth_data = base64_encode($ini['Proxy']['user'] . ':' . $ini['Proxy']['pass']);
  261. $client->setDefaultHeader('Proxy-Authorization', 'Basic ' . $proxy_auth_data);
  262. }
  263. }
  264. // リファラ設定
  265. if (is_null($referer)) {
  266. $send_referer = (boolean)$ini['Getter']['sendreferer'];
  267. if ($send_referer) {
  268. if ($ini['Getter']['norefhosts']) {
  269. $pattern = preg_quote($ini['Getter']['norefhosts'], '/');
  270. $pattern = str_replace(',', '|', $pattern);
  271. $pattern = '/' . $pattern . '/i';
  272. if (preg_match($pattern, $pURL['host'])) {
  273. $send_referer = false;
  274. }
  275. }
  276. } elseif ($ini['Getter']['refhosts']) {
  277. $pattern = preg_quote($ini['Getter']['refhosts'], '/');
  278. $pattern = str_replace(',', '|', $pattern);
  279. $pattern = '/' . $pattern . '/i';
  280. if (preg_match($pattern, $pURL['host'])) {
  281. $send_referer = true;
  282. }
  283. }
  284. if ($send_referer) {
  285. $referer = $uri . '.html';
  286. }
  287. }
  288. if (is_string($referer)) {
  289. $client->setDefaultHeader('Referer', $referer);
  290. }
  291. // }}}
  292. // {{{ head
  293. $retry = 0;
  294. if ($ini['Getter']['omit_head'] == 0) { // HEAD省略
  295. // まずはHEADでチェック
  296. $client_h = clone $client;
  297. if ($ini['Getter']['retry_regex'] &&
  298. strlen(trim($ini['Getter']['retry_regex'])) > 0 &&
  299. intval($ini['Getter']['retry_max']) > 0 &&
  300. preg_match($ini['Getter']['retry_regex'], $uri))
  301. {
  302. do {
  303. $code = $client_h->head($uri);
  304. if ($code != 403) {
  305. break;
  306. }
  307. $retry++;
  308. sleep($ini['Getter']['retry_interval']);
  309. } while ($retry < intval($ini['Getter']['retry_max']));
  310. } else {
  311. $code = $client_h->head($uri);
  312. }
  313. if (PEAR::isError($code)) {
  314. ic2_error('x02', $code->getMessage());
  315. }
  316. $head = $client_h->currentResponse();
  317. // 304 Not Modified のとき
  318. if ($filepath && $force && $time && $code == 304) {
  319. ic2_finish($filepath, $thumb, $params, false);
  320. }
  321. // 200以外のときは失敗とみなす
  322. if ($code != 200) {
  323. ic2_error($code);
  324. }
  325. // Content-Type検証
  326. if (isset($head['headers']['content-type'])) {
  327. $conent_type = $head['headers']['content-type'];
  328. if (!preg_match('{^image/}', $conent_type) && $conent_type != 'application/x-shockwave-flash') {
  329. ic2_error('x02', "サポートされていないファイルタイプです。({$conent_type})");
  330. }
  331. }
  332. // Content-Length検証
  333. if (isset($head['headers']['content-length'])) {
  334. $conent_length = (int)$head['headers']['content-length'];
  335. $maxsize = $ini['Source']['maxsize'];
  336. if (preg_match('/(\d+\.?\d*)([KMG])/i', $maxsize, $m)) {
  337. $maxsize = p2_si2int($m[1], $m[2]);
  338. } else {
  339. $maxsize = (int)$maxsize;
  340. }
  341. if (0 < $maxsize && $maxsize < $conent_length) {
  342. ic2_error('x03', "ファイルサイズが大きすぎます。(file:{$conent_length}; max:{$maxsize};)");
  343. }
  344. }
  345. unset($client_h, $code, $head);
  346. } // HEAD省略 おわり
  347. // }}}
  348. // {{{ get
  349. // ダウンロード
  350. if ($ini['Getter']['retry_regex'] &&
  351. strlen(trim($ini['Getter']['retry_regex'])) > 0 &&
  352. intval($ini['Getter']['retry_max']) > 0 &&
  353. preg_match($ini['Getter']['retry_regex'], $uri))
  354. {
  355. do {
  356. $code = $client->get($uri);
  357. if ($code != 403) {
  358. break;
  359. }
  360. $retry++;
  361. sleep($ini['Getter']['retry_interval']);
  362. } while ($retry < intval($ini['Getter']['retry_max']));
  363. } else {
  364. $code = $client->get($uri);
  365. }
  366. if (PEAR::isError($code)) {
  367. ic2_error('x02', $code->getMessage());
  368. } elseif ($filepath && $force && $time && $code == 304) {
  369. // 304 Not Modified のとき
  370. ic2_finish($filepath, $thumb, $params, false);
  371. } elseif ($code != 200) {
  372. ic2_error($code);
  373. }
  374. $response = $client->currentResponse();
  375. if (isset($response['headers']['content-type'])) {
  376. $serv_mime = $response['headers']['content-type'];
  377. }
  378. // 一時ファイルに保存
  379. $tmpfile = tempnam($_conf['tmp_dir'], 'ic2_get_');
  380. $tmpobj = new IC2TempFile($tmpfile);
  381. $fp = fopen($tmpfile, 'wb');
  382. if (!$fp) {
  383. ic2_error('x02', "fopen失敗。($tmpfile)");
  384. }
  385. fwrite($fp, $response['body']);
  386. fclose($fp);
  387. // }}}
  388. // {{{ check
  389. // ウィルススキャン
  390. if ($ini['Getter']['virusscan']) {
  391. $searchpath = $thumbnailer->ini['Getter']['clamav'];
  392. if ($ini['Getter']['virusscan'] == 2) {
  393. $clamscan = 'clamdscan';
  394. } else {
  395. $clamscan = 'clamscan';
  396. }
  397. if ($clamscan = ic2_findexec($clamscan, $searchpath)) {
  398. $scan_command = $clamscan . ' --stdout ' . escapeshellarg(realpath($tmpfile)) . '; echo $?';
  399. $scan_result = @exec($scan_command, $scan_stdout, $scan_result);
  400. if ($scan_result == 1) {
  401. $params = array(
  402. 'uri' => $uri,
  403. 'host' => $pURL['host'],
  404. 'name' => basename($pURL['path']),
  405. 'size' => filesize($tmpfile),
  406. 'md5' => md5_file($tmpfile),
  407. 'width' => 0,
  408. 'height' => 0,
  409. 'mime' => 'clamscan/infected',
  410. 'memo' => $memo
  411. );
  412. ic2_aborn($params, true);
  413. ic2_error('x04', 'ウィルスを発見しました。');
  414. }
  415. }
  416. }
  417. // 画像情報を調べる。MIMEタイプはサーバが送ってきたものを信頼しない。
  418. $info = @getimagesize($tmpfile);
  419. if (!$info) {
  420. ic2_error('x02', '画像サイズの取得に失敗しました。');
  421. } elseif (!isset($info['mime'])) {
  422. // < PHP4.3.0
  423. ic2_error('x02', 'MIMEタイプの取得に失敗しました。');
  424. } else {
  425. $mime = $info['mime'];
  426. }
  427. if (!in_array($mime, array_keys($mimemap))) {
  428. ic2_error('x02', "サポートされていないファイルタイプです。({$mime})");
  429. }
  430. // 正規の画像なら、ファイルサイズとMD5チェックサムを計算
  431. $host = $pURL['host'];
  432. $name = basename($pURL['path']);
  433. $size = filesize($tmpfile);
  434. $md5 = md5_file($tmpfile);
  435. $width = $info[0];
  436. $height = $info[1];
  437. // 強制更新を試みたものの、更新されていなかったとき(レスポンスコードは200)
  438. if ($filepath && $force && $time && $size == $_size && $md5 == $_md5 && $mime == $_mime) {
  439. ic2_finish($filepath, $thumb, $params, false);
  440. }
  441. $params = compact('uri', 'host', 'name', 'size', 'md5', 'width', 'height', 'mime', 'memo');
  442. // ファイルサイズが上限を越えていないか確認
  443. ic2_checkSizeOvered($tmpfile, $params);
  444. // 同じ画像があぼーんされているか確認
  445. if (($check = ic2_checkAbornedFile($tmpfile, $params)) !== false) {
  446. $rank = $check;
  447. }
  448. // }}}
  449. // {{{ finish
  450. // すべてのチェックをパスしたなら、保存用の名前にリネームする
  451. $newfile = $thumbnailer->srcPath($size, $md5, $mime);
  452. $newdir = dirname($newfile);
  453. if (!is_dir($newdir) && !@mkdir($newdir)) {
  454. ic2_error('x02', "ディレクトリを作成できませんでした。({$newdir})");
  455. }
  456. if (($force || !file_exists($newfile)) && !@rename($tmpfile, $newfile)) {
  457. ic2_error('x02', "リネーム失敗。({$tmpfile} → {$newfile})");
  458. }
  459. @chmod($newfile, 0644);
  460. // データベースにファイル情報を記録する
  461. $record = new IC2_DataObject_Images;
  462. if ($retry && $size == $_size && $md5 == $_md5 && $mime == $_mime) {
  463. $record->time = time();
  464. if ($ini['General']['automemo'] && !is_null($memo)) {
  465. $record->memo = $memo;
  466. }
  467. $record->whereAddQuoted('uri', '=', $uri);
  468. $record->whereAddQuoted('size', '=', $size);
  469. $record->whereAddQuoted('md5', '=', $md5);
  470. $record->whereAddQuoted('mime', '=', $mime);
  471. $record->update();
  472. } else {
  473. $record->uri = $uri;
  474. $record->host = $host;
  475. $record->name = $name;
  476. $record->size = $size;
  477. $record->md5 = $md5;
  478. $record->width = $width;
  479. $record->height = $height;
  480. $record->mime = $mime;
  481. $record->time = time();
  482. $record->rank = $rank;
  483. if ($ini['General']['automemo'] && !is_null($memo)) {
  484. $record->memo = $memo;
  485. }
  486. $record->insert();
  487. }
  488. $is_anigif = false; $is_gif_caution == false;
  489. if (($ini['Thumbdeco']['anigif'] || $ini['Thumbdeco']['gifcaution']) && $params['mime'] == 'image/gif') {
  490. $is_anigif = check_anigif($newfile);
  491. if ($ini['Thumbdeco']['gifcaution']) {
  492. $is_gif_caution = $is_anigif && check_mime_caution($serv_mime, $mime, $mimemap, $name);
  493. }
  494. }
  495. // 画像を表示
  496. ic2_finish($newfile, $thumb, $params, $force, $ini['Thumbdeco']['anigif'] ? $is_anigif : false, $is_gif_caution);
  497. // }}}
  498. // {{{ 関数
  499. // {{{ ic2_aborn()
  500. function ic2_aborn($params, $infected = false)
  501. {
  502. global $ini;
  503. extract($params);
  504. $aborn = new IC2_DataObject_Images;
  505. $aborn->uri = $uri;
  506. $aborn->host = $host;
  507. $aborn->name = $name;
  508. $aborn->size = $size;
  509. $aborn->md5 = $md5;
  510. $aborn->width = $width;
  511. $aborn->height = $height;
  512. $aborn->mime = $mime;
  513. $aborn->time = time();
  514. $aborn->rank = $infected ? -4 : -1;
  515. if ($ini['General']['automemo'] && !is_null($memo)) {
  516. $aborn->memo = $memo;
  517. }
  518. return $aborn->insert();
  519. }
  520. // }}}
  521. // {{{ ic2_checkAbornedFile()
  522. function ic2_checkAbornedFile($tmpfile, $params)
  523. {
  524. global $ini;
  525. extract($params);
  526. // ブラックリスト検索
  527. $bl_check = new IC2_DataObject_BlackList;
  528. $bl_check->whereAddQuoted('size', '=', $size);
  529. $bl_check->whereAddQuoted('md5', '=', $md5);
  530. if ($bl_check->find(true)) {
  531. $bl_add = clone $bl_check;
  532. $bl_add->id = null;
  533. $bl_add->uri = $uri;
  534. switch ((int)$bl_check->type) {
  535. case 0:
  536. $errcode = 'x05'; // No More
  537. break;
  538. case 1:
  539. $errcode = 'x01'; // Aborn
  540. break;
  541. case 2:
  542. $errcode = 'x04'; // Virus
  543. break;
  544. default:
  545. $errcode = 'x06'; // Unknown
  546. }
  547. // 厳密には、その可能性が限りなく高いだけで100%ではない
  548. ic2_error($errcode, 'ブラックリストにある画像と同じ内容です。', false);
  549. }
  550. // あぼーん画像検索
  551. $check = new IC2_DataObject_Images;
  552. $check->whereAddQuoted('size', '=', $size);
  553. $check->whereAddQuoted('md5', '=', $md5);
  554. //$check->whereAddQuoted('mime', '=', $mime); // SizeとMD5で十分
  555. // 同じのが異なるURLで複数登録されていて、ランクが違う可能性があるので
  556. // (普通に使う分には起こらない...と思う。少なくとも起こりにくいはず)
  557. $check->orderByArray(array('rank' => 'ASC'));
  558. if ($check->find(true)) {
  559. if ($check->rank < 0) {
  560. ic2_aborn($params);
  561. // 現状では(たぶんずっと) -1 or -4 だけだが、一応
  562. if ($check->rank >= -5) {
  563. $errcode = 'x0' . abs($check->rank);
  564. } else {
  565. $errcode = 'x06'; // Unknown
  566. }
  567. // 厳密には、以下同文
  568. if ($check->rank == -4) {
  569. $errmsg = 'ウィルスに感染していた画像と同じ内容です。';
  570. } else {
  571. $errmsg = '既にあぼーんされている画像と同じ内容です。';
  572. }
  573. ic2_error($errcode, $errmsg);
  574. } else {
  575. return $check->rank;
  576. }
  577. }
  578. return false;
  579. }
  580. // }}}
  581. // {{{ ic2_checkSizeOvered()
  582. function ic2_checkSizeOvered($tmpfile, $params)
  583. {
  584. global $ini;
  585. extract($params);
  586. $isError = false;
  587. $maxsize = $ini['Source']['maxsize'];
  588. if (preg_match('/(\d+\.?\d*)([KMG])/i', $maxsize, $m)) {
  589. $maxsize = p2_si2int($m[1], $m[2]);
  590. } else {
  591. $maxsize = (int)$maxsize;
  592. }
  593. if (0 < $maxsize && $maxsize < $size) {
  594. $isError = true;
  595. $errmsg = "ファイルサイズが大きすぎます。(file:{$size}; max:{$maxsize};)";
  596. }
  597. $maxwidth = (int)$ini['Source']['maxwidth'] ;
  598. $maxheight = (int)$ini['Source']['maxheight'];
  599. if ((0 < $maxwidth && $maxwidth < $width) ||
  600. (0 < $maxheight && $maxheight < $height)
  601. ) {
  602. $isError = true;
  603. $errmsg = "画像サイズが大きすぎます。(file:{$width}x{$height}; max:{$maxwidth}x{$maxheight};)";
  604. }
  605. if ($isError) {
  606. ic2_aborn($params);
  607. ic2_error('x03', $errmsg);
  608. }
  609. return true;
  610. }
  611. // }}}
  612. // {{{ ic2_display()
  613. function ic2_display($path, $params)
  614. {
  615. global $_conf, $ini, $thumb, $redirect, $id, $uri, $file, $thumbnailer;
  616. if (P2_OS_WINDOWS) {
  617. $path = str_replace('\\', '/', $path);
  618. }
  619. if (strncmp($path, '/', 1) == 0) {
  620. $s = empty($_SERVER['HTTPS']) ? '' : 's';
  621. $to = 'http' . $s . '://' . $_SERVER['HTTP_HOST'] . $path;
  622. } else {
  623. $dir = dirname(P2Util::getMyUrl());
  624. if (strncasecmp($path, './', 2) == 0) {
  625. $to = $dir . substr($path, 1);
  626. } elseif (strncasecmp($path, '../', 3) == 0) {
  627. $to = dirname($dir) . substr($path, 2);
  628. } else {
  629. $to = $dir . '/' . $path;
  630. }
  631. }
  632. $name = basename($path);
  633. $ext = strrchr($name, '.');
  634. switch ($redirect) {
  635. case 1:
  636. header("Location: {$to}");
  637. exit;
  638. case 2:
  639. switch ($ext) {
  640. case '.jpg': header("Content-Type: image/jpeg; name=\"{$name}\""); break;
  641. case '.png': header("Content-Type: image/png; name=\"{$name}\""); break;
  642. case '.gif': header("Content-Type: image/gif; name=\"{$name}\""); break;
  643. default:
  644. if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false ||
  645. strpos($_SERVER['HTTP_USER_AGENT'], 'Opera') !== false
  646. ) {
  647. header("Content-Type: application/octetstream; name=\"{$name}\"");
  648. } else {
  649. header("Content-Type: application/octet-stream; name=\"{$name}\"");
  650. }
  651. }
  652. header("Content-Disposition: inline; filename=\"{$name}\"");
  653. header('Content-Length: ' . filesize($path));
  654. readfile($path);
  655. exit;
  656. default:
  657. if (!class_exists('HTML_Template_Flexy', false)) {
  658. require 'HTML/Template/Flexy.php';
  659. }
  660. if (!class_exists('HTML_QuickForm', false)) {
  661. require 'HTML/QuickForm.php';
  662. }
  663. if (!class_exists('HTML_QuickForm_Renderer_ObjectFlexy', false)) {
  664. require 'HTML/QuickForm/Renderer/ObjectFlexy.php';
  665. }
  666. if (isset($uri)) {
  667. $img_o = 'uri';
  668. $img_p = $uri;
  669. } elseif (isset($id)) {
  670. $img_o = 'id';
  671. $img_p = $id;
  672. } else {
  673. $img_o = 'file';
  674. $img_p = $file;
  675. }
  676. $img_q = $img_o . '=' . rawurlencode($img_p);
  677. // QuickFormの初期化
  678. $_size = explode('x', $thumbnailer->calc($params['width'], $params['height']));
  679. $_constants = array(
  680. 'o' => sprintf('原寸 (%dx%d)', $params['width'], $params['height']),
  681. 's' => '作成',
  682. 't' => $thumb,
  683. 'u' => $img_p,
  684. 'v' => $img_o,
  685. 'x' => $_size[0],
  686. 'y' => $_size[1],
  687. );
  688. $_defaults = array(
  689. 'q' => $ini["Thumb{$thumb}"]['quality'],
  690. 'r' => '0',
  691. );
  692. $mobile = Net_UserAgent_Mobile::singleton();
  693. $qa = 'size=3 maxlength=3';
  694. if ($mobile->isDoCoMo()) {
  695. $qa .= ' istyle=4';
  696. } elseif ($mobile->isEZweb()) {
  697. $qa .= ' format=*N';
  698. } elseif ($mobile->isSoftBank()) {
  699. $qa .= ' mode=numeric';
  700. }
  701. $_presets = array('' => 'サイズ・品質');
  702. foreach ($ini['Dynamic']['presets'] as $_preset_name => $_preset_params) {
  703. $_presets[$_preset_name] = $_preset_name;
  704. }
  705. $qf = new HTML_QuickForm('imgmaker', 'get', 'ic2_mkthumb.php');
  706. $qf->setConstants($_constants);
  707. $qf->setDefaults($_defaults);
  708. $qf->addElement('hidden', 't');
  709. $qf->addElement('hidden', 'u');
  710. $qf->addElement('hidden', 'v');
  711. $qf->addElement('text', 'x', '高さ', $qa);
  712. $qf->addElement('text', 'y', '横幅', $qa);
  713. $qf->addElement('text', 'q', '品質', $qa);
  714. $qf->addElement('select', 'p', 'プリセット', $_presets);
  715. $qf->addElement('select', 'r', '回転', array('0' => 'なし', '90' => '右に90°', '270' => '左に90°', '180' => '180°'));
  716. $qf->addElement('checkbox', 'w', 'トリム');
  717. $qf->addElement('checkbox', 'z', 'DL');
  718. $qf->addElement('submit', 's');
  719. $qf->addElement('submit', 'o');
  720. // FlexyとQurickForm_Rendererの初期化
  721. $_flexy_options = array(
  722. 'locale' => 'ja',
  723. 'charset' => 'cp932',
  724. 'compileDir' => $_conf['compile_dir'] . DIRECTORY_SEPARATOR . 'ic2',
  725. 'templateDir' => P2EX_LIB_DIR . '/ic2/templates',
  726. 'numberFormat' => '', // ",0,'.',','" と等価
  727. );
  728. $flexy = new HTML_Template_Flexy($_flexy_options);
  729. $rdr = new HTML_QuickForm_Renderer_ObjectFlexy($flexy);
  730. $qf->accept($rdr);
  731. // 表示
  732. $flexy->setData('p2vid', P2_VERSION_ID);
  733. $flexy->setData('title', 'IC2::Cached');
  734. $flexy->setData('pc', !$_conf['ktai']);
  735. $flexy->setData('iphone', $_conf['iphone']);
  736. if (!$_conf['ktai']) {
  737. $flexy->setData('skin', $GLOBALS['skin_name']);
  738. //$flexy->setData('stylesheets', array('css'));
  739. //$flexy->setData('javascripts', array('js'));
  740. } else {
  741. $flexy->setData('k_color', array(
  742. 'c_bgcolor' => !empty($_conf['mobile.background_color']) ? $_conf['mobile.background_color'] : '#ffffff',
  743. 'c_text' => !empty($_conf['mobile.text_color']) ? $_conf['mobile.text_color'] : '#000000',
  744. 'c_link' => !empty($_conf['mobile.link_color']) ? $_conf['mobile.link_color'] : '#0000ff',
  745. 'c_vlink' => !empty($_conf['mobile.vlink_color']) ? $_conf['mobile.vlink_color'] : '#9900ff',
  746. ));
  747. }
  748. $rank = isset($params['rank']) ? $params['rank'] : 0;
  749. if ($_conf['iphone']) {
  750. $img_dir = 'img/iphone/';
  751. $img_ext = '.png';
  752. } else {
  753. $img_dir = 'img/';
  754. $img_ext = $_conf['ktai'] ? '.gif' : '.png';
  755. }
  756. $stars = array();
  757. $stars[-1] = $img_dir . (($rank == -1) ? 'sn1' : 'sn0') . $img_ext;
  758. //$stars[0] = $img_dir . (($rank == 0) ? 'sz1' : 'sz0') . $img_ext;
  759. $stars[0] = $img_dir . ($_conf['iphone'] ? 'sz0' : 'sz1') . $img_ext;
  760. for ($i = 1; $i <= 5; $i++) {
  761. $stars[$i] = $img_dir . (($rank >= $i) ? 's1' : 's0') . $img_ext;
  762. }
  763. $k_at_a = str_replace('&amp;', '&', $_conf['k_at_a']);
  764. $setrank_url = "ic2.php?{$img_q}&t={$thumb}&r=0{$k_at_a}";
  765. $flexy->setData('stars', $stars);
  766. $flexy->setData('params', $params);
  767. if ($thumb == 2 && $rank >= 0) {
  768. if ($ini['General']['inline'] == 1) {
  769. $t = 2;
  770. $link = null;
  771. } else {
  772. $t = 1;
  773. $link = $path;
  774. }
  775. $r = ($ini['General']['redirect'] == 1) ? 1 : 2;
  776. $preview = "{$_SERVER['SCRIPT_NAME']}?o=1&r={$r}&t={$t}&{$img_q}{$k_at_a}";
  777. $flexy->setData('preview', $preview);
  778. $flexy->setData('link', $link);
  779. $flexy->setData('info', null);
  780. } else {
  781. $flexy->setData('preview', null);
  782. $flexy->setData('link', $path);
  783. $flexy->setData('info', null);
  784. }
  785. if (!$_conf['ktai'] || $_conf['iphone']) {
  786. $flexy->setData('backto', null);
  787. } elseif (isset($_REQUEST['from'])) {
  788. $flexy->setData('backto', $_REQUEST['from']);
  789. $setrank_url .= '&from=' . rawurlencode($_REQUEST['from']);
  790. } elseif (isset($_SERVER['HTTP_REFERER'])) {
  791. $flexy->setData('backto', $_SERVER['HTTP_REFERER']);
  792. } else {
  793. $flexy->setData('backto', null);
  794. }
  795. $flexy->setData('stars', $stars);
  796. $flexy->setData('sertank', $setrank_url . '&rank=');
  797. if ($_conf['iphone']) {
  798. $_conf['extra_headers_ht'] .= <<<EOP
  799. <link rel="stylesheet" type="text/css" href="css/ic2_iphone.css?{$_conf['p2_version_id']}">
  800. EOP;
  801. $_conf['extra_headers_xht'] .= <<<EOP
  802. <link rel="stylesheet" type="text/css" href="css/ic2_iphone.css?{$_conf['p2_version_id']}" />
  803. EOP;
  804. }
  805. $flexy->setData('edit', (extension_loaded('gd') && $rank >= 0));
  806. $flexy->setData('form', $rdr->toObject());
  807. $flexy->setData('doctype', $_conf['doctype']);
  808. $flexy->setData('extra_headers', $_conf['extra_headers_ht']);
  809. $flexy->setData('extra_headers_x', $_conf['extra_headers_xht']);
  810. $flexy->compile('preview.tpl.html');
  811. P2Util::header_nocache();
  812. $flexy->output();
  813. }
  814. exit;
  815. }
  816. // }}}
  817. // {{{ ic2_error()
  818. function ic2_error($code, $optmsg = '', $write_log = true)
  819. {
  820. global $_conf, $id, $uri, $file, $redirect;
  821. $map = array(
  822. 100 => 'Continue',
  823. 101 => 'Switching Protocols',
  824. 200 => 'OK',
  825. 201 => 'Created',
  826. 202 => 'Accepted',
  827. 203 => 'Non-Authoritative Information',
  828. 204 => 'No Content',
  829. 205 => 'Reset Content',
  830. 206 => 'Partial Content',
  831. 300 => 'Multiple Choices',
  832. 301 => 'Moved Permanently',
  833. 302 => 'Found',
  834. 303 => 'See Other',
  835. 304 => 'Not Modified',
  836. 305 => 'Use Proxy',
  837. 306 => '(Unused)',
  838. 307 => 'Temporary Redirect',
  839. 400 => 'Bad Request',
  840. 401 => 'Unauthorized',
  841. 402 => 'Payment Required',
  842. 403 => 'Forbidden',
  843. 404 => 'Not Found',
  844. 405 => 'Method Not Allowed',
  845. 406 => 'Not Acceptable',
  846. 407 => 'Proxy Authentication Required',
  847. 408 => 'Request Timeout',
  848. 409 => 'Conflict',
  849. 410 => 'Gone',
  850. 411 => 'Length Required',
  851. 412 => 'Precondition Failed',
  852. 413 => 'Request Entity Too Large',
  853. 414 => 'Request-URI Too Long',
  854. 415 => 'Unsupported Media Type',
  855. 416 => 'Requested Range Not Satisfiable',
  856. 417 => 'Expectation Failed',
  857. 500 => 'Internal Server Error',
  858. 501 => 'Not Implemented',
  859. 502 => 'Bad Gateway',
  860. 503 => 'Service Unavailable',
  861. 504 => 'Gateway Timeout',
  862. 505 => 'HTTP Version Not Supported',
  863. 'x01' => 'IC2 - Aborned Image',
  864. 'x02' => 'IC2 - Broken (or Not) Image',
  865. 'x03' => 'IC2 - Too Large',
  866. 'x04' => 'IC2 - Virus Infected',
  867. 'x05' => 'IC2 - No More',
  868. 'x06' => 'IC2 - ???',
  869. );
  870. $message = $code . ' ' . $map[$code];
  871. if ($optmsg) {
  872. $message .= '<br />' . $optmsg;
  873. }
  874. if ($write_log) {
  875. $logger = new IC2_DataObject_Errors;
  876. $logger->uri = isset($uri) ? $uri : (isset($id) ? $id : $file);
  877. $logger->errcode = $code;
  878. $logger->errmsg = mb_convert_encoding($message, 'UTF-8', 'CP932');
  879. $logger->occured = time();
  880. $logger->insert();
  881. $logger->ic2_errlog_lotate();
  882. }
  883. /*if (isset($map[$code]) && 100 <= $code && $code <= 505) {
  884. header("HTTP/1.0 {$code} {$map[$code]}");
  885. }*/
  886. if ($redirect) {
  887. if ($_conf['ktai'] && !$_conf['iphone']) {
  888. $type = 'gif';
  889. } else {
  890. $type = 'png';
  891. }
  892. $img = strval($code) . '.' . $type;
  893. $path = './img/' . $img;
  894. $name = 'filename="' . $img . '"';
  895. header('Content-Type: image/' . $type . '; ' . $name);
  896. header('Content-Disposition: inline; ' . $name);
  897. readfile($path);
  898. exit;
  899. }
  900. echo <<<EOF
  901. <html>
  902. <head><title>ImageCache::Error</title></head>
  903. <body>
  904. <p>{$message}</p>
  905. </body>
  906. </html>
  907. EOF;
  908. exit;
  909. }
  910. // }}}
  911. // {{{ ic2_finish()
  912. function ic2_finish($filepath, $thumb, $params, $force, $anigif = false, $gif_caution = false)
  913. {
  914. global $thumbnailer;
  915. extract($params);
  916. if ($thumb == 0) {
  917. ic2_display($filepath, $params);
  918. } else {
  919. $thumbpath = $thumbnailer->convert($size, $md5, $mime, $width, $height, $force, $anigif, $gif_caution);
  920. if (PEAR::isError($thumbpath)) {
  921. ic2_error('x02', $thumbpath->getMessage());
  922. }
  923. ic2_display($thumbpath, $params);
  924. }
  925. }
  926. // }}}
  927. // {{{ check_anigif()
  928. function check_anigif($path) {
  929. require_once P2EX_LIB_DIR . '/ic2/GifAnimationDetector.php';
  930. return GifAnimationDetector::isAnimated($path);
  931. }
  932. // }}}
  933. // {{{ check_mime_caution()
  934. function check_mime_caution($serv_mime, $file_mime, $mimemap, $name) {
  935. // サーバーの送ってきたcontent-typeが実際のファイルと異なっているか
  936. if (strlen($serv_mime) && strtolower($serv_mime) != strtolower($file_mime)) return true;
  937. // 念のため拡張子でもチェック
  938. $ext = strrchr($name, '.');
  939. if (strlen($ext) && $ext_mime = array_search($ext, $mimemap)) {
  940. if (strtolower($ext_mime) != strtolower($file_mime)) return true;
  941. }
  942. return false;
  943. }
  944. // }}}
  945. // }}}
  946. /*
  947. * Local Variables:
  948. * mode: php
  949. * coding: cp932
  950. * tab-width: 4
  951. * c-basic-offset: 4
  952. * indent-tabs-mode: nil
  953. * End:
  954. */
  955. // vim: set syn=php fenc=cp932 ai et ts=4 sw=4 sts=4 fdm=marker: