OSDN Git Service

BugTrack/2283 ls2 plugin: Improve include loop handling
[pukiwiki/pukiwiki.git] / plugin / ref.inc.php
1 <?php
2 // PukiWiki - Yet another WikiWikiWeb clone
3 // ref.inc.php
4 // Copyright
5 //   2002-2017 PukiWiki Development Team
6 //   2001-2002 Originally written by yu-ji
7 // License: GPL v2 or (at your option) any later version
8 //
9 // Image refernce plugin
10 // Include an attached image-file as an inline-image
11
12 // File icon image
13 if (! defined('FILE_ICON'))
14         define('FILE_ICON',
15         '<img src="' . IMAGE_DIR . 'file.png" width="20" height="20"' .
16         ' alt="file" style="border-width:0px" />');
17
18 /////////////////////////////////////////////////
19 // Default settings
20
21 // Horizontal alignment
22 define('PLUGIN_REF_DEFAULT_ALIGN', 'left'); // 'left', 'center', 'right'
23
24 // Text wrapping
25 define('PLUGIN_REF_WRAP_TABLE', FALSE); // TRUE, FALSE
26
27 // URL指定時に画像サイズを取得するか
28 define('PLUGIN_REF_URL_GET_IMAGE_SIZE', FALSE); // FALSE, TRUE
29
30 // UPLOAD_DIR のデータ(画像ファイルのみ)に直接アクセスさせる
31 define('PLUGIN_REF_DIRECT_ACCESS', FALSE); // FALSE or TRUE
32 // - これは従来のインラインイメージ処理を互換のために残すもので
33 //   あり、高速化のためのオプションではありません
34 // - UPLOAD_DIR をWebサーバー上に露出させており、かつ直接アクセス
35 //   できる(アクセス制限がない)状態である必要があります
36 // - Apache などでは UPLOAD_DIR/.htaccess を削除する必要があります
37 // - ブラウザによってはインラインイメージの表示や、「インライン
38 //   イメージだけを表示」させた時などに不具合が出る場合があります
39
40 /////////////////////////////////////////////////
41
42 // Image suffixes allowed
43 define('PLUGIN_REF_IMAGE', '/\.(gif|png|jpe?g|swf)$/i');
44
45 // Usage (a part of)
46 define('PLUGIN_REF_USAGE', "([pagename/]attached-file-name[,parameters, ... ][,title])");
47
48 function plugin_ref_inline()
49 {
50         // Not reached, because of "$aryargs[] = & $body" at plugin.php
51         // if (! func_num_args())
52         //      return '&amp;ref(): Usage:' . PLUGIN_REF_USAGE . ';';
53
54         $params = plugin_ref_body(func_get_args());
55
56         if (isset($params['_error']) && $params['_error'] != '') {
57                 // Error
58                 return '&amp;ref(): ' . $params['_error'] . ';';
59         } else {
60                 return $params['_body'];
61         }
62 }
63
64 function plugin_ref_convert()
65 {
66         if (! func_num_args())
67                 return '<p>#ref(): Usage:' . PLUGIN_REF_USAGE . "</p>\n";
68
69         $params = plugin_ref_body(func_get_args());
70
71         if (isset($params['_error']) && $params['_error'] != '') {
72                 return "<p>#ref(): {$params['_error']}</p>\n";
73         }
74
75         if ((PLUGIN_REF_WRAP_TABLE && ! $params['nowrap']) || $params['wrap']) {
76                 // 枠で包む
77                 // margin:auto
78                 //      Mozilla 1.x  = x (wrap,aroundが効かない)
79                 //      Opera 6      = o
80                 //      Netscape 6   = x (wrap,aroundが効かない)
81                 //      IE 6         = x (wrap,aroundが効かない)
82                 // margin:0px
83                 //      Mozilla 1.x  = x (wrapで寄せが効かない)
84                 //      Opera 6      = x (wrapで寄せが効かない)
85                 //      Netscape 6   = x (wrapで寄せが効かない)
86                 //      IE6          = o
87                 $margin = ($params['around'] ? '0px' : 'auto');
88                 $margin_align = ($params['_align'] == 'center') ? '' : ";margin-{$params['_align']}:0px";
89                 $params['_body'] = <<<EOD
90 <table class="style_table" style="margin:$margin$margin_align">
91  <tr>
92   <td class="style_td">{$params['_body']}</td>
93  </tr>
94 </table>
95 EOD;
96         }
97
98         if ($params['around']) {
99                 $style = ($params['_align'] == 'right') ? 'float:right' : 'float:left';
100         } else {
101                 $style = "text-align:{$params['_align']}";
102         }
103
104         // divで包む
105         return "<div class=\"img_margin\" style=\"$style\">{$params['_body']}</div>\n";
106 }
107
108 function plugin_ref_body($args)
109 {
110         global $vars;
111         global $WikiName, $BracketName; // compat
112
113         $script = get_base_uri();
114         // 戻り値
115         $params = array(
116                 'left'   => FALSE, // 左寄せ
117                 'center' => FALSE, // 中央寄せ
118                 'right'  => FALSE, // 右寄せ
119                 'wrap'   => FALSE, // TABLEで囲む
120                 'nowrap' => FALSE, // TABLEで囲まない
121                 'around' => FALSE, // 回り込み
122                 'noicon' => FALSE, // アイコンを表示しない
123                 'nolink' => FALSE, // 元ファイルへのリンクを張らない
124                 'noimg'  => FALSE, // 画像を展開しない
125                 'zoom'   => FALSE, // 縦横比を保持する
126                 '_size'  => FALSE, // サイズ指定あり
127                 '_w'     => 0,       // 幅
128                 '_h'     => 0,       // 高さ
129                 '_%'     => 0,     // 拡大率
130                 '_args'  => array(),
131                 '_done'  => FALSE,
132                 '_error' => ''
133         );
134
135         // 添付ファイルのあるページ: defaultは現在のページ名
136         $page = isset($vars['page']) ? $vars['page'] : '';
137
138         // 添付ファイルのファイル名
139         $name = '';
140
141         // 添付ファイルまでのパスおよび(実際の)ファイル名
142         $file = '';
143
144         // 第一引数: "[ページ名および/]添付ファイル名"、あるいは"URL"を取得
145         $name = array_shift($args);
146         $is_url = is_url($name);
147
148         if(! $is_url) {
149                 // 添付ファイル
150                 if (! is_dir(UPLOAD_DIR)) {
151                         $params['_error'] = 'No UPLOAD_DIR';
152                         return $params;
153                 }
154
155                 $matches = array();
156                 // ファイル名にページ名(ページ参照パス)が合成されているか
157                 //   (Page_name/maybe-separated-with/slashes/ATTACHED_FILENAME)
158                 if (preg_match('#^(.+)/([^/]+)$#', $name, $matches)) {
159                         if ($matches[1] == '.' || $matches[1] == '..') {
160                                 $matches[1] .= '/'; // Restore relative paths
161                         }
162                         $name = $matches[2];
163                         $page = get_fullname(strip_bracket($matches[1]), $page); // strip is a compat
164                         $file = UPLOAD_DIR . encode($page) . '_' . encode($name);
165                         $is_file = is_file($file);
166
167                 // 第二引数以降が存在し、それはrefのオプション名称などと一致しない
168                 } else if (isset($args[0]) && $args[0] != '' && ! isset($params[$args[0]])) {
169                         $e_name = encode($name);
170
171                         // Try the second argument, as a page-name or a path-name
172                         $_arg = get_fullname(strip_bracket($args[0]), $page); // strip is a compat
173                         $file = UPLOAD_DIR .  encode($_arg) . '_' . $e_name;
174                         $is_file_second = is_file($file);
175
176                         // If the second argument is WikiName, or double-bracket-inserted pagename (compat)
177                         $is_bracket_bracket = preg_match("/^($WikiName|\[\[$BracketName\]\])$/", $args[0]);
178
179                         if ($is_file_second && $is_bracket_bracket) {
180                                 // Believe the second argument (compat)
181                                 array_shift($args);
182                                 $page = $_arg;
183                                 $is_file = TRUE;
184                         } else {
185                                 // Try default page, with default params
186                                 $is_file_default = is_file(UPLOAD_DIR . encode($page) . '_' . $e_name);
187
188                                 // Promote new design
189                                 if ($is_file_default && $is_file_second) {
190                                         // Because of race condition NOW
191                                         $params['_error'] = htmlsc('The same file name "' .
192                                                 $name . '" at both page: "' .  $page . '" and "' .  $_arg .
193                                                 '". Try ref(pagename/filename) to specify one of them');
194                                 } else {
195                                         // Because of possibility of race condition, in the future
196                                         $params['_error'] = 'The style ref(filename,pagename) is ambiguous ' .
197                                                 'and become obsolete. ' .
198                                                 'Please try ref(pagename/filename)';
199                                 }
200                                 return $params;
201                         }
202                 } else {
203                         // Simple single argument
204                         $file = UPLOAD_DIR . encode($page) . '_' . encode($name);
205                         $is_file = is_file($file);
206                 }
207                 if (! $is_file) {
208                         $params['_error'] = htmlsc('File not found: "' .
209                                 $name . '" at page "' . $page . '"');
210                         return $params;
211                 }
212         }
213
214         // 残りの引数の処理
215         if (! empty($args))
216                 foreach ($args as $arg)
217                         ref_check_arg($arg, $params);
218
219 /*
220  $nameをもとに以下の変数を設定
221  $url,$url2 : URL
222  $title :タイトル
223  $is_image : 画像のときTRUE
224  $info : 画像ファイルのときgetimagesize()の'size'
225          画像ファイル以外のファイルの情報
226          添付ファイルのとき : ファイルの最終更新日とサイズ
227          URLのとき : URLそのもの
228 */
229         $title = $url = $url2 = $info = '';
230         $width = $height = 0;
231         $matches = array();
232
233         if ($is_url) {  // URL
234                 if (PKWK_DISABLE_INLINE_IMAGE_FROM_URI) {
235                         //$params['_error'] = 'PKWK_DISABLE_INLINE_IMAGE_FROM_URI prohibits this';
236                         //return $params;
237                         $url = htmlsc($name);
238                         $params['_body'] = '<a href="' . $url . '">' . $url . '</a>';
239                         return $params;
240                 }
241
242                 $url = $url2 = htmlsc($name);
243                 $title = htmlsc(preg_match('/([^\/]+)$/', $name, $matches) ? $matches[1] : $url);
244
245                 $is_image = (! $params['noimg'] && preg_match(PLUGIN_REF_IMAGE, $name));
246
247                 if ($is_image && PLUGIN_REF_URL_GET_IMAGE_SIZE && (bool)ini_get('allow_url_fopen')) {
248                         $size = @getimagesize($name);
249                         if (is_array($size)) {
250                                 $width  = $size[0];
251                                 $height = $size[1];
252                                 $info   = $size[3];
253                         }
254                 }
255
256         } else { // 添付ファイル
257
258                 $title = htmlsc($name);
259
260                 $is_image = (! $params['noimg'] && preg_match(PLUGIN_REF_IMAGE, $name));
261
262                 // Count downloads with attach plugin
263                 $url = $script . '?plugin=attach' . '&amp;refer=' . rawurlencode($page) .
264                         '&amp;openfile=' . rawurlencode($name); // Show its filename at the last
265
266                 if ($is_image) {
267                         // Swap $url
268                         $url2 = $url;
269
270                         // URI for in-line image output
271                         if (! PLUGIN_REF_DIRECT_ACCESS) {
272                                 // With ref plugin (faster than attach)
273                                 $url = $script . '?plugin=ref' . '&amp;page=' . rawurlencode($page) .
274                                         '&amp;src=' . rawurlencode($name); // Show its filename at the last
275                         } else {
276                                 // Try direct-access, if possible
277                                 $url = $file;
278                         }
279
280                         $width = $height = 0;
281                         $size = @getimagesize($file);
282                         if (is_array($size)) {
283                                 $width  = $size[0];
284                                 $height = $size[1];
285                         }
286                 } else {
287                         $info = get_date('Y/m/d H:i:s', filemtime($file) - LOCALZONE) .
288                                 ' ' . sprintf('%01.1f', round(filesize($file)/1024, 1)) . 'KB';
289                 }
290         }
291
292         // 拡張パラメータをチェック
293         if (! empty($params['_args'])) {
294                 $_title = array();
295                 foreach ($params['_args'] as $arg) {
296                         if (preg_match('/^([0-9]+)x([0-9]+)$/', $arg, $matches)) {
297                                 $params['_size'] = TRUE;
298                                 $params['_w'] = $matches[1];
299                                 $params['_h'] = $matches[2];
300
301                         } else if (preg_match('/^([0-9.]+)%$/', $arg, $matches) && $matches[1] > 0) {
302                                 $params['_%'] = $matches[1];
303
304                         } else {
305                                 $_title[] = $arg;
306                         }
307                 }
308
309                 if (! empty($_title)) {
310                         $title = htmlsc(join(',', $_title));
311                         if ($is_image) $title = make_line_rules($title);
312                 }
313         }
314
315         // 画像サイズ調整
316         if ($is_image) {
317                 // 指定されたサイズを使用する
318                 if ($params['_size']) {
319                         if ($width == 0 && $height == 0) {
320                                 $width  = $params['_w'];
321                                 $height = $params['_h'];
322                         } else if ($params['zoom']) {
323                                 $_w = $params['_w'] ? $width  / $params['_w'] : 0;
324                                 $_h = $params['_h'] ? $height / $params['_h'] : 0;
325                                 $zoom = max($_w, $_h);
326                                 if ($zoom) {
327                                         $width  = (int)($width  / $zoom);
328                                         $height = (int)($height / $zoom);
329                                 }
330                         } else {
331                                 $width  = $params['_w'] ? $params['_w'] : $width;
332                                 $height = $params['_h'] ? $params['_h'] : $height;
333                         }
334                 }
335                 if ($params['_%']) {
336                         $width  = (int)($width  * $params['_%'] / 100);
337                         $height = (int)($height * $params['_%'] / 100);
338                 }
339                 if ($width && $height) $info = "width=\"$width\" height=\"$height\" ";
340         }
341
342         // アラインメント判定
343         $params['_align'] = PLUGIN_REF_DEFAULT_ALIGN;
344         foreach (array('right', 'left', 'center') as $align) {
345                 if ($params[$align])  {
346                         $params['_align'] = $align;
347                         break;
348                 }
349         }
350
351         if ($is_image) { // 画像
352                 $params['_body'] = "<img src=\"$url\" alt=\"$title\" title=\"$title\" $info/>";
353                 if (! $params['nolink'] && $url2)
354                         $params['_body'] = "<a href=\"$url2\" title=\"$title\">{$params['_body']}</a>";
355         } else {
356                 $icon = $params['noicon'] ? '' : FILE_ICON;
357                 $params['_body'] = "<a href=\"$url\" title=\"$info\">$icon$title</a>";
358         }
359
360         return $params;
361 }
362
363 // オプションを解析する
364 function ref_check_arg($val, & $params)
365 {
366         if ($val == '') {
367                 $params['_done'] = TRUE;
368                 return;
369         }
370
371         if (! $params['_done']) {
372                 foreach (array_keys($params) as $key) {
373                         if (strpos($key, strtolower($val)) === 0) {
374                                 $params[$key] = TRUE;
375                                 return;
376                         }
377                 }
378                 $params['_done'] = TRUE;
379         }
380
381         $params['_args'][] = $val;
382 }
383
384 // Output an image (fast, non-logging <==> attach plugin)
385 function plugin_ref_action()
386 {
387         global $vars;
388
389         $usage = 'Usage: plugin=ref&amp;page=page_name&amp;src=attached_image_name';
390
391         if (! isset($vars['page']) || ! isset($vars['src']))
392                 return array('msg'=>'Invalid argument', 'body'=>$usage);
393
394         $page     = $vars['page'];
395         $filename = $vars['src'] ;
396
397         $ref = UPLOAD_DIR . encode($page) . '_' . encode(preg_replace('#^.*/#', '', $filename));
398         if(! file_exists($ref))
399                 return array('msg'=>'Attach file not found', 'body'=>$usage);
400
401         $is_image = preg_match(PLUGIN_REF_IMAGE, $filename);
402         if (!$is_image) {
403                 return array('msg'=>'Seems not an image', 'body'=>$usage);
404         }
405         $got = @getimagesize($ref);
406         if (! isset($got[2])) $got[2] = FALSE;
407         switch ($got[2]) {
408         case 1: $type = 'image/gif' ; break;
409         case 2: $type = 'image/jpeg'; break;
410         case 3: $type = 'image/png' ; break;
411         case 4: $type = 'application/x-shockwave-flash'; break;
412         default:
413                 return array('msg'=>'Seems not an image', 'body'=>$usage);
414         }
415
416         // Care for Japanese-character-included file name
417         $legacy_filename = mb_convert_encoding($filename, 'UTF-8', SOURCE_ENCODING);
418         if (LANG == 'ja') {
419                 switch(UA_NAME . '/' . UA_PROFILE){
420                 case 'MSIE/default':
421                         $legacy_filename = mb_convert_encoding($filename, 'SJIS', SOURCE_ENCODING);
422                         break;
423                 }
424         }
425         $utf8filename = mb_convert_encoding($filename, 'UTF-8', SOURCE_ENCODING);
426         $size = filesize($ref);
427
428         // Output
429         pkwk_common_headers();
430         header('Content-Disposition: inline; filename="' . $legacy_filename
431                 .'"; filename*=utf-8\'\'' . rawurlencode($utf8filename));
432         header('Content-Length: ' . $size);
433         header('Content-Type: '   . $type);
434         // Disable output bufferring
435         while (ob_get_level()) {
436                 ob_end_flush();
437         }
438         flush();
439         @readfile($ref);
440         exit;
441 }