弄ってみました。長くてすいません。*1
plugin/backup.inc.php
ディレクトリ管理仕様のlib/backup.php
従来型データ用のlib/backup.php
最初はlib/backup.php, plugin/backup.inc.phpを両方入れ替えることでディレクトリ管理に移行する方向で考えていましたが、従来のbackup.phpにも最小限の安全性を加えようとしていくうちにずるずる今の形になってしまっています。
--- /lib/file.php Sun Jul 03 23:16:24 2005 +++ /lib/file.php Mon Sep 12 00:26:52 2005 @@ -36,14 +36,14 @@ $postdata = make_str_rules($postdata); + // Create backup + make_backup($page, $postdata == ''); // Is $postdata null? + // Create and write diff $oldpostdata = is_page($page) ? join('', get_source($page)) : ''; $diffdata = do_diff($oldpostdata, $postdata); file_write(DIFF_DIR, $page, $diffdata); - // Create backup - make_backup($page, $postdata == ''); // Is $postdata null? - // Create wiki text file_write(DATA_DIR, $page, $postdata, $notimestamp);
--- /plugin/backup.inc.php Sun May 08 01:21:52 2005 +++ /plugin/backup.inc.php Sat Sep 17 20:32:36 2005 @@ -9,60 +9,76 @@ function plugin_backup_action() { - global $vars, $do_backup, $hr; - global $_msg_backuplist, $_msg_diff, $_msg_nowdiff, $_msg_source, $_msg_backup; - global $_msg_view, $_msg_goto, $_msg_deleted; - global $_title_backupdiff, $_title_backupnowdiff, $_title_backupsource; - global $_title_backup, $_title_pagebackuplist, $_title_backuplist; + global $vars, $do_backup; + global $_title_pagebackuplist, $_title_backuplist; if (! $do_backup) return; $page = isset($vars['page']) ? $vars['page'] : ''; + $action = isset($vars['action']) ? $vars['action'] : ''; + + if (preg_match('/^(?:im|ex)port$/', $action)) return plugin_backup_convert($action); if ($page == '') return array('msg'=>$_title_backuplist, 'body'=>plugin_backup_get_list_all()); check_readable($page, true, true); - $s_page = htmlspecialchars($page); - $r_page = rawurlencode($page); - - $action = isset($vars['action']) ? $vars['action'] : ''; - if ($action == 'delete') return plugin_backup_delete($page); - $s_action = $r_action = ''; - if ($action != '') { - $s_action = htmlspecialchars($action); - $r_action = rawurlencode($action); + $s_utime = NULL; + if (isset($vars['age']) && is_numeric($vars['age']) && + ($vars['age'] += 0) > 0 && is_int($vars['age'])) { + $s_utime = $vars['age']; } - $s_age = (isset($vars['age']) && is_numeric($vars['age'])) ? $vars['age'] : 0; - if ($s_age == 0) return array( 'msg'=>$_title_pagebackuplist, 'body'=>plugin_backup_get_list($page)); + if ($action == 'delete') return plugin_backup_delete($page, $s_utime); + + if (isset($s_utime)) return plugin_backup_get_detail($page, $s_utime, $action); + + return array( 'msg'=>$_title_pagebackuplist, 'body'=>plugin_backup_get_list($page)); +} + +function plugin_backup_get_detail($page, $s_utime, $action) +{ + global $hr, $_msg_view, $_msg_goto, $_msg_deleted, $_msg_nobackup; + global $_msg_backuplist, $_msg_diff, $_msg_nowdiff, $_msg_source, $_msg_backup; + global $_title_backupdiff, $_title_backupnowdiff, $_title_backupsource, $_title_backup; + + $s_page = htmlspecialchars($page); + $r_page = rawurlencode($page); + $s_action = htmlspecialchars($action); + $r_action = rawurlencode($action); + $script = get_script_uri(); $body = '<ul>' . "\n"; $body .= ' <li><a href="' . $script . '?cmd=backup">' . $_msg_backuplist . '</a></li>' ."\n"; - $href = $script . '?cmd=backup&page=' . $r_page . '&age=' . $s_age; + $href = $script . '?cmd=backup&page=' . $r_page . '&age=' . $s_utime; $is_page = is_page($page); - if ($is_page && $action != 'diff') - $body .= ' <li>' . str_replace('$1', '<a href="' . $href . - '&action=diff">' . $_msg_diff . '</a>', - $_msg_view) . '</li>' . "\n"; - - if ($is_page && $action != 'nowdiff') - $body .= ' <li>' . str_replace('$1', '<a href="' . $href . - '&action=nowdiff">' . $_msg_nowdiff . '</a>', - $_msg_view) . '</li>' . "\n"; - - if ($action != 'source') - $body .= ' <li>' . str_replace('$1', '<a href="' . $href . - '&action=source">' . $_msg_source . '</a>', - $_msg_view) . '</li>' . "\n"; - - if ($action) - $body .= ' <li>' . str_replace('$1', '<a href="' . $href . - '">' . $_msg_backup . '</a>', - $_msg_view) . '</li>' . "\n"; + $backups = get_backup($page); + $s_age = array_search($s_utime, $backups); + + if (is_int($s_age)) { + if ($is_page && $action != 'diff') + $body .= ' <li>' . str_replace('$1', '<a href="' . $href . + '&action=diff">' . $_msg_diff . '</a>', + $_msg_view) . '</li>' . "\n"; + + if ($is_page && $action != 'nowdiff') + $body .= ' <li>' . str_replace('$1', '<a href="' . $href . + '&action=nowdiff">' . $_msg_nowdiff . '</a>', + $_msg_view) . '</li>' . "\n"; + + if ($action != 'source') + $body .= ' <li>' . str_replace('$1', '<a href="' . $href . + '&action=source">' . $_msg_source . '</a>', + $_msg_view) . '</li>' . "\n"; + + if ($action) + $body .= ' <li>' . str_replace('$1', '<a href="' . $href . + '">' . $_msg_backup . '</a>', + $_msg_view) . '</li>' . "\n"; + } if ($is_page) { $body .= ' <li>' . str_replace('$1', @@ -72,69 +88,71 @@ $body .= ' <li>' . str_replace('$1', $s_page, $_msg_deleted) . "\n"; } - $backups = get_backup($page); if (! empty($backups)) { - $body .= ' <ul>' . "\n"; - foreach($backups as $age => $val) { - $date = format_date($val['time'], TRUE); - $body .= ($age == $s_age) ? - ' <li><em>' . $age . ' ' . $date . '</em></li>' . "\n" : - ' <li><a href="' . $script . '?cmd=backup&action=' . - $r_action . '&page=' . $r_page . '&age=' . $age . - '">' . $age . ' ' . $date . '</a></li>' . "\n"; + $body .= ' <ol>' . "\n"; + foreach($backups as $age=>$utime) { + $date = format_date($utime, TRUE); + if ($age == $s_age) { + $body .= ' <li><em>' . $date . '</em></li>' . "\n"; + } else { + $body .= ' <li><a href="' . $script . '?cmd=backup&action=' . + $r_action . '&page=' . $r_page . '&age=' . $utime . + '">' . $date . '</a></li>' . "\n"; + } } - $body .= ' </ul>' . "\n"; + $body .= ' </ol>' . "\n"; } $body .= ' </li>' . "\n"; $body .= '</ul>' . "\n"; - if ($action == 'diff') { + if (!is_int($s_age)) { + $title = str_replace('(No.$2)', '', $_title_backup); + $body .= str_replace('$1', format_date($s_utime, TRUE), $_msg_nobackup); + } else if ($action == 'diff') { $title = & $_title_backupdiff; - $old = ($s_age > 1) ? join('', $backups[$s_age - 1]['data']) : ''; - $cur = join('', $backups[$s_age]['data']); + $old = $s_age > 0 ? get_backup($page, $backups[$s_age-1]) : ''; + $cur = get_backup($page, $s_utime); $body .= plugin_backup_diff(do_diff($old, $cur)); - } else if ($s_action == 'nowdiff') { + } else if ($action == 'nowdiff') { $title = & $_title_backupnowdiff; - $old = join('', $backups[$s_age]['data']); - $cur = join('', get_source($page)); + $old = get_backup($page, $s_utime); + $cur = implode('', get_source($page)); $body .= plugin_backup_diff(do_diff($old, $cur)); - } else if ($s_action == 'source') { + } else if ($action == 'source') { $title = & $_title_backupsource; - $body .= '<pre>' . htmlspecialchars(join('', $backups[$s_age]['data'])) . - '</pre>' . "\n"; + $body .= '<pre>' . htmlspecialchars(get_backup($page, $s_utime)) . '</pre>' . "\n"; } else { if (PLUGIN_BACKUP_DISABLE_BACKUP_RENDERING) { die_message('This feature is prohibited'); } else { $title = & $_title_backup; $body .= $hr . "\n" . - drop_submit(convert_html($backups[$s_age]['data'])); + drop_submit(convert_html(get_backup($page, $s_utime))); } } - return array('msg'=>str_replace('$2', $s_age, $title), 'body'=>$body); + return array('msg'=>str_replace('$2', $s_age+1, $title), 'body'=>$body); } // Delete backup -function plugin_backup_delete($page) +function plugin_backup_delete($page, $s_utime) { global $vars, $_title_backup_delete, $_title_pagebackuplist, $_msg_backup_deleted; global $_msg_backup_adminpass, $_btn_delete, $_msg_invalidpass; - if (! _backup_file_exists($page)) + if (! backup_file_exists($page)) return array('msg'=>$_title_pagebackuplist, 'body'=>plugin_backup_get_list($page)); // Say "is not found" $body = ''; if (isset($vars['pass'])) { if (pkwk_login($vars['pass'])) { - _backup_delete($page); - return array( - 'msg' => $_title_backup_delete, - 'body' => str_replace('$1', make_pagelink($page), $_msg_backup_deleted) - ); - } else { - $body = '<p><strong>' . $_msg_invalidpass . '</strong></p>' . "\n"; + if(backup_delete($page, $s_utime)) + $body = str_replace('$1', make_pagelink($page), $_msg_backup_deleted); + else + $body = '削除失敗'; + return array('msg' => $_title_backup_delete, 'body' => $body); } + $body = '<p><strong>' . $_msg_invalidpass . '</strong></p>' . "\n"; } $script = get_script_uri(); @@ -145,6 +163,7 @@ <div> <input type="hidden" name="cmd" value="backup" /> <input type="hidden" name="page" value="$s_page" /> + <input type="hidden" name="age" value="$s_utime" /> <input type="hidden" name="action" value="delete" /> <input type="password" name="pass" size="12" /> <input type="submit" name="ok" value="$_btn_delete" /> @@ -161,7 +180,7 @@ $str = htmlspecialchars($str); $str = preg_replace('/^(\-)(.*)$/m', '<span class="diff_removed"> $2</span>', $str); $str = preg_replace('/^(\+)(.*)$/m', '<span class="diff_added"> $2</span>', $str); - $str = trim($str); + $str = rtrim($str); $str = <<<EOD $hr <ul> @@ -189,13 +208,15 @@ <ul> EOD; $retval[1] = "\n"; - $retval[2] = <<<EOD - </ul> + $retval[2] = " </ul>\n <ol>\n"; + $retval[3] = ''; + $retval[4] = <<<EOD + </ol> </li> </ul> EOD; - $backups = _backup_file_exists($page) ? get_backup($page) : array(); + $backups = get_backup($page); if (empty($backups)) { $msg = str_replace('$1', make_pagelink($page), $_msg_nobackup); $retval[1] .= ' <li>' . $msg . '</li>' . "\n"; @@ -209,17 +230,18 @@ $href = $script . '?cmd=backup&page=' . $r_page . '&age='; $_anchor_from = $_anchor_to = ''; - foreach ($backups as $age=>$data) { + foreach ($backups as $utime) { if (! PLUGIN_BACKUP_DISABLE_BACKUP_RENDERING) { - $_anchor_from = '<a href="' . $href . $age . '">'; + $_anchor_from = '<a href="' . $href . $utime . '">'; $_anchor_to = '</a>'; } - $date = format_date($data['time'], TRUE); - $retval[1] .= <<<EOD - <li>$_anchor_from$age $date$_anchor_to - [ <a href="$href$age&action=diff">$_msg_diff</a> - | <a href="$href$age&action=nowdiff">$_msg_nowdiff</a> - | <a href="$href$age&action=source">$_msg_source</a> + $date = format_date((int)$utime, TRUE); + $retval[3] .= <<<EOD + <li>$_anchor_from$date$_anchor_to + [ <a href="$href$utime&action=diff">$_msg_diff</a> + | <a href="$href$utime&action=nowdiff">$_msg_nowdiff</a> + | <a href="$href$utime&action=source">$_msg_source</a> + | <a href="$href$utime&action=delete">削除</a> ] </li> EOD; @@ -233,12 +255,60 @@ { global $cantedit; - $pages = array_diff(get_existpages(BACKUP_DIR, BACKUP_EXT), $cantedit); + $pages = array_diff(backup_file_exists(), $cantedit); if (empty($pages)) { return ''; } else { return page_list($pages, 'backup', $withfilename); } +} + +function plugin_backup_convert($action) +{ + global $vars, $_msg_invalidpass; + + $title_backup_convert = 'バックアップ方式の変更'; + $msg_import = '標準バックアップ形式からインポートする'; + $msg_export = '標準バックアップ形式にエクスポートする'; + $msg_adminpass = '管理者パスワード'; + $msg_complete = '変更終了'; + $btn_submit = '実行'; + $body = ''; + if (isset($vars['pass'])) { + if (pkwk_login($vars['pass'])) { + if($action == 'import'){ + backup_import(FALSE); + }else{ + backup_export(FALSE); + } + return array('msg' => $title_backup_convert, 'body' => $msg_complete); + } + $body = '<p><strong>' . $_msg_invalidpass . '</strong></p>' . "\n"; + } + + if($action == 'import'){ + $check_import = ' checked="checked"'; + $check_export = ''; + }else{ + $check_import = ''; + $check_export = ' checked="checked"'; + } + $script = get_script_uri(); + $body .= <<<EOD +<form action="$script" method="post"> + <div> + <input type="hidden" name="cmd" value="backup" /> + <input type="radio" name="action" value="import" id="_p_backup_import"$check_import /> + <label for="_p_backup_import">$msg_import</label><br /> + <input type="radio" name="action" value="export" id="_p_backup_export"$check_export /> + <label for="_p_backup_export">$msg_export</label><br /> + <label for="_p_backup_adminpass">$msg_adminpass</label> + <input type="password" name="pass" size="12" id="_p_backup_adminpass" /> + <input type="submit" name="ok" value="$btn_submit" /> + </div> +</form> +EOD; + return array('msg'=>$title_backup_convert, 'body'=>$body); } ?>
backup.phpは差分の方が長くなるので…
<?php /** * * PukiWiki - Yet another WikiWikiWeb clone. * * backup.php * * バックアップを管理する * * @package org.pukiwiki * @access public * @author * @create * @version $Id: backup.php,v 1.9 2005/04/30 05:21:00 henoheno Exp $ * Copyright (C) * 2002-2005 PukiWiki Developers Team * 2001-2002 Originally written by yu-ji * License: GPL v2 or (at your option) any later version **/ if(extension_loaded('zlib')){ define('BACKUP_EXT', '.gz'); if(version_compare(PHP_VERSION, '4.3.0') < 0) define('BACKUP_PROTOCOL_WRAPPER', 'zlib:'); else define('BACKUP_PROTOCOL_WRAPPER', 'compress.zlib://'); }else{ define('BACKUP_EXT', '.txt'); define('BACKUP_PROTOCOL_WRAPPER', ''); } /** * make_backup * バックアップを作成する * * @access public * @param String $page ページ名 * @param Boolean $delete TRUE:バックアップを削除する * * @return Void */ function make_backup($page, $delete = FALSE) { global $cycle, $maxage; global $do_backup, $del_backup; if (PKWK_READONLY || ! $do_backup) return; if (($del_backup && $delete) || $maxage < 1) { backup_delete($page); return; } if (! is_page($page)) return; $lastmod = _backup_get_filetime($page); if ($lastmod == 0 || UTIME - $lastmod > 60 * 60 * $cycle) { $filename = _backup_get_filename($page); $die_msg = 'cannot write file ' . htmlspecialchars($filename) . '<br />maybe permission is not writable or filename is too long'; do{ if(($lock = @fopen($filename, 'r')) !== FALSE){ flock($lock, LOCK_EX); clearstatcache(); if(_backup_get_filetime($page) != $lastmod) break; $fp = @fopen(BACKUP_PROTOCOL_WRAPPER . $filename, 'rb') or die_message('cannot read file ' . htmlspecialchars($filename)); // .gzのfreadは解凍後のサイズ分読み込むのでループ必須 for($data = ''; !feof($fp); $data .= fread($fp, 1048576)); fclose($fp); $regex_splitter = '/^' . preg_quote(PKWK_SPLITTER, '/') . ' (\d+)\n/m'; $data = preg_split($regex_splitter, $data, -1, PREG_SPLIT_DELIM_CAPTURE); // 最初のsplitterより前のデータを捨てる(通常は空文字列) array_shift($data); $count = (count($data) >> 1) + 1; // 直後に1件追加するので、(最大件数 - 1)を超える要素を捨てる if ($count > $maxage) array_splice($data, 0, ($count - $maxage) << 1); }else{ $lock = fopen($filename, 'a+') or die_message($die_msg); flock($lock, LOCK_EX); clearstatcache(); if(filesize($filename) != 0) break; $data = array(); } $regex_splitter = '/^(' . preg_quote(PKWK_SPLITTER, '/') . ' \d+)$/'; // 暫定的に差分から前回の更新日時を得る $utime = filemtime(DIFF_DIR . encode($page) . '.txt') - LOCALZONE; // Escape 'lines equal to PKWK_SPLITTER', by inserting a space $body = PKWK_SPLITTER . ' ' . $utime . "\n"; $body .= rtrim(implode('', preg_replace($regex_splitter, '$1 ', get_source($page)))) . "\n"; $fp = fopen(BACKUP_PROTOCOL_WRAPPER . $filename, 'wb') or die_message($die_msg); while(!empty($data)) { fputs($fp, PKWK_SPLITTER . ' ' . array_shift($data) . "\n"); // Splitter format fputs($fp, array_shift($data)); } fputs($fp, $body); fclose($fp); _backup_set_filetime($page, UTIME); }while(FALSE); flock($lock, LOCK_UN); fclose($lock); } } /** * get_backup * バックアップを取得する * $utime = 0または省略 : 全てのバックアップ作成日時を配列で取得する * $utime > 0 : 指定した世代のバックアップデータを取得する * * @access public * @param String $page ページ名 * @param Integer $utime バックアップの作成日時 * * @return String バックアップ ($utime > 0) * Array バックアップ時刻の配列 ($utime == 0) */ function get_backup($page, $utime = 0) { static $c_page, $data, $lists; if($page != $c_page){ $c_page = $page; $data = $lists = array(); $regex_splitter = '/^' . preg_quote(PKWK_SPLITTER, '/') . ' (\d+)\n/m'; $filename = _backup_get_filename($page); if(($lock = @fopen($filename, 'r')) === FALSE) return $utime == 0 ? array() : ''; flock($lock, LOCK_SH); $fp = fopen(BACKUP_PROTOCOL_WRAPPER . $filename, 'rb') or die_message('cannot read file ' . htmlspecialchars($filename)); for($data = ''; !feof($fp); $data .= fread($fp, 1048576)); fclose($fp); flock($lock, LOCK_UN); fclose($lock); $data = preg_split($regex_splitter, $data, -1, PREG_SPLIT_DELIM_CAPTURE); foreach($data as $key=>$val){ if($key & 1){ unset($data[$key]); $lists[] = (int)$val; } } array_shift($data); } if($utime == 0){ return $lists; } $key = array_search($utime, $lists); return is_int($key) ? $data[$key] : ''; } /** * backup_file_exists * バックアップファイルが存在するか * * @access public * @param String $page ページ名 * * @return Boolean TRUE:ある FALSE:ない * Array バックアップの存在するページの配列(ページ名省略時) */ function backup_file_exists($page = NULL) { if(isset($page)) return file_exists(_backup_get_filename($page)); return get_existpages(BACKUP_DIR, BACKUP_EXT); } /** * backup_delete * バックアップファイルを削除する * * @access public * @param String $page ページ名 * @param Integer $utime バックアップの作成日時 * * @return Boolean FALSE:失敗 */ function backup_delete($page, $utime = NULL) { $filename = _backup_get_filename($page); if(!isset($utime)) return @unlink($filename); if(($lock = @fopen($filename, 'r')) === FALSE) return FALSE; flock($lock, LOCK_EX); do{ $success = FALSE; $regex_splitter = '/^' . preg_quote(PKWK_SPLITTER, '/') . ' (\d+)\n/m'; $filename = _backup_get_filename($page); if(($fp = @fopen(BACKUP_PROTOCOL_WRAPPER . $filename, 'rb')) === FALSE) break; for($data = ''; !feof($fp); $data .= fread($fp, 1048576)); fclose($fp); $data = preg_split($regex_splitter, $data, -1, PREG_SPLIT_DELIM_CAPTURE); array_shift($data); $lists = array(); foreach($data as $key=>$val){ if(($key & 1) == 0) $lists[$key] = (int)$val; } $key = array_search($utime, $lists); if(!is_int($key)) break; array_splice($data, $key, 2); $filetime = _backup_get_filetime($page); if(($fp = @fopen(BACKUP_PROTOCOL_WRAPPER . $filename, 'wb')) === FALSE) break; while(!empty($data)) { fputs($fp, PKWK_SPLITTER . ' ' . array_shift($data) . "\n"); // Splitter format fputs($fp, array_shift($data)); } fclose($fp); $success = _backup_set_filetime($page, $filetime); }while(FALSE); flock($lock, LOCK_UN); fclose($lock); return $success; } /** * backup_import * バックアップ形式を標準データ形式からインポートする * * @access public * @param Boolean $keep_data 元データを削除しない * * @return Void */ function backup_import($keep_data) { if(BACKUP_EXT == '.txt') die_message('this backup system is standard data format'); if(ini_get('safe_mode') == '0') set_time_limit(0); foreach(get_existpages(BACKUP_DIR, '.txt') as $page){ $src = BACKUP_DIR . encode($page) . '.txt'; $dist = BACKUP_DIR . encode($page) . BACKUP_EXT; if(file_exists($dist)){ error_log('PukiWiki Notice: ' . htmlspecialchars($src) . ' is skipped.', 0); continue; } $f_src = @fopen($src, 'rb') or die_message('cannot read file ' . htmlspecialchars($src)); $f_dist = @fopen(BACKUP_PROTOCOL_WRAPPER . $dist, 'wb') or die_message('cannot write file ' . htmlspecialchars($dist)); while(!feof($f_src)) fputs($f_dist, fread($f_src, 1048576)); fclose($f_dist); fclose($f_src); touch($dist, filemtime($src)); if(!$keep_data && !@unlink($src)) die_message('cannot remove file ' . htmlspecialchars($src)); } } /** * backup_export * バックアップ形式を標準データ形式にエクスポートする * * @access public * @param Boolean $keep_data 元データを削除しない * * @return Void */ function backup_export($keep_data) { if(BACKUP_EXT == '.txt') die_message('this backup system is standard data format'); if(ini_get('safe_mode') == '0') set_time_limit(0); $regex_splitter = '/^' . preg_quote(PKWK_SPLITTER, '/') . ' (\d+)\n/m'; foreach(get_existpages(BACKUP_DIR, BACKUP_EXT) as $page){ $src = BACKUP_DIR . encode($page) . BACKUP_EXT; $dist = BACKUP_DIR . encode($page) . '.txt'; if(file_exists($dist)){ error_log('PukiWiki Notice: ' . htmlspecialchars($src) . ' is skipped.', 0); continue; } $f_src = @fopen(BACKUP_PROTOCOL_WRAPPER . $src, 'rb') or die_message('cannot read file ' . htmlspecialchars($src)); $f_dist = @fopen($dist, 'wb') or die_message('cannot write file ' . htmlspecialchars($dist)); $buf = $utime = NULL; $time_lists = array(); while(!feof($f_src)){ $buf .= fread($f_src, 1048576); $data = preg_split($regex_splitter, $buf, -1, PREG_SPLIT_DELIM_CAPTURE); if(!feof($f_src)){ $buf = array_pop($data); }else{ unset($buf); } if(isset($utime)){ array_unshift($data, $utime); }else if(!empty($data)){ // 最初のsplitterより前のデータを捨てる array_shift($data); } while(!empty($data)){ $utime = (int)array_shift($data); while(in_array($utime, $time_lists)) $utime++; // 同一タイプスタンプがある場合、1秒を加算 if(!empty($data)){ $time_lists[] = $utime; fputs($f_dist, PKWK_SPLITTER . ' ' . $utime . "\n"); fputs($f_dist, array_shift($data)); } } } fclose($f_dist); fclose($f_src); touch($dist, filemtime($src)); if(!$keep_data && !@unlink($src)) die_message('cannot remove file ' . htmlspecialchars($src)); } } //////////////// private function follows //////////////// /** * _backup_get_filename * バックアップファイル名を取得する * * @access private * @param String $page ページ名 * * @return String バックアップのファイル名 */ function _backup_get_filename($page) { return BACKUP_DIR . encode($page) . BACKUP_EXT; } /** * _backup_get_filetime * バックアップファイルの更新時刻を得る * * @access private * @param String $page ページ名 * * @return Integer ファイルの更新時刻(GMT) */ function _backup_get_filetime($page) { if (!backup_file_exists($page) || ($time = filemtime(_backup_get_filename($page))) === FALSE) return 0; return $time - LOCALZONE; } /** * _backup_set_filetime * バックアップファイルの更新時刻をセットする * * @access private * @param String $page ページ名 * @param Integer $utime ファイルの更新時刻(GMT) * * @return Boolean FALSE:失敗 */ function _backup_set_filetime($page, $utime) { return touch(_backup_get_filename($page), $utime + LOCALZONE); } ?>
ディレクトリで管理するタイプのbackup.php
<?php /** * * PukiWiki - Yet another WikiWikiWeb clone. * * backup.php * * バックアップをディレクトリで管理する by Cue * * @package org.pukiwiki * @access public * @author * @create * @version $Id: backup.php,v 1.9 2005/04/30 05:21:00 henoheno Exp $ * Copyright (C) * 2002-2005 PukiWiki Developers Team * 2001-2002 Originally written by yu-ji * License: GPL v2 or (at your option) any later version **/ define('BACKUP_DIR_PERMISSION', 0777); if(extension_loaded('zlib')){ define('BACKUP_EXT', '.gz'); if(version_compare(PHP_VERSION, '4.3.0') < 0) define('BACKUP_PROTOCOL_WRAPPER', 'zlib:'); else define('BACKUP_PROTOCOL_WRAPPER', 'compress.zlib://'); }else{ define('BACKUP_EXT', '.txt'); define('BACKUP_PROTOCOL_WRAPPER', ''); } /** * make_backup * バックアップを作成する * * @access public * @param String $page ページ名 * Boolean $delete TRUE:バックアップを削除する * * @return Void */ function make_backup($page, $delete = FALSE) { global $cycle, $maxage; global $do_backup, $del_backup; if (PKWK_READONLY || ! $do_backup) return; if (($del_backup && $delete) || $maxage < 1) { backup_delete($page); return; } if (! is_page($page)) return; $backup = &new backup($page); $lastmod = $backup->get_lastmod(); if(UTIME > $lastmod + 60 * 60 * $cycle){ if($lastmod == 0 && !$backup->create_dir()) die_message('cannot create directory ' . htmlspecialchars($backup->dirname) . '<br />maybe permission is not writable or filename is too long'); for($backup_lists = $backup->lists(); count($backup_lists) >= $maxage; $backup->delete(array_shift($backup_lists)) ); // 暫定的に差分から前回の更新日時を得る $utime = filemtime(DIFF_DIR . encode($page) . '.txt') - LOCALZONE; $backup->append(implode('', get_source($page)), $utime); } } /** * get_backup * バックアップを取得する * $utime = 0または省略 : 全てのバックアップ作成時刻を配列で取得する * $utime > 0 : 指定した時刻のバックアップデータを取得する * * @access public * @param String $page ページ名 * Integer $utime バックアップの作成時刻 * * @return String バックアップ ($utime != 0) * Array バックアップ時刻の配列 ($utime == 0) */ function get_backup($page, $utime = 0) { $backup = &new backup($page); if($utime == 0){ return array_keys($backup->lists()); } $lines = $backup->read($utime); return $lines !== FALSE ? implode('', $lines) : ''; } /** * backup_file_exists * バックアップが存在するか * * @access public * @param String $page ページ名 * * @return Boolean TRUE:ある FALSE:ない * Array バックアップの存在するページ名の配列(ページ名省略時) */ function backup_file_exists($page = NULL) { if(isset($page)){ $backup = &new backup($page); return file_exists($backup->dirname); } return get_existpages(BACKUP_DIR, ''); } /** * backup_delete * バックアップを削除する * * @access public * @param String $page ページ名 * Integer $utime バックアップ作成時刻(省略時は全て) * * @return Boolean FALSE:失敗 */ function backup_delete($page, $utime = NULL) { $backup = &new backup($page); return $backup->delete($utime); } /** * backup_import * バックアップ形式を標準データ形式からインポートする * * @access public * @param Boolean $keep_data 元データを削除しない * * @return Void */ function backup_import($keep_data) { if(ini_get('safe_mode') == '0') set_time_limit(0); $regex_splitter = '/^' . preg_quote(PKWK_SPLITTER, '/') . ' (\d+)\n/m'; foreach(get_existpages(BACKUP_DIR, '.txt') as $page){ $import_file = BACKUP_DIR . encode($page) . '.txt'; $backup = &new backup($page); if(file_exists($backup->dirname)){ error_log('PukiWiki Notice: ' . htmlspecialchars($import_file) . ' is skipped.', 0); continue; } if(!$backup->create_dir()) die_message('cannot create directory ' . htmlspecialchars($backup->dirname) . '<br />maybe permission is not writable or filename is too long'); $fp = @fopen($import_file, 'rb') or die_message('cannot read file ' . htmlspecialchars($import_file)); $buf = $utime = NULL; while(!feof($fp)){ $buf .= fread($fp, 1048576); $data = preg_split($regex_splitter, $buf, -1, PREG_SPLIT_DELIM_CAPTURE); if(!feof($fp)){ $buf = array_pop($data); }else{ unset($buf); } if(isset($utime)){ array_unshift($data, $utime); }else if(!empty($data)){ // 最初のsplitterより前のデータを捨てる(通常は空文字列) array_shift($data); } while(!empty($data)){ $utime = (int)array_shift($data); while(file_exists($backup->filename($utime))) $utime++; // 同一タイプスタンプがある場合、1秒を加算 if(!empty($data)) $backup->append(rtrim(array_shift($data))."\n", $utime); } } fclose($fp); $lists = $backup->lists(); if(empty($lists)) die_message('empty data file found ' . htmlspecialchars($import_file)); $backup->set_lastmod(filemtime($import_file) - LOCALZONE); if(!$keep_data && !@unlink($import_file)) die_message('cannot remove file ' . htmlspecialchars($import_file)); } } /** * backup_export * バックアップ形式を標準データ形式にエクスポートする * * @access public * @param Boolean $keep_data 元データを削除しない * * @return Void */ function backup_export($keep_data) { if(ini_get('safe_mode') == '0') set_time_limit(0); $regex_splitter = '/^(' . preg_quote(PKWK_SPLITTER, '/') . ' \d+)$/'; foreach(get_existpages(BACKUP_DIR, '') as $page){ $export_file = BACKUP_DIR . encode($page) . '.txt'; if(file_exists($export_file)){ error_log('PukiWiki Notice: ' . htmlspecialchars($export_file) . ' is skipped.', 0); continue; } $fp = @fopen($export_file, 'wb') or die_message('cannot write file ' . htmlspecialchars($export_file)); $backup = &new backup($page); foreach(array_keys($backup->lists()) as $utime){ fputs($fp, PKWK_SPLITTER . ' ' . $utime . "\n"); $lines = $backup->read($utime) or die_message('cannot read file ' . htmlspecialchars($backup->filename($utime))); fputs($fp, implode('', preg_replace($regex_splitter, '$1 ', $lines))); } fclose($fp); touch($export_file, $backup->get_lastmod() + LOCALZONE); if(!$keep_data && !$backup->delete()) die_message('cannot remove directory ' . htmlspecialchars($backup->dirname)); } } //////////////// private function follows //////////////// class backup { var $page; var $dirname; function backup($page){ $this->page = $page; $this->dirname = BACKUP_DIR . encode($this->page) . '/'; } function filename($utime){ return $this->dirname . $utime . BACKUP_EXT; } function get_lastmod(){ $time = @filemtime($this->dirname . 'index.html'); return $time !== FALSE ? $time - LOCALZONE : 0; } function set_lastmod($utime){ return touch($this->dirname . 'index.html', $utime + LOCALZONE); } function create_dir(){ mkdir($this->dirname); chmod($this->dirname, BACKUP_DIR_PERMISSION); if(($fp = @fopen($this->dirname . 'index.html', 'wb')) === FALSE) return FALSE; fputs($fp, "backup files are placed here.\n"); fclose($fp); return TRUE; } function lists(){ $lists = $matches = array(); if(($dp = @opendir($this->dirname)) !== FALSE){ while($filename = readdir($dp)){ if(preg_match('/^(\d+)'.preg_quote(BACKUP_EXT, '/').'$/', $filename, $matches)) $lists[(int)$matches[1]] = $this->dirname . $filename; } closedir($dp); ksort($lists); } return $lists; } function delete($utime = NULL){ if(isset($utime)) return @unlink($this->filename($utime)); $success = TRUE; foreach($this->lists() as $filename) $success &= @unlink($filename); if($success) $success &= @unlink($this->dirname . 'index.html'); return $success ? @rmdir($this->dirname) : FALSE; } function read($utime){ return @file(BACKUP_PROTOCOL_WRAPPER . $this->filename($utime)); } function append($body, $utime){ $filename = $this->filename($utime); $die_msg = 'cannot write file ' . htmlspecialchars($filename) . '<br />maybe permission is not writable or filename is too long'; $lock = @fopen($filename, 'a+') or die_message($die_msg); flock($lock, LOCK_EX); clearstatcache(); if(filesize($filename) === 0){ $fp = @fopen(BACKUP_PROTOCOL_WRAPPER . $filename, 'wb') or die_message($die_msg); fputs($fp, $body); fclose($fp); $this->set_lastmod(UTIME); } flock($lock, LOCK_UN); fclose($lock); } } ?>