2 // PukiWiki - Yet another WikiWikiWeb clone
5 // 2003-2022 PukiWiki Development Team
6 // 2002-2003 PANDA <panda@arino.jp> http://home.arino.jp/
7 // 2002 Y.MASUI <masui@hisec.co.jp> http://masui.net/pukiwiki/
8 // 2001-2002 Originally written by yu-ji
9 // License: GPL v2 or (at your option) any later version
13 // NOTE (PHP > 4.2.3):
14 // This feature is disabled at newer version of PHP.
15 // Set this at php.ini if you want.
16 // Max file size for upload on PHP (PHP default: 2MB)
17 ini_set('upload_max_filesize', '2M');
19 // Max file size for upload on script of PukiWikiX_FILESIZE
20 define('PLUGIN_ATTACH_MAX_FILESIZE', (1024 * 1024)); // default: 1MB
22 // 管理者だけが添付ファイルをアップロードできるようにする
23 define('PLUGIN_ATTACH_UPLOAD_ADMIN_ONLY', TRUE); // FALSE or TRUE
25 // 管理者だけが添付ファイルを削除できるようにする
26 define('PLUGIN_ATTACH_DELETE_ADMIN_ONLY', TRUE); // FALSE or TRUE
28 // 管理者が添付ファイルを削除するときは、バックアップを作らない
29 // PLUGIN_ATTACH_DELETE_ADMIN_ONLY=TRUEのとき有効
30 define('PLUGIN_ATTACH_DELETE_ADMIN_NOBACKUP', FALSE); // FALSE or TRUE
32 // アップロード/削除時にパスワードを要求する(ADMIN_ONLYが優先)
33 define('PLUGIN_ATTACH_PASSWORD_REQUIRE', FALSE); // FALSE or TRUE
36 define('PLUGIN_ATTACH_RENAME_ENABLE', TRUE); // FALSE or TRUE
39 define('PLUGIN_ATTACH_FILE_MODE', 0644);
40 //define('PLUGIN_ATTACH_FILE_MODE', 0604); // for XREA.COM
43 define('PLUGIN_ATTACH_FILE_ICON', '<img src="' . IMAGE_DIR . 'file.png"' .
44 ' width="20" height="20" alt="file"' .
45 ' style="border-width:0" />');
48 define('PLUGIN_ATTACH_CONFIG_PAGE_MIME', 'plugin/attach/mime-type');
50 // Limit attach file name length
51 define('PLUGIN_ATTACH_MAX_LEAF_BASE_NAME_LENGTH', 240);
54 function plugin_attach_convert()
58 $page = isset($vars['page']) ? $vars['page'] : '';
60 $nolist = $noform = FALSE;
61 if (func_num_args() > 0) {
62 foreach (func_get_args() as $arg) {
63 $arg = strtolower($arg);
64 $nolist |= ($arg == 'nolist');
65 $noform |= ($arg == 'noform');
71 $obj = new AttachPages($page);
72 $ret .= $obj->toString($page, TRUE);
75 $ret .= attach_form($page);
82 function plugin_attach_action()
84 global $vars, $_attach_messages;
86 // Backward compatible
87 if (isset($vars['openfile'])) {
88 $vars['file'] = $vars['openfile'];
89 $vars['pcmd'] = 'open';
91 if (isset($vars['delfile'])) {
92 $vars['file'] = $vars['delfile'];
93 $vars['pcmd'] = 'delete';
96 $pcmd = isset($vars['pcmd']) ? $vars['pcmd'] : '';
97 $refer = isset($vars['refer']) ? $vars['refer'] : '';
98 $pass = isset($vars['pass']) ? $vars['pass'] : NULL;
99 $page = isset($vars['page']) ? $vars['page'] : '';
101 if ($refer === '' && $page !== '') {
104 if ($refer != '' && is_pagename($refer)) {
105 if(in_array($pcmd, array('info', 'open', 'list'))) {
106 check_readable($refer);
108 check_editable($refer);
113 if (isset($_FILES['attach_file'])) {
115 return attach_upload($_FILES['attach_file'], $refer, $pass);
118 case 'delete': /*FALLTHROUGH*/
121 if (PKWK_READONLY) die_message('PKWK_READONLY prohibits editing');
124 case 'info' : return attach_info();
125 case 'delete' : return attach_delete();
126 case 'open' : return attach_open();
127 case 'list' : return attach_list();
128 case 'freeze' : return attach_freeze(TRUE);
129 case 'unfreeze' : return attach_freeze(FALSE);
130 case 'rename' : return attach_rename();
131 case 'upload' : return attach_showform();
133 if ($page == '' || ! is_page($page)) {
134 return attach_list();
136 return attach_showform();
141 //-------- call from skin
142 function attach_filelist()
144 global $vars, $_attach_messages;
146 $page = isset($vars['page']) ? $vars['page'] : '';
148 $obj = new AttachPages($page, 0);
150 if (! isset($obj->pages[$page])) {
153 return $_attach_messages['msg_file'] . ': ' .
154 $obj->toString($page, TRUE) . "\n";
160 // $pass = NULL : パスワードが指定されていない
161 // $pass = TRUE : アップロード許可
162 function attach_upload($file, $page, $pass = NULL)
164 global $_attach_messages, $notify, $notify_subject;
166 if (PKWK_READONLY) die_message('PKWK_READONLY prohibits editing');
168 // Check query-string
169 $query = 'plugin=attach&pcmd=info&refer=' . rawurlencode($page) .
170 '&file=' . rawurlencode($file['name']);
172 if (PKWK_QUERY_STRING_MAX && strlen($query) > PKWK_QUERY_STRING_MAX) {
173 pkwk_common_headers();
174 echo('Query string (page name and/or file name) too long');
176 } else if (! is_page($page)) {
177 die_message('No such page');
178 } else if ($file['tmp_name'] == '' || ! is_uploaded_file($file['tmp_name'])) {
179 return array('result'=>FALSE);
180 } else if ($file['size'] > PLUGIN_ATTACH_MAX_FILESIZE) {
183 'msg'=>$_attach_messages['err_exceed']);
184 } else if (! is_pagename($page) || ($pass !== TRUE && ! is_editable($page))) {
187 msg'=>$_attach_messages['err_noparm']);
188 } else if (PLUGIN_ATTACH_UPLOAD_ADMIN_ONLY && $pass !== TRUE &&
189 ($pass === NULL || ! pkwk_login($pass))) {
192 'msg'=>$_attach_messages['err_adminpass']);
195 $obj = new AttachFile($page, $file['name']);
197 return array('result'=>FALSE,
198 'msg'=>$_attach_messages['err_exists']);
200 $leafbasename_len = strlen($obj->leafbasename);
201 if (PLUGIN_ATTACH_MAX_LEAF_BASE_NAME_LENGTH < $leafbasename_len) {
202 die_message('Too long file name (' . $leafbasename_len . ' bytes)');
205 if (move_uploaded_file($file['tmp_name'], $obj->filename))
206 chmod($obj->filename, PLUGIN_ATTACH_FILE_MODE);
209 pkwk_touch_file(get_filename($page));
212 $obj->status['pass'] = ($pass !== TRUE && $pass !== NULL) ? md5($pass) : '';
216 $footer['ACTION'] = 'File attached';
217 $footer['FILENAME'] = $file['name'];
218 $footer['FILESIZE'] = $file['size'];
219 $footer['PAGE'] = $page;
221 $footer['URI'] = get_base_uri(PKWK_URI_ABSOLUTE) .
224 '&refer=' . rawurlencode($page) .
225 '&file=' . rawurlencode($file['name']) .
228 $footer['USER_AGENT'] = TRUE;
229 $footer['REMOTE_ADDR'] = TRUE;
231 pkwk_mail_notify($notify_subject, "\n", $footer) or
232 die('pkwk_mail_notify(): Failed');
237 'msg'=>$_attach_messages['msg_uploaded']);
241 function attach_info($err = '')
243 global $vars, $_attach_messages;
245 foreach (array('refer', 'file', 'age') as $var)
246 ${$var} = isset($vars[$var]) ? $vars[$var] : '';
248 $obj = new AttachFile($refer, $file, $age);
249 return $obj->getstatus() ?
251 array('msg'=>$_attach_messages['err_notfound']);
255 function attach_delete()
257 global $vars, $_attach_messages;
259 foreach (array('refer', 'file', 'age', 'pass') as $var)
260 ${$var} = isset($vars[$var]) ? $vars[$var] : '';
262 if (is_freeze($refer) || ! is_editable($refer))
263 return array('msg'=>$_attach_messages['err_noparm']);
265 $obj = new AttachFile($refer, $file, $age);
266 if (! $obj->getstatus())
267 return array('msg'=>$_attach_messages['err_notfound']);
269 return $obj->delete($pass);
273 function attach_freeze($freeze)
275 global $vars, $_attach_messages;
277 foreach (array('refer', 'file', 'age', 'pass') as $var) {
278 ${$var} = isset($vars[$var]) ? $vars[$var] : '';
281 if (is_freeze($refer) || ! is_editable($refer)) {
282 return array('msg'=>$_attach_messages['err_noparm']);
284 $obj = new AttachFile($refer, $file, $age);
285 return $obj->getstatus() ?
286 $obj->freeze($freeze, $pass) :
287 array('msg'=>$_attach_messages['err_notfound']);
292 function attach_rename()
294 global $vars, $_attach_messages;
296 foreach (array('refer', 'file', 'age', 'pass', 'newname') as $var) {
297 ${$var} = isset($vars[$var]) ? $vars[$var] : '';
300 if (is_freeze($refer) || ! is_editable($refer)) {
301 return array('msg'=>$_attach_messages['err_noparm']);
303 $obj = new AttachFile($refer, $file, $age);
304 if (! $obj->getstatus())
305 return array('msg'=>$_attach_messages['err_notfound']);
307 return $obj->rename($pass, $newname);
312 function attach_open()
314 global $vars, $_attach_messages;
316 foreach (array('refer', 'file', 'age') as $var) {
317 ${$var} = isset($vars[$var]) ? $vars[$var] : '';
320 $obj = new AttachFile($refer, $file, $age);
321 return $obj->getstatus() ?
323 array('msg'=>$_attach_messages['err_notfound']);
327 function attach_list()
329 global $vars, $_attach_messages;
331 $refer = isset($vars['refer']) ? $vars['refer'] : '';
333 $obj = new AttachPages($refer);
335 $msg = $_attach_messages[($refer == '') ? 'msg_listall' : 'msg_listpage'];
336 $body = ($refer == '' || isset($obj->pages[$refer])) ?
337 $obj->toString($refer, FALSE) :
338 $_attach_messages['err_noexist'];
340 return array('msg'=>$msg, 'body'=>$body);
343 // アップロードフォームを表示 (action時)
344 function attach_showform()
346 global $vars, $_attach_messages;
348 $page = isset($vars['page']) ? $vars['page'] : '';
349 $vars['refer'] = $page;
350 $body = attach_form($page);
351 return array('msg'=>$_attach_messages['msg_upload'], 'body'=>$body);
356 function attach_mime_content_type($filename, $displayname)
358 $type = 'application/octet-stream'; // default
360 if (! file_exists($filename)) return $type;
361 $pathinfo = pathinfo($displayname);
362 $ext0 = $pathinfo['extension'];
363 if (preg_match('/^(gif|jpg|jpeg|png|swf)$/i', $ext0)) {
364 $size = @getimagesize($filename);
365 if (is_array($size)) {
367 case 1: return 'image/gif';
368 case 2: return 'image/jpeg';
369 case 3: return 'image/png';
370 case 4: return 'application/x-shockwave-flash';
375 $config = new Config(PLUGIN_ATTACH_CONFIG_PAGE_MIME);
376 $table = $config->read() ? $config->get('mime-type') : array();
377 unset($config); // メモリ節約
378 foreach ($table as $row) {
379 $_type = trim($row[0]);
380 $exts = preg_split('/\s+|,/', trim($row[1]), -1, PREG_SPLIT_NO_EMPTY);
381 foreach ($exts as $ext) {
382 if (preg_match("/\.$ext$/i", $displayname)) return $_type;
389 function attach_form($page)
391 global $vars, $_attach_messages;
393 $script = get_base_uri();
394 $r_page = rawurlencode($page);
395 $s_page = htmlsc($page);
398 [<a href="$script?plugin=attach&pcmd=list&refer=$r_page">{$_attach_messages['msg_list']}</a>]
399 [<a href="$script?plugin=attach&pcmd=list">{$_attach_messages['msg_listall']}</a>]
403 if (! ini_get('file_uploads')) return '#attach(): file_uploads disabled<br />' . $navi;
404 if (! is_page($page)) return '#attach(): No such page<br />' . $navi;
406 $maxsize = PLUGIN_ATTACH_MAX_FILESIZE;
407 $msg_maxsize = sprintf($_attach_messages['msg_maxsize'], number_format($maxsize/1024) . 'KB');
410 if (PLUGIN_ATTACH_PASSWORD_REQUIRE || PLUGIN_ATTACH_UPLOAD_ADMIN_ONLY) {
411 $title = $_attach_messages[PLUGIN_ATTACH_UPLOAD_ADMIN_ONLY ? 'msg_adminpass' : 'msg_password'];
412 $pass = '<br />' . $title . ': <input type="password" name="pass" size="8" />';
415 <form enctype="multipart/form-data" action="$script" method="post">
417 <input type="hidden" name="plugin" value="attach" />
418 <input type="hidden" name="pcmd" value="post" />
419 <input type="hidden" name="refer" value="$s_page" />
420 <input type="hidden" name="max_file_size" value="$maxsize" />
425 <label for="_p_attach_file">{$_attach_messages['msg_file']}:</label> <input type="file" name="attach_file" id="_p_attach_file" />
427 <input type="submit" value="{$_attach_messages['btn_upload']}" />
437 var $page, $file, $age, $basename, $filename, $logname;
442 var $status = array('count'=>array(0), 'age'=>'', 'pass'=>'', 'freeze'=>FALSE);
444 function AttachFile($page, $file, $age = 0)
446 $this->__construct($page, $file, $age);
448 function __construct($page, $file, $age = 0)
451 $this->file = preg_replace('#^.*/#','',$file);
452 $this->age = is_numeric($age) ? $age : 0;
454 $this->leafbasename = encode($page) . '_' . encode($this->file);
455 $this->basename = UPLOAD_DIR . $this->leafbasename;
456 $this->filename = $this->basename . ($age ? '.' . $age : '');
457 $this->logname = $this->basename . '.log';
458 $this->exist = file_exists($this->filename);
459 $this->time = $this->exist ? filemtime($this->filename) - LOCALZONE : 0;
464 return $this->exist ? md5_file($this->filename) : '';
471 if (file_exists($this->logname)) {
472 $data = file($this->logname);
473 foreach ($this->status as $key=>$value) {
474 $this->status[$key] = chop(array_shift($data));
476 $this->status['count'] = explode(',', $this->status['count']);
478 if (! $this->exist) return FALSE;
479 $this->time_str = get_date('Y/m/d H:i:s', $this->time);
480 $this->size = filesize($this->filename);
481 $this->size_str = sprintf('%01.1f', round($this->size/1024, 1)) . 'KB';
482 $this->type = attach_mime_content_type($this->filename, $this->file);
489 $this->status['count'] = join(',', $this->status['count']);
490 $fp = fopen($this->logname, 'wb') or
491 die_message('cannot write ' . $this->logname);
492 set_file_buffer($fp, 0);
495 foreach ($this->status as $key=>$value) {
496 fwrite($fp, $value . "\n");
502 function toString($showicon, $showinfo)
504 global $_attach_messages;
506 $script = get_base_uri();
508 $param = '&file=' . rawurlencode($this->file) . '&refer=' . rawurlencode($this->page) .
509 ($this->age ? '&age=' . $this->age : '');
510 $title = $this->time_str . ' ' . $this->size_str;
511 $label = ($showicon ? PLUGIN_ATTACH_FILE_ICON : '') . htmlsc($this->file);
513 $label .= ' (backup No.' . $this->age . ')';
517 $_title = str_replace('$1', rawurlencode($this->file), $_attach_messages['msg_info']);
518 $info = "\n<span class=\"small\">[<a href=\"$script?plugin=attach&pcmd=info$param\" title=\"$_title\">{$_attach_messages['btn_info']}</a>]</span>\n";
519 $count = ($showicon && ! empty($this->status['count'][$this->age])) ?
520 sprintf($_attach_messages['msg_count'], $this->status['count'][$this->age]) : '';
522 return "<a href=\"$script?plugin=attach&pcmd=open$param\" title=\"$title\">$label</a>$count$info";
528 global $_attach_messages;
530 $script = get_base_uri();
531 $r_page = rawurlencode($this->page);
532 $s_page = htmlsc($this->page);
533 $s_file = htmlsc($this->file);
534 $s_err = ($err == '') ? '' : '<p style="font-weight:bold">' . $_attach_messages[$err] . '</p>';
539 $msg_delete = '<input type="radio" name="pcmd" id="_p_attach_delete" value="delete" />' .
540 '<label for="_p_attach_delete">' . $_attach_messages['msg_delete'] .
541 $_attach_messages['msg_require'] . '</label><br />';
544 if ($this->status['freeze']) {
545 $msg_freezed = "<dd>{$_attach_messages['msg_isfreeze']}</dd>";
547 $msg_freeze = '<input type="radio" name="pcmd" id="_p_attach_unfreeze" value="unfreeze" />' .
548 '<label for="_p_attach_unfreeze">' . $_attach_messages['msg_unfreeze'] .
549 $_attach_messages['msg_require'] . '</label><br />';
552 $msg_delete = '<input type="radio" name="pcmd" id="_p_attach_delete" value="delete" />' .
553 '<label for="_p_attach_delete">' . $_attach_messages['msg_delete'];
554 if (PLUGIN_ATTACH_DELETE_ADMIN_ONLY || $this->age)
555 $msg_delete .= $_attach_messages['msg_require'];
556 $msg_delete .= '</label><br />';
557 $msg_freeze = '<input type="radio" name="pcmd" id="_p_attach_freeze" value="freeze" />' .
558 '<label for="_p_attach_freeze">' . $_attach_messages['msg_freeze'] .
559 $_attach_messages['msg_require'] . '</label><br />';
561 if (PLUGIN_ATTACH_RENAME_ENABLE) {
562 $msg_rename = '<input type="radio" name="pcmd" id="_p_attach_rename" value="rename" />' .
563 '<label for="_p_attach_rename">' . $_attach_messages['msg_rename'] .
564 $_attach_messages['msg_require'] . '</label><br /> ' .
565 '<label for="_p_attach_newname">' . $_attach_messages['msg_newname'] .
567 '<input type="text" name="newname" id="_p_attach_newname" size="40" value="' .
568 $this->file . '" /><br />';
572 $info = $this->toString(TRUE, FALSE);
573 $hash = $this->gethash();
575 $retval = array('msg'=>sprintf($_attach_messages['msg_info'], htmlsc($this->file)));
576 $retval['body'] = <<< EOD
578 [<a href="$script?plugin=attach&pcmd=list&refer=$r_page">{$_attach_messages['msg_list']}</a>]
579 [<a href="$script?plugin=attach&pcmd=list">{$_attach_messages['msg_listall']}</a>]
583 <dd>{$_attach_messages['msg_page']}:$s_page</dd>
584 <dd>{$_attach_messages['msg_filename']}:{$this->filename}</dd>
585 <dd>{$_attach_messages['msg_md5hash']}:$hash</dd>
586 <dd>{$_attach_messages['msg_filesize']}:{$this->size_str} ({$this->size} bytes)</dd>
587 <dd>Content-type:{$this->type}</dd>
588 <dd>{$_attach_messages['msg_date']}:{$this->time_str}</dd>
589 <dd>{$_attach_messages['msg_dlcount']}:{$this->status['count'][$this->age]}</dd>
594 <form action="$script" method="post">
596 <input type="hidden" name="plugin" value="attach" />
597 <input type="hidden" name="refer" value="$s_page" />
598 <input type="hidden" name="file" value="$s_file" />
599 <input type="hidden" name="age" value="{$this->age}" />
604 <label for="_p_attach_password">{$_attach_messages['msg_password']}:</label>
605 <input type="password" name="pass" id="_p_attach_password" size="8" />
606 <input type="submit" value="{$_attach_messages['btn_submit']}" />
613 function delete($pass)
615 global $_attach_messages, $notify, $notify_subject;
617 if ($this->status['freeze']) return attach_info('msg_isfreeze');
619 if (! pkwk_login($pass)) {
620 if (PLUGIN_ATTACH_DELETE_ADMIN_ONLY || $this->age) {
621 return attach_info('err_adminpass');
622 } else if (PLUGIN_ATTACH_PASSWORD_REQUIRE &&
623 md5($pass) !== $this->status['pass']) {
624 return attach_info('err_password');
630 (PLUGIN_ATTACH_DELETE_ADMIN_ONLY && PLUGIN_ATTACH_DELETE_ADMIN_NOBACKUP)) {
631 @unlink($this->filename);
634 $age = ++$this->status['age'];
635 } while (file_exists($this->basename . '.' . $age));
637 if (! rename($this->basename,$this->basename . '.' . $age)) {
639 return array('msg'=>$_attach_messages['err_delete']);
642 $this->status['count'][$age] = $this->status['count'][0];
643 $this->status['count'][0] = 0;
647 if (is_page($this->page))
648 touch(get_filename($this->page));
651 $footer['ACTION'] = 'File deleted';
652 $footer['FILENAME'] = $this->file;
653 $footer['PAGE'] = $this->page;
654 $footer['URI'] = get_page_uri($this->page, PKWK_URI_ABSOLUTE);
655 $footer['USER_AGENT'] = TRUE;
656 $footer['REMOTE_ADDR'] = TRUE;
657 pkwk_mail_notify($notify_subject, "\n", $footer) or
658 die('pkwk_mail_notify(): Failed');
661 return array('msg'=>$_attach_messages['msg_deleted']);
664 function rename($pass, $newname)
666 global $_attach_messages, $notify, $notify_subject;
668 if ($this->status['freeze']) return attach_info('msg_isfreeze');
670 if (! pkwk_login($pass)) {
671 if (PLUGIN_ATTACH_DELETE_ADMIN_ONLY || $this->age) {
672 return attach_info('err_adminpass');
673 } else if (PLUGIN_ATTACH_PASSWORD_REQUIRE &&
674 md5($pass) !== $this->status['pass']) {
675 return attach_info('err_password');
678 $newbase = UPLOAD_DIR . encode($this->page) . '_' . encode($newname);
679 if (file_exists($newbase)) {
680 return array('msg'=>$_attach_messages['err_exists']);
682 if (! PLUGIN_ATTACH_RENAME_ENABLE) {
683 return array('msg'=>$_attach_messages['err_rename']);
685 if (! rename($this->basename, $newbase)) {
686 return array('msg'=>$_attach_messages['err_rename']);
688 // Rename primary file succeeded.
689 // Then, rename backup(archive) files and log file)
690 $rename_targets = array();
691 $dir = opendir(UPLOAD_DIR);
693 $matches_leaf = array();
694 if (preg_match('/(((?:[0-9A-F]{2})+)_((?:[0-9A-F]{2})+))$/', $this->basename, $matches_leaf)) {
695 $attachfile_leafname = $matches_leaf[1];
696 $attachfile_leafname_pattern = preg_quote($attachfile_leafname, '/');
697 $pattern = "/^({$attachfile_leafname_pattern})(\.((\d+)|(log)))$/";
699 while ($file = readdir($dir)) {
700 if (! preg_match($pattern, $file, $matches))
702 $basename2 = $matches[0];
703 $newbase2 = $newbase . $matches[2];
704 $rename_targets[$basename2] = $newbase2;
709 foreach ($rename_targets as $basename2=>$newbase2) {
710 $basename2path = UPLOAD_DIR . $basename2;
711 rename($basename2path, $newbase2);
713 return array('msg'=>$_attach_messages['msg_renamed']);
716 function freeze($freeze, $pass)
718 global $_attach_messages;
720 if (! pkwk_login($pass)) return attach_info('err_adminpass');
723 $this->status['freeze'] = $freeze;
726 return array('msg'=>$_attach_messages[$freeze ? 'msg_freezed' : 'msg_unfreezed']);
732 $this->status['count'][$this->age]++;
734 $filename = $this->file;
736 // Care for Japanese-character-included file name
737 $legacy_filename = mb_convert_encoding($filename, 'UTF-8', SOURCE_ENCODING);
739 switch(UA_NAME . '/' . UA_PROFILE){
741 $legacy_filename = mb_convert_encoding($filename, 'SJIS', SOURCE_ENCODING);
745 $utf8filename = mb_convert_encoding($filename, 'UTF-8', SOURCE_ENCODING);
747 ini_set('default_charset', '');
748 mb_http_output('pass');
750 pkwk_common_headers();
751 header('Content-Disposition: inline; filename="' . $legacy_filename
752 . '"; filename*=utf-8\'\'' . rawurlencode($utf8filename));
753 header('Content-Length: ' . $this->size);
754 header('Content-Type: ' . $this->type);
755 // Disable output bufferring
756 while (ob_get_level()) {
760 @readfile($this->filename);
769 var $files = array();
771 function AttachFiles($page)
773 $this->__construct($page);
775 function __construct($page)
780 function add($file, $age)
782 $this->files[$file][$age] = new AttachFile($this->page, $file, $age);
785 // date comparison function for uasort()
786 // $a, $b: AttachFile object
787 function datecomp($a, $b) {
788 return ($a->time == $b->time) ? 0 : (($a->time > $b->time) ? -1 : 1);
792 function toString($flat)
794 global $_title_cannotread;
796 if (! check_readable($this->page, FALSE, FALSE)) {
797 return str_replace('$1', make_pagelink($this->page), $_title_cannotread);
799 return $this->to_flat();
803 $files = array_keys($this->files);
804 sort($files, SORT_STRING);
806 foreach ($files as $file) {
808 foreach (array_keys($this->files[$file]) as $age) {
809 $_files[$age] = $this->files[$file][$age]->toString(FALSE, TRUE);
811 if (! isset($_files[0])) {
812 $_files[0] = htmlsc($file);
814 ksort($_files, SORT_NUMERIC);
817 $ret .= " <li>$_file\n";
818 if (count($_files)) {
819 $ret .= "<ul>\n<li>" . join("</li>\n<li>", $_files) . "</li>\n</ul>\n";
823 return make_pagelink($this->page) . "\n<ul>\n$ret</ul>\n";
831 foreach (array_keys($this->files) as $file) {
832 if (isset($this->files[$file][0])) {
833 $files[$file] = & $this->files[$file][0];
836 uasort($files, array($this, 'datecomp'));
837 foreach (array_keys($files) as $file) {
838 $ret .= $files[$file]->toString(TRUE, TRUE) . ' ';
847 var $pages = array();
849 function AttachPages($page = '', $age = NULL)
851 $this->__construct($page, $age);
853 function __construct($page = '', $age = NULL)
856 $dir = opendir(UPLOAD_DIR) or
857 die('directory ' . UPLOAD_DIR . ' is not exist or not readable.');
859 $page_pattern = ($page == '') ? '(?:[0-9A-F]{2})+' : preg_quote(encode($page), '/');
860 $age_pattern = ($age === NULL) ?
861 '(?:\.([0-9]+))?' : ($age ? "\.($age)" : '');
862 $pattern = "/^({$page_pattern})_((?:[0-9A-F]{2})+){$age_pattern}$/";
865 while (($file = readdir($dir)) !== FALSE) {
866 if (! preg_match($pattern, $file, $matches)) continue;
868 $_page = decode($matches[1]);
869 $_file = decode($matches[2]);
870 $_age = isset($matches[3]) ? $matches[3] : 0;
871 if (! isset($this->pages[$_page])) {
872 $this->pages[$_page] = new AttachFiles($_page);
874 $this->pages[$_page]->add($_file, $_age);
879 function toString($page = '', $flat = FALSE)
882 if (! isset($this->pages[$page])) {
885 return $this->pages[$page]->toString($flat);
890 $pages = array_keys($this->pages);
891 sort($pages, SORT_STRING);
893 foreach ($pages as $page) {
894 if (check_non_list($page)) continue;
895 $ret .= '<li>' . $this->pages[$page]->toString($flat) . '</li>' . "\n";
897 return "\n" . '<ul>' . "\n" . $ret . '</ul>' . "\n";