$v) unset($$k); // register_globals = off // SETTINGS - default settings, can be overridden in config.php $WIKI_TITLE = 'My new wiki'; // name of the site $PASSWORD = ''; // SHA1 hash $TEMPLATE = 'templates/dandelion.html'; // presentation template $PROTECTED_READ = false; // if true, you need to fill password for reading pages too $NO_HTML = true; // XSS protection $START_PAGE = 'Main page'; // Which page should be default (start page)? $SYNTAX_PAGE = 'http://lionwiki.0o.cz/?page=Syntax+reference'; $DATE_FORMAT = 'Y/m/d H:i'; $LOCAL_HOUR = 0; @error_reporting(E_ERROR | E_WARNING | E_PARSE); @ini_set('default_charset', 'UTF-8'); set_magic_quotes_runtime(0); umask(0); if(get_magic_quotes_gpc()) // magic_quotes_gpc can't be turned off for($i = 0, $_SG = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST), $c = count($_SG); $i < $c; ++$i) $_SG[$i] = array_map('stripslashes', $_SG[$i]); $self = basename($_SERVER['PHP_SELF']); $REAL_PATH = realpath(dirname(__FILE__)).'/'; $VAR_DIR = 'var/'; $PG_DIR = $VAR_DIR.'pages/'; $HIST_DIR = $VAR_DIR.'history/'; $PLUGINS_DIR = 'plugins/'; $PLUGINS_DATA_DIR = $VAR_DIR.'plugins/'; $LANG_DIR = 'lang/'; @include('config.php'); // config file is not required, see settings above // default translation $T_HOME = 'Main page'; $T_SYNTAX = 'Syntax'; $T_DONE = 'Save changes'; $T_DISCARD_CHANGES = 'Discard changes'; $T_PREVIEW = 'Preview'; $T_SEARCH = 'Search'; $T_SEARCH_RESULTS = 'Search results'; $T_LIST_OF_ALL_PAGES = 'List of all pages'; $T_RECENT_CHANGES = 'Recent changes'; $T_LAST_CHANGED = 'Last changed'; $T_HISTORY = 'History'; $T_RESTORE = 'Restore'; $T_REV_DIFF = 'Difference between revisions from {REVISION1} and {REVISION2}.'; $T_REVISION = "'''This revision is from {TIME}. You can {RESTORE} it.'''\n\n"; $T_PASSWORD = 'Password'; $T_EDIT = 'Edit'; $T_EDIT_SUMMARY = 'Summary of changes'; $T_EDIT_CONFLICT = 'Edit conflict: somebody saved this page after you started editing. See last {DIFF} before saving your changes.'; $T_SHOW_SOURCE = 'Show source'; $T_SHOW_PAGE = 'Show page'; $T_ERASE_COOKIE = 'Erase cookies'; $T_MOVE_TEXT = 'New name'; $T_DIFF = 'diff'; $T_CREATE_PAGE = 'Create page'; $T_PROTECTED_READ = 'You need to enter password to view content of site: '; $T_WRONG_PASSWORD = 'Password is incorrect.'; if($_GET['lang']) { $LANG = clear_path($_GET['lang']); setcookie('LW_LANG', $LANG, time() + 365 * 86400); } elseif($_COOKIE['LW_LANG']) $LANG = clear_path($_COOKIE['LW_LANG']); else list($LANG) = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']); if((@include("$LANG_DIR$LANG.php")) === false && (@include($LANG_DIR . substr($LANG, 0, 2) . '.php')) === false) $LANG = 'en'; // Creating essential directories if they don't exist if(!file_exists($VAR_DIR) && !mkdir(rtrim($VAR_DIR, "/"))) die("Can't create directory $VAR_DIR. Please create $VAR_DIR with 0777 rights."); else foreach(array($PG_DIR, $HIST_DIR, $PLUGINS_DATA_DIR) as $DIR) if(@mkdir(rtrim($DIR, '/'), 0777)) { $f = fopen($DIR . ".htaccess", "w"); fwrite($f, "deny from all"); fclose($f); } if($_GET['erasecookie']) // remove cookie without reloading foreach($_COOKIE as $k => $v) if(substr($k, 0, 3) == 'LW_') { setcookie($k); unset($_COOKIE[$k]); } for($plugins = array(), $dir = @opendir($PLUGINS_DIR); $dir && $f = readdir($dir);) // load plugins if(preg_match('/wkp_(.+)\.php$/', $f, $m) > 0) { require $PLUGINS_DIR . $f; $plugins[$m[1]] = new $m[1](); if(isset($$m[1])) foreach($$m[1] as $name => $value) $plugins[$m[1]]->$name = $value; } plugin('pluginsLoaded'); foreach(array('action', 'content', 'error', 'esum', 'f1', 'f2', 'last_changed', 'moveto', 'page', 'par', 'preview', 'query', 'restore', 'sc', 'showsource', 'time') as $req) $$req = $_REQUEST[$req]; // export request variables to global namespace $TITLE = $page = clear_path($page); $moveto = clear_path($moveto); $f1 = clear_path($f1); $f2 = clear_path($f2); $CON = $content; plugin('actionBegin'); if(!$action) if(!$page) die(header("Location:$self?page=" . u($START_PAGE))); elseif(file_exists("$PG_DIR$page.$LANG.txt")) // language variant die(header("Location:$self?page=" . u("$page.$LANG"))); elseif(!file_exists("$PG_DIR$page.txt")) $action = 'edit'; // create page if it doesn't exist if($PROTECTED_READ && !authentified()) { // does user need password to read content of site. If yes, ask for it. $CON = "

$T_PROTECTED_READ

"; $action = 'view-html'; } else if($restore || $action == 'rev') { // Show old revision $CON = @file_get_contents("$HIST_DIR$page/$f1"); if($action == 'rev') { $rev_restore = "[$T_RESTORE|./$self?page=".u($page)."&action=edit&f1=$f1&restore=1]"; $CON = strtr($T_REVISION, array('{TIME}' => rev_time($f1), '{RESTORE}' => $rev_restore)) . $CON; $action = ''; } } else if($page) { // Load the page $last_changed_ts = @filemtime("$PG_DIR$page.txt"); if(!$action || $action == 'edit') { $CON = @file_get_contents("$PG_DIR$page.txt"); $CON = $par ? get_paragraph($CON, $par) : $CON; if(!$action && substr($CON, 0, 10) == '{redirect:' && $_REQUEST['redirect'] != 'no') die(header("Location:$self?page=".u(substr($CON, 10, strpos($CON, '}') - 10)))); } } if($action == 'save' && !$preview && authentified()) { // do we have page to save? if(!trim($content) && !$par) // delete empty page @unlink("$PG_DIR$page.txt"); elseif($last_changed < @filemtime("$PG_DIR$page.txt")) { $action = 'edit'; $error = str_replace('{DIFF}', "$T_DIFF", $T_EDIT_CONFLICT); } elseif(!plugin('writingPage')) { // are plugins OK with page? (e.g. checking for spam) if($par) { $c = @file_get_contents("$PG_DIR$page.txt"); $content = str_replace(get_paragraph($c, $par), $content, $c); } if(!$file = @fopen("$PG_DIR$page.txt", 'w')) die("Could not write page $PG_DIR$page.txt!"); fwrite($file, $content); fclose($file); // Backup old revision @mkdir($HIST_DIR.$page, 0777); // Create directory if does not exist $rightnow = date('Ymd-Hi-s', time() + $LOCAL_HOUR * 3600); if(!$bak = @fopen("$HIST_DIR$page/$rightnow.bak", 'w')) die("Could not write to $HIST_DIR$page!"); fwrite($bak, $content); fclose($bak); $es = fopen("$HIST_DIR$page/meta.dat", 'ab'); fwrite($es, '!' . $rightnow . str_pad($_SERVER['REMOTE_ADDR'], 16, ' ', STR_PAD_LEFT) . str_pad(filesize("$PG_DIR$page.txt"), 11, ' ', STR_PAD_LEFT) . ' ' . str_pad(substr($esum, 0, 128), 128 + 2)) . "\n"; fclose($es); if($moveto != $page && $moveto) if(file_exists("$PG_DIR$moveto.txt")) die('Error: target filename already exists. Page was not moved.'); elseif(!rename("$PG_DIR$page.txt", "$PG_DIR$moveto.txt")) die('Unknown error! Page was not moved.'); elseif(!rename($HIST_DIR.$page, $HIST_DIR.$moveto)) { rename("$PG_DIR$moveto.txt", "$PG_DIR$page.txt"); // revert previous change die('Unknown error2! Page was not moved.'); } else $page = $moveto; if(!plugin('pageWritten')) die(header("Location:$self?page=" . u($page) . '&redirect=no' . ($par ? "&par=$par" : '') . ($_REQUEST['ajax'] ? '&ajax=1' : ''))); else $action = ''; // display content ... } else // there's some problem with page, give user a chance to fix it $action = 'edit'; } elseif($action == 'save' && !$preview) { // wrong password, give user another chance $error = $T_WRONG_PASSWORD; $action = 'edit'; } if($action == 'edit' || $preview) { $CON_FORM_BEGIN = "
"; $CON_FORM_END = '
'; $CON_TEXTAREA = ''; $CON_PREVIEW = ''; if(!$showsource) { $CON_SUBMIT = ''; $EDIT_SUMMARY_TEXT = $T_EDIT_SUMMARY; $EDIT_SUMMARY = ''; if(!authentified()) { // if not logged on, require password $FORM_PASSWORD = $T_PASSWORD; $FORM_PASSWORD_INPUT = ''; } if(!$par) { $RENAME_TEXT = $T_MOVE_TEXT; $RENAME_INPUT = ''; } } if($preview) $TITLE = "$T_PREVIEW: $page"; } elseif($action == 'history') { // show whole history of page for($dir = @opendir("$HIST_DIR$page/"); $f = @readdir($dir);) if(substr($f, -4) == '.bak') $files[] = $f; rsort($files); $CON = '

'; $meta = @fopen("$HIST_DIR$page/meta.dat", "rb"); for($i = 0, $mi = 1, $c = count($files); $i < $c; $i++) { if(($m = meta_getline($meta, $mi)) && !strcmp(basename($files[$i], ".bak"), $m[0])) $mi++; $CON .= ''; $CON .= "".rev_time($files[$i])." - ($m[2] B) $m[1] ".h($m[3])."
"; } $CON .= '
'; } elseif($action == 'diff') { if(!$f1 && $dir = @opendir("$HIST_DIR$page/")) { // diff is made on two last revisions while($f = @readdir($dir)) if(substr($f, -4) == '.bak') $files[] = $f; rsort($files); die(header("Location:$self?action=diff&page=".u($page)."&f1=$files[0]&f2=$files[1]")); } $r1 = "".rev_time($f1).""; $r2 = "".rev_time($f2).""; $CON = str_replace(array("{REVISION1}", "{REVISION2}"), array($r1, $r2), $T_REV_DIFF); $CON .= diff($f1, $f2); } elseif($action == 'search') { for($files = array(), $dir = opendir($PG_DIR); $f = readdir($dir);) if(substr($f, -4) == '.txt' && ($c = @file_get_contents($PG_DIR . $f))) if(!$query || stristr($f . $c, $query) !== false) $files[] = substr($f, 0, -4); sort($files); foreach($files as $f) $list .= "
  • '.h($f)."
  • "; $CON = ""; if($query && !file_exists("$PG_DIR$query.txt")) // offer to create the page $CON = "

    $T_CREATE_PAGE ".h($query).".

    ".$CON; $TITLE = (!$query ? $T_LIST_OF_ALL_PAGES : "$T_SEARCH_RESULTS $query") . " (".count($files).")"; } elseif($action == 'recent') { // recent changes for($files = array(), $dir = opendir($PG_DIR); $f = readdir($dir);) if(substr($f, -4) == '.txt') $files[substr($f, 0, -4)] = filemtime($PG_DIR . $f); arsort($files); foreach(array_slice($files, 0, 100) as $f => $ts) { // just first 100 files if($meta = @fopen($HIST_DIR . basename($f, '.txt') . '/meta.dat', 'r')) { $m = meta_getline($meta, 1); fclose($meta); } $recent .= "$T_DIFF".date($DATE_FORMAT, $ts + $LOCAL_HOUR * 3600)."$m[1]".h($f)." ($m[2] B) ".h($m[3]).""; } $CON = "$recent
    "; $TITLE = $T_RECENT_CHANGES; } else plugin('action', $action); if(!$action || $preview) { // page parsing if(preg_match("/(?/U", "", $CON); // internal comments $CON = preg_replace("/\^(.)/e", "'&#'.ord('$1').';'", $CON); $CON = str_replace(array("<", "&"), array("<", "&"), $CON); $CON = preg_replace("/&([a-z]+;|\#[0-9]+;)/U", "&$1", $CON); // keep HTML entities $CON = preg_replace("/(\r\n|\r)/", "\n", $CON); // unifying newlines to Unix ones preg_match_all("/{{(.+)}}/Ums", $CON, $codes, PREG_PATTERN_ORDER); $CON = preg_replace("/{{(.+)}}/Ums", "
    {CODE}
    ", $CON); // spans preg_match_all("/\{([\.#][^\s\"\}]*)(\s([^\}\"]*))?\}/m", $CON, $spans, PREG_SET_ORDER); foreach($spans as $m) { $class = $id = ''; $parts = preg_split('/([\.#])/', $m[1], -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); for($i = 0, $c = count($parts); $c > 1 && $i < $c; $i += 2) if($parts[$i] == '.') $class .= $parts[$i + 1] . ' '; else $id = $parts[$i + 1]; $CON = str_replace($m[0], '', $CON); } $CON = str_replace('{/}', '', $CON); plugin('formatBegin'); $CON = strtr($CON, array('<-->' => '↔', '-->' => '→', '<--' => '←', "(c)" => '©', "(r)" => '®')); $CON = preg_replace("/\{small\}(.*)\{\/small\}/U", "$1", $CON); // small $CON = preg_replace("/\{su([bp])\}(.*)\{\/su([bp])\}/U", "$2", $CON); // sup and sub $CON = preg_replace("/^([^!\*#\n][^\n]+)$/Um", '

    $1

    ', $CON); // paragraphs // images preg_match_all("#\[((https?://|\./)[^\]]+\.(jpeg|jpg|gif|png))(\|[^\]]+)?\]#", $CON, $imgs, PREG_SET_ORDER); foreach($imgs as $img) { preg_match_all("/\|([^\]\|=]+)(=([^\]\|\"]+))?(?=[\]\|])/", $img[0], $options, PREG_SET_ORDER); $link = $i_attr = $a_attr = $center = $tag = ""; foreach($options as $o) if($o[1] == 'center') $center = true; elseif($o[1] == 'right' || $o[1] == 'left') $i_attr .= " style=\"float:$o[1]\""; elseif($o[1] == 'link') $link = (substr($o[3], 0, 4) == "http" || substr($o[3], 0, 2) == "./") ? $o[3] : "$self?page=" . u($o[3]); elseif($o[1] == 'alt') $i_attr .= " alt=\"$o[3]\""; elseif($o[1] == 'title') $a_attr .= " title=\"$o[3]\""; $tag = "\"$alt\"$i_attr/"; if($link) $tag = "$tag"; if($center) $tag = "
    $tag
    "; $CON = str_replace($img[0], $tag, $CON); } $CON = preg_replace('#([0-9a-zA-Z\./~\-_]+@[0-9a-z/~\-_]+\.[0-9a-z\./~\-_]+)#i', '$0', $CON); // mail recognition // links $CON = preg_replace("#\[([^\]\|]+)\|(\./([^\]]+)|(https?://[^\]]+))\]#U", '$1', $CON); $CON = preg_replace("#(?$0', $CON); preg_match_all("/\[(?:([^|\]]+)\|)?([^\]#]+)(?:#([^\]]+))?\]/", $CON, $matches, PREG_SET_ORDER); // matching Wiki links foreach($matches as $m) { $m[1] = $m[1] ? $m[1] : $m[2]; // is page label same as its name? $m[3] = $m[3] ? '#'.u(preg_replace('/[^\da-z]/i', '_', $m[3])) : ''; // anchor $attr = file_exists("$PG_DIR$m[2].txt") ? $m[3] : '&action=edit" class="pending"'; $CON = str_replace($m[0], ''.$m[1].'', $CON); } for($i = 10; $i >= 1; $i--) { // Lists, ordered, unordered $CON = preg_replace('/^'.str_repeat('\*', $i)."(.*)(\n?)/m", str_repeat('', $i).'$2', $CON); $CON = preg_replace('/^'.str_repeat('\#', $i)."(.*)(\n?)/m", str_repeat('
      ', $i).'
    1. $1
    2. '.str_repeat('
    ', $i).'$2', $CON); $CON = preg_replace("#(\n?
      |\n?
        )#", '', $CON); } // headings preg_match_all('/^(!+)(.*)$/m', $CON, $matches, PREG_SET_ORDER); $stack = array(); for($h_id = max($par, 1), $i = 0, $c = count($matches); $i < $c && $m = $matches[$i]; $i++, $h_id++) { $excl = strlen($m[1]) + 1; $hash = preg_replace('/[^\da-z]/i', '_', $m[2]); for($ret = ''; end($stack) >= $excl; $ret .= '', array_pop($stack)); $stack[] = $excl; $ret .= "
        $m[2]"; if(is_writable($PG_DIR . $page . '.txt')) $ret .= "($T_EDIT)"; $CON = preg_replace('/' . preg_quote($m[0], '/') . '/', "$ret", $CON, 1); $TOC .= str_repeat("", $excl - 2); } $CON .= str_repeat('
        ', count($stack)); $TOC = '
          ' . preg_replace(array_fill(0, 5, "#
        \n*
          #"), array_fill(0, 5, ''), $TOC) . '
        '; $TOC = str_replace(array('
          ', '
      • ', '
      ', '
          '), array('
            ', '
        • ', '
      ', '
        • '), $TOC); $CON = preg_replace("/'--(.*)--'/Um", '$1', $CON); // strikethrough $CON = preg_replace("/'__(.*)__'/Um", '$1', $CON); // underlining $CON = preg_replace("/'''(.*)'''/Um", '$1', $CON); // bold $CON = preg_replace("/''(.*)''/Um", '$1', $CON); // italic $CON = str_replace('{br}', '
          ', $CON); // new line $CON = preg_replace('/-----*/', '
          ', $CON); // horizontal line $CON = str_replace('--', '—', $CON); // -- $CON = preg_replace(array_fill(0, count($codes[1]) + 1, '/{CODE}/'), $codes[1], $CON, 1); // put HTML and "normal" codes back $CON = preg_replace(array_fill(0, count($htmlcodes[1]) + 1, '/{HTML}/'), $htmlcodes[1], $CON, 1); plugin('formatEnd'); } plugin('formatFinished'); // Loading template. If does not exist, use built-in default $html = file_exists($TEMPLATE) ? file_get_contents(clear_path($TEMPLATE)) : fallback_template(); // including pages in pure HTML while(preg_match('/{include:([^}]+)}/U', $html, $m)) { $inc = str_replace(array('{html}', '{/html}'), '', @file_get_contents("$PG_DIR$m[1].txt")); $html = str_replace($m[0], $inc, $html); } plugin('template'); // plugin templating $html = preg_replace('/\{([^}]* )?plugin:.+( [^}]*)?\}/U', '', $html); // get rid of absent plugin tags $tpl_subs = array( 'HEAD' => $HEAD . ($action ? '' : ''), 'SEARCH_FORM' => '
          ', '\/SEARCH_FORM' => "
          ", 'SEARCH_INPUT' => '', 'SEARCH_SUBMIT' => "", 'HOME' => "$T_HOME", 'RECENT_CHANGES' => "$T_RECENT_CHANGES", 'ERROR' => $error, 'HISTORY' => $page ? "$T_HISTORY" : "", 'PAGE_TITLE' => h($page == $START_PAGE && $page == $TITLE ? $WIKI_TITLE : $TITLE), 'PAGE_TITLE_HEAD' => h($TITLE), 'PAGE_URL' => u($page), 'EDIT' => !$action ? ("$T_EDIT" : "&showsource=1\">$T_SHOW_SOURCE")) : "", 'WIKI_TITLE' => h($WIKI_TITLE), 'LAST_CHANGED_TEXT' => $last_changed_ts ? $T_LAST_CHANGED : "", 'LAST_CHANGED' => $last_changed_ts ? date($DATE_FORMAT, $last_changed_ts + $LOCAL_HOUR * 3600) : "", 'CONTENT' => $action != "edit" ? $CON : "", 'TOC' => $TOC, 'SYNTAX' => $action == "edit" || $preview ? "$T_SYNTAX" : "", 'SHOW_PAGE' => $action == "edit" || $preview ? "$T_SHOW_PAGE" : "", 'COOKIE' => ''.$T_ERASE_COOKIE.'', 'CONTENT_FORM' => $CON_FORM_BEGIN, '\/CONTENT_FORM' => $CON_FORM_END, 'CONTENT_TEXTAREA' => $CON_TEXTAREA, 'CONTENT_SUBMIT' => $CON_SUBMIT, 'CONTENT_PREVIEW' => $CON_PREVIEW, 'RENAME_TEXT' => $RENAME_TEXT, 'RENAME_INPUT' => $RENAME_INPUT, 'EDIT_SUMMARY_TEXT' => $EDIT_SUMMARY_TEXT, 'EDIT_SUMMARY_INPUT' => $EDIT_SUMMARY, 'FORM_PASSWORD' => $FORM_PASSWORD, 'FORM_PASSWORD_INPUT' => $FORM_PASSWORD_INPUT ); foreach($tpl_subs as $tpl => $rpl) // substituting values $html = template_replace($tpl, $rpl, $html); header('Content-type: text/html; charset=UTF-8'); die($html); // Function library function h($t) { return htmlspecialchars($t); } function u($t) { return urlencode($t); } function template_replace($what, $subs, $where) { return preg_replace("/\{(([^}]*) )?$what( ([^}]*))?\}/U", empty($subs) ? "" : "\${2}".str_replace("$", "$", trim($subs))."\${4}", $where); } function template_match($what, $where, &$dest) { return preg_match("/\{(([^}]*) )?$what( ([^}]*))?\}/U", $where, $dest); } function clear_path($s) { for($i = 0, $ret = "", $c = strlen($s); $i < $c; $i++) $ret .= ctype_cntrl($s[$i]) ? "" : $s[$i]; return trim(str_replace("..", "", $ret), "/"); } function rev_time($time) { preg_match('/(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})-(\d{2})/U', $time, $m); return date($GLOBALS['DATE_FORMAT'], mktime($m[4], $m[5], $m[6], $m[2], $m[3], $m[1])); } // get paragraph number $par_id. function get_paragraph($text, $par_id) { $par = array(); // paragraph $count = 1; // paragraph count $par_excl = 0; // number of ! $inside_code = $inside_html = false; // exclamation marks inside {{}} and {html}{/html} are not headings $lines = explode("\n", $text); foreach($lines as $l) { if($l[0] == '!' && !$inside_html && !$inside_code) { for($excl = 1, $c = strlen($l); $excl < $c && $l[$excl] == '!'; $excl++); if($count == $par_id) { $par[] = $l; $par_excl = $excl; } elseif($par_excl) if($excl > $par_excl) $par[] = $l; else break; $count++; } elseif($par_excl) $par[] = $l; if(preg_match('/(?'.h(trim($d1[$i]))."\n"; if($r2 = array_key_exists($i, $d2)) $ret .= ''.h(trim($d2[$i]))."\n"; if(!$r1 && !$r2) $ret .= h(trim($a2[$i]))."\n"; } return "
          $ret
          "; } function authentified() { if(!$GLOBALS['PASSWORD'] || !strcasecmp($_COOKIE['LW_AUT'], $GLOBALS['PASSWORD']) || !strcasecmp(sha1($GLOBALS['sc']), $GLOBALS['PASSWORD'])) { setsafecookie('LW_AUT', $GLOBALS['PASSWORD'], time() + ($GLOBALS['PROTECTED_READ'] ? 4 * 3600 : 365 * 86400)); return true; } else return false; } function setsafecookie() { // setcookie for sensitive informations $args = func_get_args(); if(version_compare(PHP_VERSION, '5.2.0') >= 0) { while(count($args) != 6) $args[] = ''; $args[] = true; // httponly, supported only in some browsers and PHP >= 5.2.0. Successfully prevents XSS attacks. } call_user_func_array('setcookie', $args); } // returns "line" from meta.dat files. $lnum is number of line from the end of file starting with 1 function meta_getline($file, $lnum) { if(fseek($file, -($lnum * 175), SEEK_END) || !($line = fread($file, 175)) || $line[0] != "!") return false; // ! is control character $date = substr($line, 1, 16); $ip = trim(substr($line, 19, 15)); $size = (int) substr($line, 35, 10); $esum = trim(substr($line, 45, 128)); return array($date, $ip, $size, $esum); } // Call a method for all plugins, second to last arguments are forwarded to plugins as arguments function plugin($method) { $ret = false; $args = array_slice(func_get_args(), 1); foreach($GLOBALS['plugins'] as $idx => $plugin) $ret |= method_exists($GLOBALS['plugins'][$idx], $method) && call_user_func_array(array(&$GLOBALS['plugins'][$idx], $method), $args); return $ret; // returns true if treated by a plugin } function fallback_template() { return ' {PAGE_TITLE_HEAD - }{WIKI_TITLE} {HEAD}
          {HOME} {RECENT_CHANGES} {EDIT} {SYNTAX} {HISTORY}

          {PAGE_TITLE} {( plugin:VERSIONS_LIST )}

          {
          ERROR
          } {CONTENT} {plugin:TAG_LIST} {CONTENT_FORM} {RENAME_TEXT} {RENAME_INPUT

          } {CONTENT_TEXTAREA}

          {FORM_PASSWORD} {FORM_PASSWORD_INPUT} {plugin:CAPTCHA_QUESTION} {plugin:CAPTCHA_INPUT} {EDIT_SUMMARY_TEXT} {EDIT_SUMMARY_INPUT} {CONTENT_SUBMIT} {CONTENT_PREVIEW}

          {/CONTENT_FORM}

          {SEARCH_FORM}{SEARCH_INPUT}{SEARCH_SUBMIT}{/SEARCH_FORM}
          Powered by LionWiki. {LAST_CHANGED_TEXT}: {LAST_CHANGED} {COOKIE} {EDIT} {SYNTAX} {HISTORY}
          '; }