$value) unset($$key); // register_globals = off // SETTINGS - fallback settings when no config file exists. $WIKI_TITLE = "My new wiki"; // name of the site $PASSWORD = ""; // if left blank, no password is required to edit. Consider also $PASSWORD_MD5 below // More secure way to use password protection, just insert MD5 hash into $PASSWORD_MD5 // if not empty, $PASSWORD is ignored and $PASSWORD_MD5 is used instead $PASSWORD_MD5 = ""; $TEMPLATE = "template_dandelion.html"; // Page template $USE_AUTOLANG = true; // should we try to detect language from browser? $LANG = "en"; // language code you want to use, used only when $USE_AUTOLANG = false $PROTECTED_READ = false; // if true, you need to fill password for reading pages too $HISTORY_COMPRESSION = "gzip"; // possible values: bzip2, gzip and plain $NO_HTML = false; // XSS protection, meaningful only when password protection is enabled $USE_META = true; // use and create meta data. Small overhead, but edit summary and IP info $USE_HISTORY = true; // If you don't want to keep history of pages, change to false $START_PAGE = "Main page"; // Which page should be default (start page)? $SYNTAX_PAGE = "Syntax reference"; // Which page contains help informations? $COOKIE_LIFE_WRITE = 365 * 24 * 86400; // lifetime of cookies when password protection applies only to writing $COOKIE_LIFE_READ = 4 * 3600; // lifetime of cookies when $PROTECTED_READ = true // END OF SETTINGS $DATE_FORMAT = "Y/m/d H:i"; $LOCAL_HOUR = "0"; $EDIT_SUMMARY_LEN = "128"; // don't play with this!!! @error_reporting(E_ERROR | E_WARNING | E_PARSE); set_magic_quotes_runtime(0); // turn off magic quotes $self = $_SERVER['PHP_SELF']; if(get_magic_quotes_gpc()) { // magic_quotes_gpc can't be turned off foreach($_GET as $k => $v) $_GET[$k] = stripslashes($v); foreach($_POST as $k => $v) $_POST[$k] = stripslashes($v); foreach($_COOKIE as $k => $v) $_COOKIE[$k] = stripslashes($v); foreach($_REQUEST as $k => $v) $_REQUEST[$k] = stripslashes($v); } $BASE_DIR = $_GET["basedir"] ? $_GET["basedir"] . "/" : ""; @include("_config.php"); // config file is not required, see settings above if(!empty($BASE_DIR)) @include($BASE_DIR . "_config.php"); // subdomain specific settings if(empty($PASSWORD_MD5) && !empty($PASSWORD)) $PASSWORD_MD5 = md5($PASSWORD); $WIKI_VERSION = "LionWiki 2.3.6"; $PAGES_DIR = $BASE_DIR . "pages/"; $HISTORY_DIR = $BASE_DIR . "history/"; $PLUGINS_DIR = "plugins/"; $PLUGINS_DATA_DIR = "data/"; $LANG_DIR = "lang/"; umask(0); // sets default mask // some strings may not be translated, in that case, we'll use english translation, which should be always complete $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 = "Edit summary"; $T_EDIT_CONFLICT = "Edit conflict: somebody saved this page after you started editing. It is strongly encouraged to see last {DIFF} before saving it. After reviewing and possibly merging changes, you can save page by clicking on save button."; $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: "; $TE_WRONG_PASSWORD = "Password is incorrect."; // Default character set for auto content header @ini_set("default_charset", "UTF-8"); header("Content-type: text/html; charset=UTF-8"); // consider only first language, don't consider language variant (like en-us or pt-br) if($USE_AUTOLANG) list($LANG) = explode(",", $_SERVER['HTTP_ACCEPT_LANGUAGE']); $LANG = !empty($_COOKIE["LW_LANG"]) ? $_COOKIE["LW_LANG"] : $LANG; if(!empty($_GET["lang"])) { $LANG = $_GET["lang"]; setcookie('LW_LANG', $LANG, time() + 365 * 86400); } if(@file_exists($LANG_DIR . $LANG . ".php")) @include $LANG_DIR . $LANG . ".php"; else if(@file_exists($LANG_DIR . substr($LANG, 0, 2) . ".php")) @include $LANG_DIR . substr($LANG, 0, 2) . ".php"; else $LANG = "en"; // Installation - create directories pages and history, if possible if(!file_exists($PAGES_DIR) && !mkdir(rtrim($PAGES_DIR, "/"))) die("Can't create directory $PAGES_DIR. Please create $PAGES_DIR and $HISTORY_DIR with 0777 rights."); if($USE_HISTORY && !file_exists($HISTORY_DIR) && !mkdir(rtrim($HISTORY_DIR, "/"))) die("Can't create directory $HISTORY_DIR. Please create $HISTORY_DIR with 0777 rights or turn off history feature in config file. Turning off history now."); if($_GET["erasecookie"]) // remove cookie without reloading foreach($_COOKIE as $key => $value) if(!strcmp(substr($key, 0, 3), "LW_")) { setcookie($key); unset($_COOKIE[$key]); } $plugins = array(); $plugin_files = array(); $plugin_saveok = true; // is OK to save page changes (from plugins) // We load common plugins for all subsites and then just for this subsite. if(!empty($BASE_DIR) && ($dir = @opendir($BASE_DIR . $PLUGINS_DIR))) while($file = readdir($dir)) $plugin_files[] = $BASE_DIR . $PLUGINS_DIR . $file; if($dir = @opendir($PLUGINS_DIR)) // common plugins while($file = readdir($dir)) if(!in_array($PLUGINS_DIR . $BASE_DIR . $file, $plugin_files)) // we don't want to load plugin twice $plugin_files[] = $PLUGINS_DIR . $file; foreach($plugin_files as $pfile) if(preg_match("/^.*wkp_(.+)\.php$/", $pfile, $matches) > 0) { require $pfile; $plugins[] = new $matches[1](); } plugin_call_method("pluginsLoaded"); // for admin plugin plugin_call_method("pluginsLoaded2"); // second pass ("for ordinary plugins") // list of variables for UTF-8 conversion and export $req_conv = array("action", "query", "sc", "content", "page", "moveto", "restore", "f1", "f2", "error", "time", "esum", "preview", "last_changed", "econfprot", "gtime", "showsource", "par"); if(extension_loaded("mbstring")) { // Conversion to UTF-8 @ini_set("mbstring.language", "Neutral"); @ini_set("mbstring.internal_encoding", "UTF-8"); @ini_set("mbstring.http_output", "UTF-8"); @ini_set("mbstring.detect_order", "UTF-8,ISO8859-2,ISO-8859-1"); @ini_set("mbstring.func_overload", MB_OVERLOAD_STRING); foreach($req_conv as $req_key) $_REQUEST[$req_key] = mb_convert_encoding($_REQUEST[$req_key], "UTF-8", mb_detect_encoding($_REQUEST[$req_key])); } // if mbstring is not supported, nothing bad should happen foreach($req_conv as $req) // export variables to main namespace $$req = $_REQUEST[$req]; if(!empty($preview)) { $action = "edit"; $CON = $content; } plugin_call_method("actionBegin"); // setting $PAGE_TITLE if($page || empty($action)) { $page = $page_nolang = $TITLE = $page ? $page : $START_PAGE; if($action == "" && file_exists($PAGES_DIR . $page . ".$LANG.txt")) // language variant $page = $TITLE = $page_nolang . "." . $LANG; else if(!file_exists($PAGES_DIR . $page . ".txt") && $action == "") $action = "edit"; // create page if it doesn't exist if(!empty($preview)) $TITLE = $T_PREVIEW . ": " . $page; } else if($action == "search") $TITLE = empty($query) ? $T_LIST_OF_ALL_PAGES : "$T_SEARCH_RESULTS $query"; elseif($action == "recent") $TITLE = $T_RECENT_CHANGES; // does user need password to read content of site. If yes, ask for it. if(!authentified() && $PROTECTED_READ) { $CON = "

$T_PROTECTED_READ

"; $action = "view-html"; $error = "error"; // so we know that something went wrong } else if($action == "save" && authentified()) { // do we have page to save? $LAST_CHANGED_TIMESTAMP = @filemtime($PAGES_DIR . $page . ".txt"); if(trim($content) == "" && !$par) @unlink($PAGES_DIR . $page . ".txt"); elseif($last_changed < $LAST_CHANGED_TIMESTAMP && $econfprot) { $action = "edit"; $error = str_replace("{DIFF}", "$T_DIFF", $T_EDIT_CONFLICT); $CON = $content; } else if(!plugin_call_method("writingPage") || $plugin_saveok) { // are plugins happy with page? (no - spam, etc) if($par && is_numeric($par)) { $c = @file_get_contents($PAGES_DIR . $page . ".txt"); $par_content = $content; $content = str_replace(getParagraph($c, $par), $content, $c); } if(!$file = @fopen($PAGES_DIR . $page . ".txt", "w")) die("Could not write page $PAGES_DIR$page.txt!"); fputs($file, $content); fclose($file); if($USE_HISTORY) { // let's archive previous revision $complete_dir = $HISTORY_DIR . $page; if(!is_dir($complete_dir)) mkdir($complete_dir); $rightnow = date("Ymd-Hi-s", time() + $LOCAL_HOUR * 3600); $filename = $complete_dir . "/" . $rightnow . ".bak"; if(!$bak = @lwopen($filename, "w")) die("Could not write backup $filename of page!"); lwwrite($bak, $content); lwclose($bak); if($USE_META) $es = fopen($complete_dir . "/meta.dat", "ab"); if($es) { $filesize = filesize($PAGES_DIR . "/" . $page . ".txt"); // Strings are in UTF-8, it's dangerous to just cut off piece of string, therefore +2 fwrite($es, "!" . $rightnow . str_pad($_SERVER['REMOTE_ADDR'], 16, " ", STR_PAD_LEFT) . str_pad($filesize, 11, " ", STR_PAD_LEFT) . " " . str_pad(substr($esum, 0, $EDIT_SUMMARY_LEN), $EDIT_SUMMARY_LEN + 2)) . "\n"; fclose($es); } } plugin_call_method("pageWritten", $file); if($moveto != $page && !empty($moveto)) { if(!rename($PAGES_DIR . $page . ".txt", $PAGES_DIR . $moveto . ".txt")) die("Moving page was not succesful! Page was not moved."); else if(!rename($HISTORY_DIR . $page, $HISTORY_DIR . $moveto)) { rename($PAGES_DIR . $moveto . ".txt", $PAGES_DIR . $page . ".txt"); // revert previous change die("Moving history of the page was not succesful! Page was not moved."); } else { @touch($PAGES_DIR . $moveto . ".txt"); // moved page should be at the top of recent ch. $page = $moveto; } } if(!($_REQUEST["ajax"] && $par)) { header("Location:?page=" . urlencode($page) . ($error ? ("&error=" . urlencode($error)) : "")); die(); } else $CON = $par_content; } else { // there's some problem with page, give user a chance to fix it (do not throw away submitted content) $CON = $content; $action = "edit"; } } else if($action == "save") { // wrong password, give user another chance (do not throw away submitted content) $error = $TE_WRONG_PASSWORD; $CON = $content; $action = "edit"; } if(@file_exists($PAGES_DIR . $page . ".txt")) { $LAST_CHANGED_TIMESTAMP = @filemtime($PAGES_DIR . $page . ".txt"); $LAST_CHANGED = date($DATE_FORMAT, $LAST_CHANGED_TIMESTAMP + $LOCAL_HOUR * 3600); if(!$CON) { $CON = @file_get_contents($PAGES_DIR . $page . ".txt"); if($par && is_numeric($par)) $CON = getParagraph($CON, $par); if(substr($CON, 0, 10) == "{redirect:" && $action == "") { header("Location:?page=" . substr($CON, 10, strpos($CON, "}") - 10)); // urlencode? die(); } } } // Restoring old version of page if($gtime && ($restore || $action == "rev") && ($file = @lwopen($HISTORY_DIR . $page . "/" . $gtime, "r"))) { $CON = ""; if($action == "rev") { $rev_restore = "[$T_RESTORE|./$self?page=" . urlencode($page) . "&action=edit&gtime=" . $gtime . "&restore=1]"; $CON = str_replace(array("{TIME}", "{RESTORE}"), array(revTime($gtime), $rev_restore), $T_REVISION); } $CON .= @lwread($file); @lwclose($file); } plugin_call_method("pageLoaded"); if($action == "edit") { if(!authentified() && !$showsource) { // if not logged on, require password $FORM_PASSWORD = $T_PASSWORD; $FORM_PASSWORD_INPUT = ""; } if(!$showsource && !$par) { $RENAME_TEXT = $T_MOVE_TEXT; $RENAME_INPUT = ""; } $CON_FORM_BEGIN = "
"; if(empty($econfprot)) $CON_FORM_BEGIN .= ""; $CON_FORM_END = "
"; $CON_TEXTAREA = ""; if(!$showsource) { $CON_SUBMIT = ""; $EDIT_SUMMARY_TEXT = $T_EDIT_SUMMARY; $EDIT_SUMMARY = ""; } $CON_PREVIEW = ""; if($preview) $action = ""; } elseif($action == "rev" && !empty($gtime)) // show old revision of page $action = ""; elseif($action == "history") { // show whole history of page $complete_dir = $HISTORY_DIR . $page . "/"; if($opening_dir = @opendir($complete_dir)) { while($filename = @readdir($opening_dir)) if(preg_match('/(.+)\.bak.*$/', $filename)) $files[] = $filename; rsort($files); $CON = "
\n"; if($USE_META) $meta = @fopen($complete_dir . "meta.dat", "rb"); $i = 1; foreach($files as $fname) { $fname = basename(basename($fname, ".bz2"), ".gz"); if($USE_META) $m = meta_getline($meta, $i); if($m && !strcmp(basename($fname, ".bak"), $m[0])) { $ip = $m[1]; $size = " - ($m[2] B)"; $esum = htmlspecialchars($m[3]); $i++; } else $ip = $size = $esum = ""; $CON .= ""; $CON .= "" . revTime($fname) . " $size $ip $esum
"; } $CON .= "
"; } else $CON = $NO_HISTORY; } elseif($action == "diff") { if(empty($f1) && $opening_dir = @opendir($HISTORY_DIR . $page . "/")) { // diff is made on two last revisions while($filename = @readdir($opening_dir)) if(preg_match('/\.bak.*$/', $filename)) $files[] = basename(basename($filename, ".gz"), ".bz2"); rsort($files); header("Location: ?action=diff&page=" . urlencode($page) . "&f1=$files[0]&f2=$files[1]"); die(); } $r1 = "".revTime($f1).""; $r2 = "".revTime($f2).""; $CON = str_replace(array("{REVISION1}", "{REVISION2}"), array($r1, $r2), $T_REV_DIFF); $CON .= diff($f1, $f2); } elseif($action == "search") { $dir = opendir($PAGES_DIR); // offer to create page if it doesn't exist if($query && !file_exists($PAGES_DIR . $query . ".txt")) $CON = "

$T_CREATE_PAGE " . htmlspecialchars($query) . ".


"; $files = array(); while($file = readdir($dir)) if(preg_match("/\.txt$/", $file) && (@$con = file_get_contents($PAGES_DIR . $file))) if(empty($query) || stristr($con, $query) !== false || stristr($file, $query) !== false) $files[] = substr($file, 0, strlen($file) - 4); sort($files); foreach($files as $file) { if(is_writable($PAGES_DIR . $file . ".txt")) { $link_text = $T_EDIT; $s_source = ""; } else { $link_text = $T_SHOW_SOURCE; $s_source = "&showsource=1"; } $CON .= "" . htmlspecialchars($file) . " ($link_text)
"; } $TITLE .= " (" . count($files) . ")"; } elseif($action == "recent") { // recent changes $dir = opendir($PAGES_DIR); while($file = readdir($dir)) if(preg_match("/\.txt$/", $file)) $filetime[$file] = filemtime($PAGES_DIR . $file); arsort($filetime); $filetime = array_slice($filetime, 0, 100); // just first 100 changed files foreach($filetime as $filename => $timestamp) { $filename = substr($filename, 0, strlen($filename) - 4); if($USE_META && ($meta = @fopen($HISTORY_DIR . basename($filename, ".txt") . "/meta.dat", "r"))) { $m = meta_getline($meta, 1); fclose($meta); $ip = $m[1]; $size = " - ($m[2] B)"; $esum = htmlspecialchars($m[3]); } else $ip = $size = $esum = ""; $CON .= "" . htmlspecialchars($filename) . " (" . date($DATE_FORMAT, $timestamp + $LOCAL_HOUR * 3600) . " - $T_DIFF) $size $ip $esum
"; } } else if(!plugin_call_method("action", $action) && $action != "view-html") $action = ""; if($action == "") { // substituting $CON to be viewed as HTML $CON = "\n" . $CON . "\n"; // Subpages while(preg_match("/([^\^]){include:([^}]+)}/Um", $CON, $match)) { if(!strcmp($match[2], $page)) // limited recursion protection $CON = str_replace($match[0], "'''Warning: subpage recursion!'''", $CON); elseif(file_exists($PAGES_DIR . $match[2] . ".txt")) { $tpl = file_get_contents($PAGES_DIR . $match[2] . ".txt"); $CON = str_replace($match[0], $match[1] . $tpl, $CON); } else $CON = str_replace($match[0], "'''Warning: subpage $match[2] was not found!'''", $CON); } plugin_call_method("subPagesLoaded"); // save content not intended for substitutions ({html} tag) if($NO_HTML == false) { // XSS protection $n_htmlcodes = preg_match_all("/[^\^](\{html\}(.+)\{\/html\})/Ums", $CON, $htmlcodes, PREG_PATTERN_ORDER); foreach($htmlcodes[1] as $hcode) $CON = str_replace($hcode, "{HTML}", $CON); } $CON = preg_replace("/[^\^]/U", "", $CON); // internal comments // escaping ^codes which protects them from substitution $CON = preg_replace("/\^(.)/Umsie", "'&#'.ord('$1').';'", $CON); $CON = str_replace("<", "<", $CON); $CON = str_replace("&", "&", $CON); // & => amp; $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 // {{CODE}} $nbcode = preg_match_all("/{{(.+)}}/Ums", $CON, $matches_code, PREG_PATTERN_ORDER); $CON = preg_replace("/{{(.+)}}/Ums", "
{{CODE}}
", $CON); plugin_call_method("formatBegin"); // substituting special characters $CON = str_replace("<-->", "↔", $CON); // <--> $CON = str_replace("-->", "→", $CON); // --> $CON = str_replace("<--", "←", $CON); // <-- $CON = preg_replace("/\([cC]\)/Umsi", "©", $CON); // (c) $CON = preg_replace("/\([rR]\)/Umsi", "®", $CON); // (r) $CON = preg_replace("/^([^!\*#\n][^\n]+)$/Um", "

$1

", $CON); // sup and sub $CON = preg_replace("/\{sup\}(.*)\{\/sup\}/U", "$1", $CON); $CON = preg_replace("/\{sub\}(.*)\{\/sub\}/U", "$1", $CON); // small $CON = preg_replace("/\{small\}(.*)\{\/small\}/U", "$1", $CON); // TODO: verif & / & $rg_url = "[0-9a-zA-Z\.\#/~\-_%=\?\&,\+\:@;!\(\)\*\$']*"; $rg_img_local = "(" . $rg_url . "\.(jpeg|jpg|gif|png))"; $rg_img_http = "h(ttps?://" . $rg_url . "\.(jpeg|jpg|gif|png))"; $rg_link_local = "(" . $rg_url . ")"; $rg_link_http = "h(ttps?://" . $rg_url . ")"; // IMAGES // [http.png] / [http.png|right] $CON = preg_replace('#\[' . $rg_img_http . '(\|(right|left))?\]#', 'xx$1', $CON); // [local.png] / [local.png|left] $CON = preg_replace('#\[' . $rg_img_local . '(\|(right|left))?\]#', '$1', $CON); // image link [http://wikiss.tuxfamily.org/img/logo_100.png|http://wikiss.tuxfamily.org/img/logo_100.png] // [http|http] $CON = preg_replace('#\[' . $rg_img_http . '\|' . $rg_link_http . '(\|(right|left))?\]#U', 'xx$3', $CON); // [http|local] $CON = preg_replace('#\[' . $rg_img_http . '\|' . $rg_link_local . '(\|(right|left))?\]#U', '$3', $CON); // [local|http] $CON = preg_replace('#\[' . $rg_img_local . '\|' . $rg_link_http . '(\|(right|left))?\]#U', 'xx$3', $CON); // [local|local] $CON = preg_replace('#\[' . $rg_img_local . '\|' . $rg_link_local . '(\|(right|left))?\]#U', '$3', $CON); $CON = preg_replace('#([0-9a-zA-Z\./~\-_]+@[0-9a-z/~\-_]+\.[0-9a-z\./~\-_]+)#i', '$0', $CON); // mail recognition // LINKS $CON = preg_replace('#\[([^\]]+)\|' . $rg_link_http . '\]#U', '$1', $CON); // local links has to start either with / or ./ $CON = preg_replace('#\[([^\]]+)\|\.\/' . $rg_link_local . '\]#U', '$1', $CON); $CON = preg_replace('#' . $rg_link_http . '#i', 'xx$1', $CON); $CON = preg_replace('#xxttp#', 'http', $CON); $CON = preg_replace('#\[\?(.*)\]#Ui', '$1', $CON); // Wikipedia preg_match_all("/\[([^|\]]+\|)?([^\]#]+)(#[^\]]+)?\]/", $CON, $matches, PREG_SET_ORDER); // matching Wiki links foreach($matches as $match) { if(empty($match[1])) // is page label same as its name? $match[1] = $match[2]; else $match[1] = rtrim($match[1], "|"); if($match[3]) // link to the heading $match[3] = "#" . preg_replace("/[^\da-z]/i", "_", urlencode(substr($match[3], 1, strlen($match[3]) - 1))); if(file_exists($PAGES_DIR . "$match[2].txt")) $CON = str_replace($match[0], '' . $match[1] . '', $CON); else $CON = str_replace($match[0], '' . $match[1] . '', $CON); } // LIST, ordered, unordered $CON = preg_replace('/^\*\*\*(.*)(\n)/Um', "$2", $CON); $CON = preg_replace('/^\*\*(.*)(\n)/Um', "$2", $CON); $CON = preg_replace('/^\*(.*)(\n)/Um', "$2", $CON); $CON = preg_replace('/^\#\#\#(.*)(\n)/Um', "
      1. $1
$2", $CON); $CON = preg_replace('/^\#\#(.*)(\n)/Um', "
    1. $1
$2", $CON); $CON = preg_replace('/^\#(.*)(\n)/Um', "
  1. $1
$2", $CON); // Fixing crappy job of parsing *** and ###. 3 times for 3 levels. for($i = 0; $i < 3; $i++) $CON = preg_replace('/(<\/ol>\n*
    |<\/ul>\n*