2 /////////////////////////////////////////////////
3 // PukiWiki - Yet another WikiWikiWeb clone.
5 // $Id: attach.inc.php,v 1.60 2004/08/15 00:19:35 henoheno Exp $
11 changed by Y.MASUI <masui@hisec.co.jp> http://masui.net/pukiwiki/
12 modified by PANDA <panda@arino.jp> http://home.arino.jp/
15 // Max file size for upload on PHP (PHP default: 2MB)
16 ini_set('upload_max_filesize', '2M');
18 // Max file size for upload on script of PukiWikiX_FILESIZE
19 define('MAX_FILESIZE', (1024 * 1024)); // default: 1MB
21 // ´ÉÍý¼Ô¤À¤±¤¬ÅºÉÕ¥Õ¥¡¥¤¥ë¤ò¥¢¥Ã¥×¥í¡¼¥É¤Ç¤¤ë¤è¤¦¤Ë¤¹¤ë
22 define('ATTACH_UPLOAD_ADMIN_ONLY', FALSE); // FALSE or TRUE
24 // ´ÉÍý¼Ô¤À¤±¤¬ÅºÉÕ¥Õ¥¡¥¤¥ë¤òºï½ü¤Ç¤¤ë¤è¤¦¤Ë¤¹¤ë
25 define('ATTACH_DELETE_ADMIN_ONLY', FALSE); // FALSE or TRUE
27 // ´ÉÍý¼Ô¤¬ÅºÉÕ¥Õ¥¡¥¤¥ë¤òºï½ü¤¹¤ë¤È¤¤Ï¡¢¥Ð¥Ã¥¯¥¢¥Ã¥×¤òºî¤é¤Ê¤¤
28 // ATTACH_DELETE_ADMIN_ONLY=TRUE¤Î¤È¤Í¸ú
29 define('ATTACH_DELETE_ADMIN_NOBACKUP', FALSE); // FALSE or TRUE
31 // ¥¢¥Ã¥×¥í¡¼¥É/ºï½ü»þ¤Ë¥Ñ¥¹¥ï¡¼¥É¤òÍ׵᤹¤ë(ADMIN_ONLY¤¬Í¥Àè)
32 define('ATTACH_PASSWORD_REQUIRE', FALSE); // FALSE or TRUE
34 // ¥Õ¥¡¥¤¥ë¤Î¥¢¥¯¥»¥¹¸¢
35 define('ATTACH_FILE_MODE', 0644);
36 //define('ATTACH_FILE_MODE', 0604); // for XREA.COM
39 if (! defined('FILE_ICON')) {
40 define('FILE_ICON', '<img src="' . IMAGE_DIR . 'file.png"' .
41 ' width="20" height="20" alt="file"' .
42 ' style="border-width:0px" />');
45 // mime-type¤òµ½Ò¤·¤¿¥Ú¡¼¥¸
46 define('ATTACH_CONFIG_PAGE_MIME', 'plugin/attach/mime-type');
49 function plugin_attach_convert()
53 $page = isset($vars['page']) ? $vars['page'] : '';
55 $nolist = $noform = FALSE;
56 if (func_num_args() > 0) {
57 foreach (func_get_args() as $arg) {
58 $arg = strtolower($arg);
59 $nolist |= ($arg == 'nolist');
60 $noform |= ($arg == 'noform');
66 $obj = & new AttachPages($page);
67 $ret .= $obj->toString($page, TRUE);
70 $ret .= attach_form($page);
77 function plugin_attach_action()
79 global $vars, $_attach_messages;
81 // Backward compatible
82 if (isset($vars['openfile'])) {
83 $vars['file'] = $vars['openfile'];
84 $vars['pcmd'] = 'open';
86 if (isset($vars['delfile'])) {
87 $vars['file'] = $vars['delfile'];
88 $vars['pcmd'] = 'delete';
91 $pcmd = isset($vars['pcmd']) ? $vars['pcmd'] : '';
92 $refer = isset($vars['refer']) ? $vars['refer'] : '';
93 $pass = isset($vars['pass']) ? $vars['pass'] : NULL;
94 $page = isset($vars['page']) ? $vars['page'] : '';
96 if ($refer != '' && is_pagename($refer)) {
97 if(in_array($pcmd, array('info', 'open', 'list'))) {
98 check_readable($refer);
100 check_editable($refer);
105 if (isset($_FILES['attach_file'])) {
107 return attach_upload($_FILES['attach_file'], $refer, $pass);
110 case 'info' : return attach_info();
111 case 'delete' : return attach_delete();
112 case 'open' : return attach_open();
113 case 'list' : return attach_list();
114 case 'freeze' : return attach_freeze(TRUE);
115 case 'unfreeze' : return attach_freeze(FALSE);
116 case 'upload' : return attach_showform();
118 if ($page == '' || ! is_page($page)) {
119 return attach_list();
121 return attach_showform();
125 //-------- call from skin
126 function attach_filelist()
128 global $vars, $_attach_messages;
130 $page = isset($vars['page']) ? $vars['page'] : '';
132 $obj = & new AttachPages($page, 0);
134 if (! isset($obj->pages[$page])) {
137 return $_attach_messages['msg_file'] . ': ' .
138 $obj->toString($page, TRUE) . "\n";
143 // ¥Õ¥¡¥¤¥ë¥¢¥Ã¥×¥í¡¼¥É
144 // $pass = NULL : ¥Ñ¥¹¥ï¡¼¥É¤¬»ØÄꤵ¤ì¤Æ¤¤¤Ê¤¤
145 // $pass = TRUE : ¥¢¥Ã¥×¥í¡¼¥Éµö²Ä
146 function attach_upload($file, $page, $pass = NULL)
148 global $_attach_messages;
150 if (! is_page($page)) {
151 die_message("No such page");
152 } else if ($file['tmp_name'] == '' || ! is_uploaded_file($file['tmp_name'])) {
153 return array('result'=>FALSE);
154 } else if ($file['size'] > MAX_FILESIZE) {
157 'msg'=>$_attach_messages['err_exceed']);
158 } else if (! is_pagename($page) || ($pass !== TRUE && ! is_editable($page))) {
161 msg'=>$_attach_messages['err_noparm']);
162 } else if (ATTACH_UPLOAD_ADMIN_ONLY && $pass !== TRUE &&
163 ($pass === NULL || ! pkwk_login($pass))) {
166 'msg'=>$_attach_messages['err_adminpass']);
169 $obj = & new AttachFile($page, $file['name']);
171 return array('result'=>FALSE,
172 'msg'=>$_attach_messages['err_exists']);
175 if (move_uploaded_file($file['tmp_name'], $obj->filename)) {
176 chmod($obj->filename, ATTACH_FILE_MODE);
179 if (is_page($page)) {
180 touch(get_filename($page));
184 $obj->status['pass'] = ($pass !== TRUE && $pass !== NULL) ? md5($pass) : '';
189 'msg'=>$_attach_messages['msg_uploaded']);
192 // ¾ÜºÙ¥Õ¥©¡¼¥à¤òɽ¼¨
193 function attach_info($err = '')
195 global $vars, $_attach_messages;
197 foreach (array('refer', 'file', 'age') as $var) {
198 ${$var} = isset($vars[$var]) ? $vars[$var] : '';
201 $obj = & new AttachFile($refer, $file, $age);
202 return $obj->getstatus() ?
204 array('msg'=>$_attach_messages['err_notfound']);
208 function attach_delete()
210 global $vars, $_attach_messages;
212 foreach (array('refer', 'file', 'age', 'pass') as $var) {
213 ${$var} = isset($vars[$var]) ? $vars[$var] : '';
216 if (is_freeze($refer) || ! is_editable($refer)) {
217 return array('msg'=>$_attach_messages['err_noparm']);
219 $obj = & new AttachFile($refer, $file, $age);
220 return $obj->getstatus() ?
221 $obj->delete($pass) :
222 array('msg'=>$_attach_messages['err_notfound']);
227 function attach_freeze($freeze)
229 global $vars, $_attach_messages;
231 foreach (array('refer', 'file', 'age', 'pass') as $var) {
232 ${$var} = isset($vars[$var]) ? $vars[$var] : '';
235 if (is_freeze($refer) || ! is_editable($refer)) {
236 return array('msg'=>$_attach_messages['err_noparm']);
238 $obj = & new AttachFile($refer, $file, $age);
239 return $obj->getstatus() ?
240 $obj->freeze($freeze, $pass) :
241 array('msg'=>$_attach_messages['err_notfound']);
246 function attach_open()
248 global $vars, $_attach_messages;
250 foreach (array('refer', 'file', 'age') as $var) {
251 ${$var} = isset($vars[$var]) ? $vars[$var] : '';
254 $obj = & new AttachFile($refer, $file, $age);
255 return $obj->getstatus() ?
257 array('msg'=>$_attach_messages['err_notfound']);
261 function attach_list()
263 global $vars, $_attach_messages;
265 $refer = isset($vars['refer']) ? $vars['refer'] : '';
267 $obj = & new AttachPages($refer);
269 $msg = $_attach_messages[($refer == '') ? 'msg_listall' : 'msg_listpage'];
270 $body = ($refer == '' || isset($obj->pages[$refer])) ?
271 $obj->toString($refer, FALSE) :
272 $_attach_messages['err_noexist'];
274 return array('msg'=>$msg, 'body'=>$body);
277 // ¥¢¥Ã¥×¥í¡¼¥É¥Õ¥©¡¼¥à¤òɽ¼¨ (action»þ)
278 function attach_showform()
280 global $vars, $_attach_messages;
282 $page = isset($vars['page']) ? $vars['page'] : '';
283 $vars['refer'] = $page;
284 $body = attach_form($page);
286 return array('msg'=>$_attach_messages['msg_upload'], 'body'=>$body);
291 function attach_mime_content_type($filename)
293 $type = 'application/octet-stream'; // default
295 if (! file_exists($filename)) return $type;
297 $size = @getimagesize($filename);
298 if (is_array($size)) {
300 case 1: return 'image/gif';
301 case 2: return 'image/jpeg';
302 case 3: return 'image/png';
303 case 4: return 'application/x-shockwave-flash';
308 if (! preg_match('/_((?:[0-9A-F]{2})+)(?:\.\d+)?$/', $filename, $matches))
311 $filename = decode($matches[1]);
313 // mime-type°ìÍ÷ɽ¤ò¼èÆÀ
314 $config = new Config(ATTACH_CONFIG_PAGE_MIME);
315 $table = $config->read() ? $config->get('mime-type') : array();
316 unset($config); // ¥á¥â¥êÀáÌó
318 foreach ($table as $row) {
319 $_type = trim($row[0]);
320 $exts = preg_split('/\s+|,/', trim($row[1]), -1, PREG_SPLIT_NO_EMPTY);
321 foreach ($exts as $ext) {
322 if (preg_match("/\.$ext$/i", $filename)) return $_type;
329 // ¥¢¥Ã¥×¥í¡¼¥É¥Õ¥©¡¼¥à¤Î½ÐÎÏ
330 function attach_form($page)
332 global $script, $vars, $_attach_messages;
334 $r_page = rawurlencode($page);
335 $s_page = htmlspecialchars($page);
338 [<a href="$script?plugin=attach&pcmd=list&refer=$r_page">{$_attach_messages['msg_list']}</a>]
339 [<a href="$script?plugin=attach&pcmd=list">{$_attach_messages['msg_listall']}</a>]
343 if (! ini_get('file_uploads')) return '#attach(): file_uploads disabled<br />' . $navi;
344 if (! is_page($page)) return '#attach(): No such page<br />' . $navi;
346 $maxsize = MAX_FILESIZE;
347 $msg_maxsize = sprintf($_attach_messages['msg_maxsize'], number_format($maxsize/1024) . 'KB');
350 if (ATTACH_PASSWORD_REQUIRE || ATTACH_UPLOAD_ADMIN_ONLY) {
351 $title = $_attach_messages[ATTACH_UPLOAD_ADMIN_ONLY ? 'msg_adminpass' : 'msg_password'];
352 $pass = '<br />' . $title . ': <input type="password" name="pass" size="8" />';
355 <form enctype="multipart/form-data" action="$script" method="post">
357 <input type="hidden" name="plugin" value="attach" />
358 <input type="hidden" name="pcmd" value="post" />
359 <input type="hidden" name="refer" value="$s_page" />
360 <input type="hidden" name="max_file_size" value="$maxsize" />
365 {$_attach_messages['msg_file']}: <input type="file" name="attach_file" />
367 <input type="submit" value="{$_attach_messages['btn_upload']}" />
377 var $page, $file, $age, $basename, $filename, $logname;
382 var $status = array('count'=>array(0), 'age'=>'', 'pass'=>'', 'freeze'=>FALSE);
384 function AttachFile($page, $file, $age = 0)
387 $this->file = basename($file);
388 $this->age = is_numeric($age) ? $age : 0;
390 $this->basename = UPLOAD_DIR . encode($page) . '_' . encode($this->file);
391 $this->filename = $this->basename . ($age ? '.' . $age : '');
392 $this->logname = $this->basename . '.log';
393 $this->exist = file_exists($this->filename);
394 $this->time = $this->exist ? filemtime($this->filename) - LOCALZONE : 0;
395 $this->md5hash = $this->exist ? md5_file($this->filename) : '';
401 if (! $this->exist) return FALSE;
404 if (file_exists($this->logname)) {
405 $data = file($this->logname);
406 foreach ($this->status as $key=>$value) {
407 $this->status[$key] = chop(array_shift($data));
409 $this->status['count'] = explode(',', $this->status['count']);
411 $this->time_str = get_date('Y/m/d H:i:s', $this->time);
412 $this->size = filesize($this->filename);
413 $this->size_str = sprintf('%01.1f', round($this->size/1024, 1)) . 'KB';
414 $this->type = attach_mime_content_type($this->filename);
422 $this->status['count'] = join(',', $this->status['count']);
423 $fp = fopen($this->logname, 'wb') or
424 die_message('cannot write ' . $this->logname);
425 set_file_buffer($fp, 0);
428 foreach ($this->status as $key=>$value) {
429 fwrite($fp, $value . "\n");
436 function datecomp($a, $b) {
437 return ($a->time == $b->time) ? 0 : (($a->time > $b->time) ? -1 : 1);
440 function toString($showicon, $showinfo)
442 global $script, $_attach_messages;
445 $param = '&file=' . rawurlencode($this->file) . '&refer=' . rawurlencode($this->page) .
446 ($this->age ? '&age=' . $this->age : '');
447 $title = $this->time_str . ' ' . $this->size_str;
448 $label = ($showicon ? FILE_ICON : '') . htmlspecialchars($this->file);
450 $label .= ' (backup No.' . $this->age . ')';
454 $_title = str_replace('$1', rawurlencode($this->file), $_attach_messages['msg_info']);
455 $info = "\n<span class=\"small\">[<a href=\"$script?plugin=attach&pcmd=info$param\" title=\"$_title\">{$_attach_messages['btn_info']}</a>]</span><br />\n";
456 $count = ($showicon && ! empty($this->status['count'][$this->age])) ?
457 sprintf($_attach_messages['msg_count'], $this->status['count'][$this->age]) : '';
459 return "<a href=\"$script?plugin=attach&pcmd=open$param\" title=\"$title\">$label</a>$count$info";
465 global $script, $_attach_messages;
467 $r_page = rawurlencode($this->page);
468 $s_page = htmlspecialchars($this->page);
469 $s_file = htmlspecialchars($this->file);
470 $s_err = ($err == '') ? '' : '<p style="font-weight:bold">' . $_attach_messages[$err] . '</p>';
474 $msg_delete = '<input type="radio" name="pcmd" value="delete" />' .
475 $_attach_messages['msg_delete'] .
476 $_attach_messages['msg_require'] . '<br />';
479 if ($this->status['freeze']) {
480 $msg_freezed = "<dd>{$_attach_messages['msg_isfreeze']}</dd>";
482 $msg_freeze = '<input type="radio" name="pcmd" value="unfreeze" />' .
483 $_attach_messages['msg_unfreeze'] .
484 $_attach_messages['msg_require'] . '<br />';
487 $msg_delete = '<input type="radio" name="pcmd" value="delete" />' .
488 $_attach_messages['msg_delete'];
489 if (ATTACH_DELETE_ADMIN_ONLY || $this->age) {
490 $msg_delete .= $_attach_messages['msg_require'];
492 $msg_delete .= '<br />';
493 $msg_freeze = '<input type="radio" name="pcmd" value="freeze" />' .
494 $_attach_messages['msg_freeze'] .
495 $_attach_messages['msg_require'] . '<br />';
498 $info = $this->toString(TRUE, FALSE);
500 $retval = array('msg'=>sprintf($_attach_messages['msg_info'], htmlspecialchars($this->file)));
501 $retval['body'] = <<< EOD
503 [<a href="$script?plugin=attach&pcmd=list&refer=$r_page">{$_attach_messages['msg_list']}</a>]
504 [<a href="$script?plugin=attach&pcmd=list">{$_attach_messages['msg_listall']}</a>]
508 <dd>{$_attach_messages['msg_page']}:$s_page</dd>
509 <dd>{$_attach_messages['msg_filename']}:{$this->filename}</dd>
510 <dd>{$_attach_messages['msg_md5hash']}:{$this->md5hash}</dd>
511 <dd>{$_attach_messages['msg_filesize']}:{$this->size_str} ({$this->size} bytes)</dd>
512 <dd>Content-type:{$this->type}</dd>
513 <dd>{$_attach_messages['msg_date']}:{$this->time_str}</dd>
514 <dd>{$_attach_messages['msg_dlcount']}:{$this->status['count'][$this->age]}</dd>
519 <form action="$script" method="post">
521 <input type="hidden" name="plugin" value="attach" />
522 <input type="hidden" name="refer" value="$s_page" />
523 <input type="hidden" name="file" value="$s_file" />
524 <input type="hidden" name="age" value="{$this->age}" />
527 {$_attach_messages['msg_password']}: <input type="password" name="pass" size="8" />
528 <input type="submit" value="{$_attach_messages['btn_submit']}" />
535 function delete($pass)
537 global $_attach_messages;
539 if ($this->status['freeze']) return attach_info('msg_isfreeze');
541 if (! pkwk_login($pass)) {
542 if (ATTACH_DELETE_ADMIN_ONLY || $this->age) {
543 return attach_info('err_adminpass');
544 } else if (ATTACH_PASSWORD_REQUIRE &&
545 md5($pass) != $this->status['pass']) {
546 return attach_info('err_password');
552 (ATTACH_DELETE_ADMIN_ONLY && ATTACH_DELETE_ADMIN_NOBACKUP)) {
553 @unlink($this->filename);
556 $age = ++$this->status['age'];
557 } while (file_exists($this->basename . '.' . $age));
559 if (! rename($this->basename,$this->basename . '.' . $age)) {
561 return array('msg'=>$_attach_messages['err_delete']);
564 $this->status['count'][$age] = $this->status['count'][0];
565 $this->status['count'][0] = 0;
569 if (is_page($this->page)) {
570 touch(get_filename($this->page));
573 return array('msg'=>$_attach_messages['msg_deleted']);
576 function freeze($freeze, $pass)
578 global $_attach_messages;
580 if (! pkwk_login($pass)) return attach_info('err_adminpass');
583 $this->status['freeze'] = $freeze;
586 return array('msg'=>$_attach_messages[$freeze ? 'msg_freezed' : 'msg_unfreezed']);
592 $this->status['count'][$this->age]++;
595 // for Japanese (???)
596 $filename = htmlspecialchars(mb_convert_encoding($this->file,'SJIS','auto'));
598 ini_set('default_charset', '');
599 mb_http_output('pass');
601 header('Content-Disposition: inline; filename="' . $filename . '"');
602 header('Content-Length: ' . $this->size);
603 header('Content-Type: ' . $this->type);
604 @readfile($this->filename);
613 var $files = array();
615 function AttachFiles($page)
620 function add($file, $age)
622 $this->files[$file][$age] = & new AttachFile($this->page, $file, $age);
625 // ¥Õ¥¡¥¤¥ë°ìÍ÷¤ò¼èÆÀ
626 function toString($flat)
628 global $_title_cannotread;
630 if (! check_readable($this->page, FALSE, FALSE)) {
631 return str_replace('$1', make_pagelink($this->page), $_title_cannotread);
633 return $this->to_flat();
637 $files = array_keys($this->files);
640 foreach ($files as $file) {
642 foreach (array_keys($this->files[$file]) as $age) {
643 $_files[$age] = $this->files[$file][$age]->toString(FALSE, TRUE);
645 if (! isset($_files[0])) {
646 $_files[0] = htmlspecialchars($file);
651 $ret .= " <li>$_file\n";
652 if (count($_files)) {
653 $ret .= "<ul>\n<li>" . join("</li>\n<li>", $_files) . "</li>\n</ul>\n";
657 return make_pagelink($this->page) . "\n<ul>\n$ret</ul>\n";
660 // ¥Õ¥¡¥¤¥ë°ìÍ÷¤ò¼èÆÀ(inline)
665 foreach (array_keys($this->files) as $file) {
666 if (isset($this->files[$file][0])) {
667 $files[$file] = & $this->files[$file][0];
670 uasort($files, array('AttachFile', 'datecomp'));
671 foreach (array_keys($files) as $file) {
672 $ret .= $files[$file]->toString(TRUE, TRUE) . ' ';
682 var $pages = array();
684 function AttachPages($page = '', $age = NULL)
687 $dir = opendir(UPLOAD_DIR) or
688 die('directory ' . UPLOAD_DIR . ' is not exist or not readable.');
690 $page_pattern = ($page == '') ? '(?:[0-9A-F]{2})+' : preg_quote(encode($page), '/');
691 $age_pattern = ($age === NULL) ?
692 '(?:\.([0-9]+))?' : ($age ? "\.($age)" : '');
693 $pattern = "/^({$page_pattern})_((?:[0-9A-F]{2})+){$age_pattern}$/";
696 while ($file = readdir($dir)) {
697 if (! preg_match($pattern, $file, $matches))
700 $_page = decode($matches[1]);
701 $_file = decode($matches[2]);
702 $_age = isset($matches[3]) ? $matches[3] : 0;
703 if (! isset($this->pages[$_page])) {
704 $this->pages[$_page] = & new AttachFiles($_page);
706 $this->pages[$_page]->add($_file, $_age);
711 function toString($page = '', $flat = FALSE)
716 if (! isset($this->pages[$page])) {
719 return $this->pages[$page]->toString($flat);
724 $pages = array_keys($this->pages);
727 foreach ($pages as $page) {
728 if (preg_match("/$non_list/", $page)) continue;
729 $ret .= '<li>' . $this->pages[$page]->toString($flat) . "</li>\n";
731 return "\n<ul>\n" . $ret . "</ul>\n";