X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=lib%2Ffile.php;h=b90a2f8678de5b4eaf8b5801f7a14fbc90dcb7dd;hb=ef13260aa62b4b4d6d7da5f9c85a568eee44aa24;hp=dc6056102f149cd2632621f2b6d27e34567019d9;hpb=512386fb0ca976b28c4293c448b4ad2d5ff57e0e;p=pukiwiki%2Fpukiwiki.git diff --git a/lib/file.php b/lib/file.php index dc60561..b90a2f8 100644 --- a/lib/file.php +++ b/lib/file.php @@ -1,32 +1,61 @@ = $count) break; } - flock($fp, LOCK_UN); - - if(! fclose($fp)) return FALSE; + 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) { - if (! file_exists($file)) return; // Ignore null posting for DATA_DIR + if ($dir == DATA_DIR && $is_delete) { + // Page deletion + if (! $file_exists) return; // Ignore null posting for DATA_DIR - // Page deletion - unlink($file); + // Update RecentDeleted (Add the $page) + add_recent($page, $whatsdeleted, '', $maxshow_deleted); - // Update RecentDeleted (Add the $page) - add_recent($page, $whatsdeleted, '', $maxshow_deleted); + // Remove the page + unlink($file); - // Update RecentChanges (Remove the $page from RecentChanges) - if (! $timestamp && $dir == DATA_DIR) put_lastmodified(); - } - } else { - if ($dir == DIFF_DIR && $str === " \n") return; // Ignore null posting for DIFF_DIR + // Update RecentDeleted, and remove the page from RecentChanges + lastmodified_add($whatsdeleted, $page); - // File replacement (Edit) - $str = rtrim(preg_replace('/' . "\r" . '/', '', $str)) . "\n"; + // Clear is_page() cache + is_page($page, TRUE); - if ($notimestamp && file_exists($file)) - $timestamp = filemtime($file) - LOCALZONE; + return; - $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); + } else if ($dir == DIFF_DIR && $str === " \n") { + return; // Ignore null posting for DIFF_DIR + } - flock($fp, LOCK_EX); + // ---- + // File replacement (Edit) - // Write - ftruncate($fp, 0); - rewind($fp); - fputs($fp, $str); + if (! is_pagename($page)) + die_message(str_replace('$1', htmlsc($page), + str_replace('$2', 'WikiName', $_msg_invalidiwn))); - flock($fp, LOCK_UN); + $str = rtrim(preg_replace('/' . "\r" . '/', '', $str)) . "\n"; + $timestamp = ($file_exists && $notimestamp) ? filemtime($file) : FALSE; - fclose($fp); + $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 + LOCALZONE); + if ($timestamp) pkwk_touch_file($file, $timestamp); + // Optional actions + if ($dir == DATA_DIR) { // Update RecentChanges (Add or renew the $page) - if (! $timestamp && $dir == DATA_DIR) put_lastmodified(); - } - - // Clear is_page() cache - is_page($page, TRUE); + if ($timestamp === FALSE) lastmodified_add($page); - // Execute $update_exec here - if ($update_exec && $dir == DATA_DIR) - system($update_exec . ' > /dev/null &'); + // Command execution per update + if (defined('PKWK_UPDATE_EXEC') && PKWK_UPDATE_EXEC) + system(PKWK_UPDATE_EXEC . ' > /dev/null &'); - if ($notify && $dir == DIFF_DIR) { + } 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 @@ -274,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); @@ -282,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); @@ -294,36 +355,132 @@ function add_recent($page, $recentpage, $subject = '', $limit = 0) fclose($fp); } -// Re-create PKWK_MAXSHOW_CACHE +// 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 + // 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); // Cut unused lines - $recent_pages = array_splice($recent_pages, 0, $maxshow); + // 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 - $fp = fopen(CACHE_DIR . PKWK_MAXSHOW_CACHE, 'w') or - die_message('Cannot write file ' . - 'CACHE_DIR/' . PKWK_MAXSHOW_CACHE . '
' . "\n" . - 'Maybe permission is not writable'); + $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"); @@ -331,17 +488,18 @@ function put_lastmodified() fclose($fp); // Create RecentChanges - $fp = fopen(get_filename($whatsnew), 'w') or - die_message('Cannot write file ' . - htmlspecialchars($whatsnew) . '
' . "\n" . - '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); 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"); // :) @@ -353,12 +511,13 @@ function put_lastmodified() list($pattern, $pattern_a, $forceignorelist) = get_autolink_pattern($pages); - $fp = fopen(CACHE_DIR . PKWK_AUTOLINK_REGEX_CACHE, 'w') or - die_message('Cannot write file ' . - 'CACHE_DIR/' . PKWK_AUTOLINK_REGEX_CACHE . '
' . "\n" . - '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"); @@ -392,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; @@ -555,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"; @@ -573,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) { @@ -595,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']); @@ -621,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 @@ -637,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: @@ -677,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; } -?>