X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=lib%2Ffile.php;h=b90a2f8678de5b4eaf8b5801f7a14fbc90dcb7dd;hb=ef13260aa62b4b4d6d7da5f9c85a568eee44aa24;hp=1b54abf3f9dd0dee13205039145da63176ce319c;hpb=b19ceea20b864baa1dcbea9f98fd5c93314c221e;p=pukiwiki%2Fpukiwiki.git diff --git a/lib/file.php b/lib/file.php index 1b54abf..b90a2f8 100644 --- a/lib/file.php +++ b/lib/file.php @@ -1,29 +1,61 @@ = $count) break; + } + if ($lock) flock($fp, LOCK_UN); + if (! fclose($fp)) return FALSE; + + return $array; +} + // Output to a file -function file_write($dir, $page, $str, $notimestamp = FALSE) +function file_write($dir, $page, $str, $notimestamp = FALSE, $is_delete = FALSE) { - global $update_exec, $_msg_invalidiwn, $notify, $notify_diff_only, $notify_subject; + global $_msg_invalidiwn, $notify, $notify_diff_only, $notify_subject; global $whatsdeleted, $maxshow_deleted; if (PKWK_READONLY) return; // Do nothing + if ($dir != DATA_DIR && $dir != DIFF_DIR) die('file_write(): Invalid directory'); - if (! is_pagename($page)) - die_message(str_replace('$1', htmlspecialchars($page), - str_replace('$2', 'WikiName', $_msg_invalidiwn))); + $page = strip_bracket($page); + $file = $dir . encode($page) . '.txt'; + $file_exists = file_exists($file); - $page = strip_bracket($page); - $file = $dir . encode($page) . '.txt'; - $timestamp = FALSE; + // ---- + // Delete? - if ($str === '') { - if ($dir == DATA_DIR && file_exists($file)) { - // File deletion - unlink($file); - add_recent($page, $whatsdeleted, '', $maxshow_deleted); // RecentDeleted - } - } else { - // File replacement (Edit) - $str = rtrim(preg_replace('/' . "\r" . '/', '', $str)) . "\n"; + if ($dir == DATA_DIR && $is_delete) { + // Page deletion + if (! $file_exists) return; // Ignore null posting for DATA_DIR - if ($notimestamp && file_exists($file)) - $timestamp = filemtime($file) - LOCALZONE; + // Update RecentDeleted (Add the $page) + add_recent($page, $whatsdeleted, '', $maxshow_deleted); - $fp = fopen($file, 'a') or die('fopen() failed: ' . - htmlspecialchars(basename($dir) . '/' . encode($page) . '.txt') . - '
' . "\n" . - 'Maybe permission is not writable or filename is too long'); - set_file_buffer($fp, 0); + // Remove the page + unlink($file); - flock($fp, LOCK_EX); - - // Write - ftruncate($fp, 0); - rewind($fp); - fputs($fp, $str); + // Update RecentDeleted, and remove the page from RecentChanges + lastmodified_add($whatsdeleted, $page); - flock($fp, LOCK_UN); + // Clear is_page() cache + is_page($page, TRUE); - fclose($fp); + return; - if ($timestamp) pkwk_touch_file($file, $timestamp + LOCALZONE); + } else if ($dir == DIFF_DIR && $str === " \n") { + return; // Ignore null posting for DIFF_DIR } - // Clear is_page() cache - is_page($page, TRUE); + // ---- + // File replacement (Edit) - if (! $timestamp && $dir == DATA_DIR) - put_lastmodified(); + if (! is_pagename($page)) + die_message(str_replace('$1', htmlsc($page), + str_replace('$2', 'WikiName', $_msg_invalidiwn))); - // Execute $update_exec here - if ($update_exec && $dir == DATA_DIR) - system($update_exec . ' > /dev/null &'); + $str = rtrim(preg_replace('/' . "\r" . '/', '', $str)) . "\n"; + $timestamp = ($file_exists && $notimestamp) ? filemtime($file) : FALSE; - if ($notify && $dir == DIFF_DIR) { - if ($notify_diff_only) $str = preg_replace('/^[^-+].*\n/m', '', $str); + $fp = fopen($file, 'a') or die('fopen() failed: ' . + htmlsc(basename($dir) . '/' . encode($page) . '.txt') . + '
' . "\n" . + 'Maybe permission is not writable or filename is too long'); + set_file_buffer($fp, 0); + flock($fp, LOCK_EX); + ftruncate($fp, 0); + rewind($fp); + fputs($fp, $str); + flock($fp, LOCK_UN); + fclose($fp); + + if ($timestamp) pkwk_touch_file($file, $timestamp); + + // Optional actions + if ($dir == DATA_DIR) { + // Update RecentChanges (Add or renew the $page) + if ($timestamp === FALSE) lastmodified_add($page); + + // Command execution per update + if (defined('PKWK_UPDATE_EXEC') && PKWK_UPDATE_EXEC) + system(PKWK_UPDATE_EXEC . ' > /dev/null &'); + } else if ($dir == DIFF_DIR && $notify) { + if ($notify_diff_only) $str = preg_replace('/^[^-+].*\n/m', '', $str); $footer['ACTION'] = 'Page update'; $footer['PAGE'] = & $page; - $footer['URI'] = get_script_uri() . '?' . rawurlencode($page); + $footer['URI'] = get_script_uri() . '?' . pagename_urlencode($page); $footer['USER_AGENT'] = TRUE; $footer['REMOTE_ADDR'] = TRUE; - pkwk_mail_notify($notify_subject, $str, $footer) or die('pkwk_mail_notify(): Failed'); } + + is_page($page, TRUE); // Clear is_page() cache } // Update RecentDeleted @@ -237,7 +335,7 @@ function add_recent($page, $recentpage, $subject = '', $limit = 0) // Add array_unshift($lines, '-' . format_date(UTIME) . ' - ' . $_page . - htmlspecialchars($subject) . "\n"); + htmlsc($subject) . "\n"); // Get latest $limit reports $lines = array_splice($lines, 0, $limit); @@ -245,7 +343,7 @@ function add_recent($page, $recentpage, $subject = '', $limit = 0) // Update $fp = fopen(get_filename($recentpage), 'w') or die_message('Cannot write page file ' . - htmlspecialchars($recentpage) . + htmlsc($recentpage) . '
Maybe permission is not writable or filename is too long'); set_file_buffer($fp, 0); flock($fp, LOCK_EX); @@ -257,30 +355,132 @@ function add_recent($page, $recentpage, $subject = '', $limit = 0) fclose($fp); } -// Update RecentChanges +// Update PKWK_MAXSHOW_CACHE itself (Add or renew about the $page) (Light) +// Use without $autolink +function lastmodified_add($update = '', $remove = '') +{ + global $maxshow, $whatsnew, $autolink; + + // AutoLink implimentation needs everything, for now + if ($autolink) { + put_lastmodified(); // Try to (re)create ALL + return; + } + + if (($update == '' || check_non_list($update)) && $remove == '') + return; // No need + + $file = CACHE_DIR . PKWK_MAXSHOW_CACHE; + if (! file_exists($file)) { + put_lastmodified(); // Try to (re)create ALL + return; + } + + // Open + pkwk_touch_file($file); + $fp = fopen($file, 'r+') or + die_message('Cannot open ' . 'CACHE_DIR/' . PKWK_MAXSHOW_CACHE); + set_file_buffer($fp, 0); + flock($fp, LOCK_EX); + + // Read (keep the order of the lines) + $recent_pages = $matches = array(); + foreach(file_head($file, $maxshow + PKWK_MAXSHOW_ALLOWANCE, FALSE) as $line) + if (preg_match('/^([0-9]+)\t(.+)/', $line, $matches)) + $recent_pages[$matches[2]] = $matches[1]; + + // Remove if it exists inside + if (isset($recent_pages[$update])) unset($recent_pages[$update]); + if (isset($recent_pages[$remove])) unset($recent_pages[$remove]); + + // Add to the top: like array_unshift() + if ($update != '') + $recent_pages = array($update => get_filetime($update)) + $recent_pages; + + // Check + $abort = count($recent_pages) < $maxshow; + + if (! $abort) { + // Write + ftruncate($fp, 0); + rewind($fp); + foreach ($recent_pages as $_page=>$time) + fputs($fp, $time . "\t" . $_page . "\n"); + } + + flock($fp, LOCK_UN); + fclose($fp); + + if ($abort) { + put_lastmodified(); // Try to (re)create ALL + return; + } + + + + // ---- + // Update the page 'RecentChanges' + + $recent_pages = array_splice($recent_pages, 0, $maxshow); + $file = get_filename($whatsnew); + + // Open + pkwk_touch_file($file); + $fp = fopen($file, 'r+') or + die_message('Cannot open ' . htmlsc($whatsnew)); + set_file_buffer($fp, 0); + flock($fp, LOCK_EX); + + // Recreate + ftruncate($fp, 0); + rewind($fp); + foreach ($recent_pages as $_page=>$time) + fputs($fp, '-' . htmlsc(format_date($time)) . + ' - ' . '[[' . htmlsc($_page) . ']]' . "\n"); + fputs($fp, '#norelated' . "\n"); // :) + + flock($fp, LOCK_UN); + fclose($fp); +} + +// Re-create PKWK_MAXSHOW_CACHE (Heavy) function put_lastmodified() { global $maxshow, $whatsnew, $autolink; if (PKWK_READONLY) return; // Do nothing + // Get WHOLE page list $pages = get_existpages(); + + // Check ALL filetime $recent_pages = array(); foreach($pages as $page) - if ($page != $whatsnew && ! check_non_list($page)) + if ($page !== $whatsnew && ! check_non_list($page)) $recent_pages[$page] = get_filetime($page); // Sort decending order of last-modification date arsort($recent_pages, SORT_NUMERIC); - // Create recent.dat (for recent.inc.php) - $fp = fopen(CACHE_DIR . 'recent.dat', 'w') or - die_message('Cannot write cache file ' . - CACHE_DIR . 'recent.dat' . - '
Maybe permission is not writable or filename is too long'); + // Cut unused lines + // BugTrack2/179: array_splice() will break integer keys in hashtable + $count = $maxshow + PKWK_MAXSHOW_ALLOWANCE; + $_recent = array(); + foreach($recent_pages as $key=>$value) { + unset($recent_pages[$key]); + $_recent[$key] = $value; + if (--$count < 1) break; + } + $recent_pages = & $_recent; + // Re-create PKWK_MAXSHOW_CACHE + $file = CACHE_DIR . PKWK_MAXSHOW_CACHE; + pkwk_touch_file($file); + $fp = fopen($file, 'r+') or + die_message('Cannot open' . 'CACHE_DIR/' . PKWK_MAXSHOW_CACHE); set_file_buffer($fp, 0); flock($fp, LOCK_EX); + ftruncate($fp, 0); rewind($fp); foreach ($recent_pages as $page=>$time) fputs($fp, $time . "\t" . $page . "\n"); @@ -288,22 +488,18 @@ function put_lastmodified() fclose($fp); // Create RecentChanges - $fp = fopen(get_filename($whatsnew), 'w') or - die_message('Cannot write page file ' . - htmlspecialchars($whatsnew) . - '
Maybe permission is not writable or filename is too long'); - + $file = get_filename($whatsnew); + pkwk_touch_file($file); + $fp = fopen($file, 'r+') or + die_message('Cannot open ' . htmlsc($whatsnew)); set_file_buffer($fp, 0); flock($fp, LOCK_EX); + ftruncate($fp, 0); rewind($fp); - - // BugTrack2/106: Only variables can be passed by reference from PHP 5.0.5 - $tmp_array = array_keys($recent_pages); // with array_splice() - - foreach (array_splice($tmp_array, 0, $maxshow) as $page) { + foreach (array_keys($recent_pages) as $page) { $time = $recent_pages[$page]; - $s_lastmod = htmlspecialchars(format_date($time)); - $s_page = htmlspecialchars($page); + $s_lastmod = htmlsc(format_date($time)); + $s_page = htmlsc($page); fputs($fp, '-' . $s_lastmod . ' - [[' . $s_page . ']]' . "\n"); } fputs($fp, '#norelated' . "\n"); // :) @@ -315,12 +511,13 @@ function put_lastmodified() list($pattern, $pattern_a, $forceignorelist) = get_autolink_pattern($pages); - $fp = fopen(CACHE_DIR . 'autolink.dat', 'w') or - die_message('Cannot write autolink file ' . - CACHE_DIR . '/autolink.dat' . - '
Maybe permission is not writable'); + $file = CACHE_DIR . PKWK_AUTOLINK_REGEX_CACHE; + pkwk_touch_file($file); + $fp = fopen($file, 'r+') or + die_message('Cannot open ' . 'CACHE_DIR/' . PKWK_AUTOLINK_REGEX_CACHE); set_file_buffer($fp, 0); flock($fp, LOCK_EX); + ftruncate($fp, 0); rewind($fp); fputs($fp, $pattern . "\n"); fputs($fp, $pattern_a . "\n"); @@ -330,7 +527,7 @@ function put_lastmodified() } } -// Get elapsed date of the pate +// Get elapsed date of the page function get_pg_passage($page, $sw = TRUE) { global $show_passage; @@ -354,21 +551,36 @@ function header_lastmod($page = NULL) } } +// Get a list of encoded files (must specify a directory and a suffix) +function get_existfiles($dir = DATA_DIR, $ext = '.txt') +{ + $aryret = array(); + $pattern = '/^(?:[0-9A-F]{2})+' . preg_quote($ext, '/') . '$/'; + + $dp = @opendir($dir) or die_message($dir . ' is not found or not readable.'); + while (($file = readdir($dp)) !== FALSE) { + if (preg_match($pattern, $file)) { + $aryret[] = $dir . $file; + } + } + closedir($dp); + + return $aryret; +} + // Get a page list of this wiki function get_existpages($dir = DATA_DIR, $ext = '.txt') { $aryret = array(); + $pattern = '/^((?:[0-9A-F]{2})+)' . preg_quote($ext, '/') . '$/'; - $pattern = '((?:[0-9A-F]{2})+)'; - if ($ext != '') $ext = preg_quote($ext, '/'); - $pattern = '/^' . $pattern . $ext . '$/'; - - $dp = @opendir($dir) or - die_message($dir . ' is not found or not readable.'); + $dp = @opendir($dir) or die_message($dir . ' is not found or not readable.'); $matches = array(); - while ($file = readdir($dp)) - if (preg_match($pattern, $file, $matches)) + while (($file = readdir($dp)) !== FALSE) { + if (preg_match($pattern, $file, $matches)) { $aryret[$file] = decode($matches[1]); + } + } closedir($dp); return $aryret; @@ -422,7 +634,7 @@ function get_readings() if(! file_exists($pagereading_chasen_path)) die_message('ChaSen not found: ' . $pagereading_chasen_path); - $tmpfname = tempnam(CACHE_DIR, 'PageReading'); + $tmpfname = tempnam(realpath(CACHE_DIR), 'PageReading'); $fp = fopen($tmpfname, 'w') or die_message('Cannot write temporary file "' . $tmpfname . '".' . "\n"); foreach ($readings as $page => $reading) { @@ -458,7 +670,7 @@ function get_readings() if(! file_exists($pagereading_kakasi_path)) die_message('KAKASI not found: ' . $pagereading_kakasi_path); - $tmpfname = tempnam(CACHE_DIR, 'PageReading'); + $tmpfname = tempnam(realpath(CACHE_DIR), 'PageReading'); $fp = fopen($tmpfname, 'w') or die_message('Cannot write temporary file "' . $tmpfname . '".' . "\n"); foreach ($readings as $page => $reading) { @@ -517,7 +729,7 @@ function get_readings() if($unknownPage || $deletedPage) { - asort($readings); // Sort by pronouncing(alphabetical/reading) order + asort($readings, SORT_STRING); // Sort by pronouncing(alphabetical/reading) order $body = ''; foreach ($readings as $page => $reading) $body .= '-[[' . $page . ']] ' . $reading . "\n"; @@ -535,19 +747,6 @@ function get_readings() return $readings; } -// Get a list of encoded files (must specify a directory and a suffix) -function get_existfiles($dir, $ext) -{ - $pattern = '/^(?:[0-9A-F]{2})+' . preg_quote($ext, '/') . '$/'; - $aryret = array(); - $dp = @opendir($dir) or die_message($dir . ' is not found or not readable.'); - while ($file = readdir($dp)) - if (preg_match($pattern, $file)) - $aryret[] = $dir . $file; - closedir($dp); - return $aryret; -} - // Get a list of related pages of the page function links_get_related($page) { @@ -557,7 +756,7 @@ function links_get_related($page) if (isset($links[$page])) return $links[$page]; // If possible, merge related pages generated by make_link() - $links[$page] = ($page == $vars['page']) ? $related : array(); + $links[$page] = ($page === $vars['page']) ? $related : array(); // Get repated pages from DB $links[$page] += links_get_related_db($vars['page']); @@ -583,12 +782,12 @@ function pkwk_chown($filename, $preserve_time = TRUE) $lockfile = CACHE_DIR . 'pkwk_chown.lock'; $flock = fopen($lockfile, 'a') or die('pkwk_chown(): fopen() failed for: CACHEDIR/' . - basename(htmlspecialchars($lockfile))); + basename(htmlsc($lockfile))); flock($flock, LOCK_EX) or die('pkwk_chown(): flock() failed for lock'); // Check owner $stat = stat($filename) or - die('pkwk_chown(): stat() failed for: ' . basename(htmlspecialchars($filename))); + die('pkwk_chown(): stat() failed for: ' . basename(htmlsc($filename))); if ($stat[4] === $php_uid) { // NOTE: Windows always here $result = TRUE; // Seems the same UID. Nothing to do @@ -599,7 +798,7 @@ function pkwk_chown($filename, $preserve_time = TRUE) // NOTE: Not 'r+'. Don't check write permission here $ffile = fopen($filename, 'r') or die('pkwk_chown(): fopen() failed for: ' . - basename(htmlspecialchars($filename))); + basename(htmlsc($filename))); // Try to chown by re-creating files // NOTE: @@ -639,7 +838,22 @@ function pkwk_touch_file($filename, $time = FALSE, $atime = FALSE) return $result; } else { die('pkwk_touch_file(): Invalid UID and (not writable for the directory or not a flie): ' . - htmlspecialchars(basename($filename))); + htmlsc(basename($filename))); } } -?> + +/** + * Lock-enabled file_get_contents + * + * Require: PHP5+ + */ +function pkwk_file_get_contents($filename) { + if (! file_exists($filename)) { + return false; + } + $fp = fopen($filename, 'rb'); + flock($fp, LOCK_SH); + $file = file_get_contents($filename); + flock($fp, LOCK_UN); + return $file; +}