root/tags/1.1.5.2/wikka.php

Revision 925, 37.2 KB (checked in by DarTar, 3 years ago)

Uploading past releases as tags, fixes #661

Line 
1<?php
2/*
3
4Copyright (c) 2002, Hendrik Mans <hendrik@mans.de>
5Copyright 2003 Carlo Zottmann
6Copyright 2004 Jason Tourtelotte <wikka-admin@jsnx.com>
7All rights reserved.
8
9Redistribution and use in source and binary forms, with or without
10modification, are permitted provided that the following conditions
11are met:
121. Redistributions of source code must retain the above copyright
13   notice, this list of conditions and the following disclaimer.
142. Redistributions in binary form must reproduce the above copyright
15   notice, this list of conditions and the following disclaimer in the
16   documentation and/or other materials provided with the distribution.
173. The name of the author may not be used to endorse or promote products
18   derived from this software without specific prior written permission.
19
20THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31*/
32
33ob_start(); 
34
35//error_reporting(E_ALL);
36error_reporting (E_ALL ^ E_NOTICE);
37
38// Do not change the version number or you will have problems upgrading.
39define("WAKKA_VERSION", "1.1.5.2");
40function getmicrotime() { 
41        list($usec, $sec) = explode(" ", microtime()); 
42        return ((float)$usec + (float)$sec); 
43} 
44
45$tstart = getmicrotime();
46
47class Wakka
48{
49        var $dblink;
50        var $page;
51        var $tag;
52        var $queryLog = array();
53        var $interWiki = array();
54        var $VERSION;
55
56        // constructor
57        function Wakka($config)
58        {
59                $this->config = $config;
60                $this->dblink = @mysql_connect($this->config["mysql_host"], $this->config["mysql_user"], $this->config["mysql_password"]);
61            if ($this->dblink) 
62            { 
63                if (!@mysql_select_db($this->config["mysql_database"], $this->dblink)) 
64                  { 
65                        @mysql_close($this->dblink); 
66                        $this->dblink = false; 
67                  } 
68            }
69                $this->VERSION = WAKKA_VERSION;
70        }
71
72        // DATABASE
73        function Query($query)
74        {
75                $start = $this->GetMicroTime();
76                if (!$result = mysql_query($query, $this->dblink))
77                {
78                        ob_end_clean();
79                        die("Query failed: ".$query." (".mysql_error().")");
80                }
81                if($this->GetConfigValue("sql_debugging"))
82                {
83                        $time = $this->GetMicroTime() - $start;
84                        $this->queryLog[] = array(
85                                "query"         => $query,
86                                "time"          => $time);
87                }
88                return $result;
89        }
90        function LoadSingle($query) { if ($data = $this->LoadAll($query)) return $data[0]; }
91        function LoadAll($query)
92        {
93        $data=array();
94                if ($r = $this->Query($query))
95                {
96                        while ($row = mysql_fetch_assoc($r)) $data[] = $row;
97                        mysql_free_result($r);
98                }
99                return $data;
100        }
101        function CheckMySQLVersion($major, $minor, $subminor)
102        {
103                $result = @mysql_query('SELECT VERSION() AS version'); 
104                if ($result != FALSE && @mysql_num_rows($result) > 0) 
105                { 
106                        $row   = mysql_fetch_array($result); 
107                        $match = explode('.', $row['version']); 
108                } 
109                else 
110                { 
111                        $result = @mysql_query('SHOW VARIABLES LIKE \'version\''); 
112                        if ($result != FALSE && @mysql_num_rows($result) > 0) { 
113                                $row   = mysql_fetch_row($result); 
114                                $match = explode('.', $row[1]); 
115                        } else { 
116                                return 0; 
117                        } 
118                } 
119
120                $mysql_major = $match[0];
121                $mysql_minor = $match[1];
122                $mysql_subminor = $match[2][0].$match[2][1];
123
124                if ($mysql_major > $major) {
125                        return 1; 
126                } else {
127                        if (($mysql_major == $major) && ($mysql_minor >= $minor) && ($mysql_subminor >= $subminor)) {
128                                return 1; 
129                        } else {
130                                return 0; 
131                        }
132                }
133        }
134
135        // MISC
136        function GetMicroTime() { list($usec, $sec) = explode(" ",microtime()); return ((float)$usec + (float)$sec); }
137        function IncludeBuffered($filename, $notfoundText = "", $vars = "", $path = "")
138        {
139                if ($path) $dirs = explode(":", $path);
140                else $dirs = array("");
141
142                foreach($dirs as $dir)
143                {
144                        if ($dir) $dir .= "/";
145                        $fullfilename = $dir.$filename;
146                        if (file_exists($fullfilename))
147                        {
148                                if (is_array($vars)) extract($vars);
149
150                                ob_start();
151                                include($fullfilename);
152                                $output = ob_get_contents();
153                                ob_end_clean();
154                                return $output;
155                        }
156                }
157                if ($notfoundText) return $notfoundText;
158                else return false;
159        }
160        function ReturnSafeHTML($html)
161        {
162                require_once('safehtml/classes/HTMLSax.php');
163                require_once('safehtml/classes/safehtml.php');
164
165                // Save all "<" symbols
166                $filtered_output = preg_replace("/<(?=[^a-zA-Z\/\!\?\%])/", "&lt;", $html);
167                       
168                // Opera6 bug workaround
169                $filtered_output = str_replace("\xC0\xBC", "&lt;", $filtered_output);
170
171                // Instantiate the handler
172                $handler=& new safehtml();
173
174                // Instantiate the parser
175                $parser=& new XML_HTMLSax();
176
177                // Register the handler with the parser
178                $parser->set_object($handler);
179
180                // Set the handlers
181                $parser->set_element_handler('openHandler','closeHandler');
182                $parser->set_data_handler('dataHandler');
183                $parser->set_escape_handler('escapeHandler');
184
185                $parser->parse($filtered_output);
186
187                $filtered_output = $handler->getXHTML(); 
188                return $filtered_output;
189        }
190        // VARIABLES
191        function GetPageTag() { return $this->tag; }
192        function GetPageTime() { return $this->page["time"]; }
193        function GetMethod() { return $this->method; }
194        function GetConfigValue($name) { return (isset($this->config[$name])) ? $this->config[$name] : null; }
195        function GetWakkaName() { return $this->GetConfigValue("wakka_name"); }
196        function GetWakkaVersion() { return $this->VERSION; }
197
198        // PAGES
199        function LoadPage($tag, $time = "", $cache = 1) {
200                // retrieve from cache
201                if (!$time && $cache) {
202                        $page = isset($this->pageCache[$tag]) ? $this->pageCache[$tag] : null; 
203                        if ($page=="cached_nonexistent_page") return null;
204                }
205                // load page
206                if (!isset($page)) $page = $this->LoadSingle("select * from ".$this->config["table_prefix"]."pages where tag = '".mysql_real_escape_string($tag)."' ".($time ? "and time = '".mysql_real_escape_string($time)."'" : "and latest = 'Y'")." limit 1");
207                // cache result
208                if ($page && !$time) {
209                        $this->pageCache[$page["tag"]] = $page;
210                } elseif (!$page) {
211                        $this->pageCache[$tag] = "cached_nonexistent_page";
212                }
213                return $page;
214        }
215        function IsLatestPage() { 
216                return $this->latest;
217        }
218        function GetCachedPage($tag) { return (isset($this->pageCache[$tag])) ? $this->pageCache[$tag] : null; }
219        function CachePage($page) { $this->pageCache[$page["tag"]] = $page; }
220        function SetPage($page) { $this->page = $page; if ($this->page["tag"]) $this->tag = $this->page["tag"]; }
221        function LoadPageById($id) { return $this->LoadSingle("select * from ".$this->config["table_prefix"]."pages where id = '".mysql_real_escape_string($id)."' limit 1"); }
222        function LoadRevisions($page) { return $this->LoadAll("select * from ".$this->config["table_prefix"]."pages where tag = '".mysql_real_escape_string($page)."' order by time desc"); }
223        function LoadPagesLinkingTo($tag) { return $this->LoadAll("select from_tag as tag from ".$this->config["table_prefix"]."links where to_tag = '".mysql_real_escape_string($tag)."' order by tag"); }
224        function LoadRecentlyChanged()
225        {
226                if ($pages = $this->LoadAll("select * from ".$this->config["table_prefix"]."pages where latest = 'Y' order by time desc"))
227                {
228                        foreach ($pages as $page)
229                        {
230                                $this->CachePage($page);
231                        }
232                        return $pages;
233                }
234        }
235        function LoadWantedPages() { return $this->LoadAll("select distinct ".$this->config["table_prefix"]."links.to_tag as tag,count(".$this->config["table_prefix"]."links.from_tag) as count from ".$this->config["table_prefix"]."links left join ".$this->config["table_prefix"]."pages on ".$this->config["table_prefix"]."links.to_tag = ".$this->config["table_prefix"]."pages.tag where ".$this->config["table_prefix"]."pages.tag is NULL group by tag order by count desc"); }
236        function IsWantedPage($tag) 
237        {
238                if ($pages = $this->LoadWantedPages())
239                {
240                        foreach ($pages as $page)
241                        {
242                                if ($page["tag"] == $tag) return true;
243                        }
244                }
245                return false;
246        }
247        function LoadOrphanedPages() { return $this->LoadAll("select distinct tag from ".$this->config["table_prefix"]."pages left join ".$this->config["table_prefix"]."links on ".$this->config["table_prefix"]."pages.tag = ".$this->config["table_prefix"]."links.to_tag where ".$this->config["table_prefix"]."links.to_tag is NULL order by tag"); }
248        function LoadPageTitles() { return $this->LoadAll("select distinct tag from ".$this->config["table_prefix"]."pages order by tag"); }
249        function LoadAllPages() { return $this->LoadAll("select * from ".$this->config["table_prefix"]."pages where latest = 'Y' order by tag"); }
250        // function FullTextSearch($phrase) { return $this->LoadAll("select * from ".$this->config["table_prefix"]."pages where latest = 'Y' and match(tag, body) against('".mysql_real_escape_string($phrase)."')"); }
251        function FullTextSearch($phrase)
252        {
253                $data = "";
254                if ($this->CheckMySQLVersion(4,00,01))
255                {       
256                        $data = $this->LoadAll("select * from "
257                        .$this->config["table_prefix"]
258                        ."pages where latest = 'Y' and match(tag, body) against('".mysql_real_escape_string($phrase)
259                        ."' IN BOOLEAN MODE) order by time DESC");
260                }
261                // else if ($this->CheckMySQLVersion(3,23,23))
262                // {
263                //      $data = $this->LoadAll("select * from "
264                //      .$this->config["table_prefix"]
265                //      ."pages where latest = 'Y' and
266                //                match(tag, body)
267                //                against('".mysql_real_escape_string($phrase)."')
268                //                order by time DESC");
269                // }
270
271                /* if no results perform a more general search */
272                if (!$data)  {
273                                $data = $this->LoadAll("select * from "
274                                .$this->config["table_prefix"]
275                                ."pages where latest = 'Y' and
276                                  (tag like '%".mysql_real_escape_string($phrase)."%' or
277                                   body like '%".mysql_real_escape_string($phrase)."%')
278                                   order by time DESC");
279                }
280               
281                return($data);
282        }
283        function FullCategoryTextSearch($phrase) { return $this->LoadAll("select * from ".$this->config["table_prefix"]."pages where latest = 'Y' and match(body) against('".mysql_real_escape_string($phrase)."' IN BOOLEAN MODE)"); }
284        function SavePage($tag, $body, $note)
285        {
286                // get current user
287                $user = $this->GetUserName();
288
289                // TODO: check write privilege
290                if ($this->HasAccess("write", $tag))
291                {
292                        // is page new?
293                        if (!$oldPage = $this->LoadPage($tag))
294                        {
295                                // current user is owner if user is logged in, otherwise, no owner.
296                                if ($this->GetUser()) $owner = $user;
297                        }
298                        else
299                        {
300                                // aha! page isn't new. keep owner!
301                                $owner = $oldPage["owner"];
302                        }
303
304                        // set all other revisions to old
305                        $this->Query("update ".$this->config["table_prefix"]."pages set latest = 'N' where tag = '".mysql_real_escape_string($tag)."'");
306
307                        // add new revision
308                        $this->Query("insert into ".$this->config["table_prefix"]."pages set ".
309                                "tag = '".mysql_real_escape_string($tag)."', ".
310                                "time = now(), ".
311                                "owner = '".mysql_real_escape_string($owner)."', ".
312                                "user = '".mysql_real_escape_string($user)."', ".
313                                "note = '".mysql_real_escape_string($note)."', ".
314                                "latest = 'Y', ".
315                                "body = '".mysql_real_escape_string($body)."'"); 
316
317                        if ($pingdata = $this->GetPingParams($this->config["wikiping_server"], $tag, $user, $note))
318                                $this->WikiPing($pingdata);
319                }
320        }
321        function PageTitle() {
322                $title = "";
323                $pagecontent = $this->page["body"];
324                if (ereg( "(=){2,5}([^=\n]+)(=){2,5}", $pagecontent, $title)) {
325                        $formatting_tags = array("**", "//", "__", "##", "''", "++", "#%", "@@", "\"\"");
326                        $title = str_replace($formatting_tags, "", $title[2]);
327                }
328                if ($title) return $title;
329                else return $this->GetPageTag();
330        }
331
332        // WIKI PING  -- Coded by DreckFehler
333        function HTTPpost($host, $data, $contenttype="application/x-www-form-urlencoded", $maxAttempts = 5) {
334                $attempt =0; $status = 300; $result = "";
335                while ($status >= 300 && $status < 400 && $attempt++ <= $maxAttempts) {
336                        $url = parse_url($host);
337                        if (isset($url["path"]) == false) $url["path"] = "/";
338                        if (isset($url["port"]) == false) $url["port"] = 80;
339
340                        if ($socket = fsockopen ($url["host"], $url["port"], $errno, $errstr, 15)) {
341                                $strQuery = "POST ".$url["path"]." HTTP/1.1\n";
342                                $strQuery .= "Host: ".$url["host"]."\n";
343                                $strQuery .= "Content-Length: ".strlen($data)."\n";
344                                $strQuery .= "Content-Type: ".$contenttype."\n";
345                                $strQuery .= "Connection: close\n\n";
346                                $strQuery .= $data;
347
348                                // send request & get response
349                                fputs($socket, $strQuery);
350                                $bHeader = true;
351                                while (!feof($socket)) {
352                                        $strLine = trim(fgets($socket, 512));
353                                        if (strlen($strLine) == 0) $bHeader = false; // first empty line ends header-info
354                                        if ($bHeader) {
355                                                if (!$status) $status = $strLine;
356                                                if (preg_match("/^Location:\s(.*)/", $strLine, $matches)) $location = $matches[1];
357                                        } else $result .= trim($strLine)."\n";
358                                }
359                                fclose ($socket);
360                        } else $status = "999 timeout";
361
362                        if ($status) {
363                                if(preg_match("/(\d){3}/", $status, $matches)) $status = $matches[1];
364                        } else $status = 999;
365                        $host = $location;
366                }
367                if (preg_match("/^[\da-fA-F]+(.*)$/", $result, $matches)) $result = $matches[1];
368                return $result;
369        }
370        function WikiPing($ping, $debug = false) {
371                if ($ping) {
372                        $rpcRequest .= "<methodCall>\n";
373                        $rpcRequest .= "<methodName>wiki.ping</methodName>\n";
374                        $rpcRequest .= "<params>\n";
375                        $rpcRequest .= "<param>\n<value>\n<struct>\n";
376                        $rpcRequest .= "<member>\n<name>tag</name>\n<value>".$ping["tag"]."</value>\n</member>\n";
377                        $rpcRequest .= "<member>\n<name>url</name>\n<value>".$ping["taglink"]."</value>\n</member>\n";
378                        $rpcRequest .= "<member>\n<name>wiki</name>\n<value>".$ping["wiki"]."</value>\n</member>\n";
379                        if ($ping["author"]) {
380                                $rpcRequest .= "<member>\n<name>author</name>\n<value>".$ping["author"]."</value>\n</member>\n";
381                                if ($ping["authorpage"]) $rpcRequest .= "<member>\n<name>authorpage</name>\n<value>".$ping["authorpage"]."</value>\n</member>\n";
382                        }
383                        if ($ping["history"]) $rpcRequest .= "<member>\n<name>history</name>\n<value>".$ping["history"]."</value>\n</member>\n";
384                        if ($ping["changelog"]) $rpcRequest .= "<member>\n<name>changelog</name>\n<value>".$ping["changelog"]."</value>\n</member>\n";
385                        $rpcRequest .= "</struct>\n</value>\n</param>\n";
386                        $rpcRequest .= "</params>\n";
387                        $rpcRequest .= "</methodCall>\n";
388
389                        foreach (explode(" ", $ping["server"]) as $server) {
390                                $response = $this->HTTPpost($server, $rpcRequest, "text/xml");
391                                if ($debug) print $response;
392                        }
393                }
394        }
395        function GetPingParams($server, $tag, $user, $changelog = "") {
396                $ping = array();
397                if ($server) {
398                        $ping["server"] = $server;
399                        if ($tag) $ping["tag"] = $tag; else return false; // set page-title
400                        if (!$ping["taglink"] = $this->href("", $tag)) return false; // set page-url
401                if (!$ping["wiki"] = $this->config["wakka_name"]) return false; // set site-name
402                        $ping["history"] = $this->href("revisions", $tag); // set url to history
403
404                        if ($user) {
405                                $ping["author"] = $user; // set username
406                                if ($this->LoadPage($user)) $ping["authorpage"] = $this->href("", $user); // set link to user page
407                        }
408                        if ($changelog) $ping["changelog"] = $changelog;
409                        return $ping;
410                } else return false;
411        }
412
413        // COOKIES
414        function SetSessionCookie($name, $value) { SetCookie($name, $value, 0, "/"); $_COOKIE[$name] = $value; }
415        function SetPersistentCookie($name, $value) { SetCookie($name, $value, time() + 90 * 24 * 60 * 60, "/"); $_COOKIE[$name] = $value; }
416        function DeleteCookie($name) { SetCookie($name, "", 1, "/"); $_COOKIE[$name] = ""; }
417        function GetCookie($name) { return $_COOKIE[$name]; }
418
419        // HTTP/REQUEST/LINK RELATED
420        function SetMessage($message) { $_SESSION["message"] = $message; }
421        function GetMessage() { $message = $_SESSION["message"]; $_SESSION["message"] = ""; return $message; }
422        function Redirect($url) { header("Location: $url"); exit; }
423        // returns just PageName[/method].
424        function MiniHref($method = "", $tag = "") { if (!$tag = trim($tag)) $tag = $this->tag; return $tag.($method ? "/".$method : ""); }
425        // returns the full url to a page/method.
426        function Href($method = "", $tag = "", $params = "")
427        {
428                $href = $this->config["base_url"].$this->MiniHref($method, $tag);
429                if ($params)
430                {
431                        $href .= ($this->config["rewrite_mode"] ? "?" : "&amp;").$params;
432                }
433                return $href;
434        }
435        function Link($tag, $method = "", $text = "", $track = 1, $escapeText = 1, $title="") {
436                if (!$text) $text = $tag;
437                // escape text?
438                if ($escapeText) $text = htmlspecialchars($text);
439                $url = '';
440
441                // is this a wiki link?
442                if (preg_match("/^[A-Za-z0-9]+$/", $tag))
443                {
444                        if ($_SESSION["linktracking"] && $track) $this->TrackLinkTo($tag);
445                        $linkedPage = $this->LoadPage($tag);
446                        // return ($linkedPage ? "<a href=\"".$this->href($method, $linkedPage['tag'])."\">".$text."</a>" : "<span class=\"missingpage\">".$text."</span><a href=\"".$this->href("edit", $tag)."\" title=\"Create this page\">?</a>");
447                        return ($linkedPage ? "<a href=\"".$this->href($method, $linkedPage['tag'])."\" title=\"$title\">".$text."</a>" : "<a href=\"".$this->href("edit", $tag)."\" title=\"Create this page\"><span class=\"missingpage\">".$text."</span></a>");
448                } 
449                elseif (preg_match("/^(http|https|ftp):\/\/([^\\s\"<>]+)$/", $tag))
450                {
451                        $url = $tag; // this is a vaild external URL
452                }       
453                // is this an interwiki link?
454                elseif (preg_match("/^([A-Z,ÄÖÜ][A-Z,a-z,ÄÖÜ,ßäöü]+)[:]([A-Z,a-z,0-9,ÄÖÜ,ßäöü]*)$/", $tag, $matches))
455                {
456                        $url = $this->GetInterWikiUrl($matches[1], $matches[2]);
457                }
458                // is this a full link? ie, does it contain alpha-numeric characters?
459                elseif (preg_match("/[^[:alnum:],ÄÖÜ,ßäöü]/", $tag))
460                {
461                        // check for email addresses
462                        if (preg_match("/^.+\@.+$/", $tag))
463                        {
464                                $url = "mailto:".$tag;
465                        }
466                        // check for protocol-less URLs
467                        else if (!preg_match("/:/", $tag))
468                        {
469                                $url = "http://".$tag;
470                        }
471                }
472                $external_link_tail = $this->GetConfigValue("external_link_tail");
473                return $url ? "<a class=\"ext\" href=\"$url\">$text</a>$external_link_tail" : $text;
474        }
475
476        // function PregPageLink($matches) { return $this->Link($matches[1]); }
477        function IsWikiName($text) { return preg_match("/^[A-Z,ÄÖÜ][a-z,ßäöü]+[A-Z,0-9,ÄÖÜ][A-Z,a-z,0-9,ÄÖÜ,ßäöü]*$/", $text); }
478        function TrackLinkTo($tag) { $_SESSION["linktable"][] = $tag; }
479        function GetLinkTable() { return $_SESSION["linktable"]; }
480        function ClearLinkTable() { $_SESSION["linktable"] = array(); }
481        function StartLinkTracking() { $_SESSION["linktracking"] = 1; }
482        function StopLinkTracking() { $_SESSION["linktracking"] = 0; }
483        function WriteLinkTable()
484        {
485                // delete old link table
486                $this->Query("delete from ".$this->config["table_prefix"]."links where from_tag = '".mysql_real_escape_string($this->GetPageTag())."'");
487                if ($linktable = $this->GetLinkTable())
488                {
489                        $from_tag = mysql_real_escape_string($this->GetPageTag());
490                        foreach ($linktable as $to_tag)
491                        {
492                                $lower_to_tag = strtolower($to_tag);
493                                if (!$written[$lower_to_tag])
494                                {
495                                        $this->Query("insert into ".$this->config["table_prefix"]."links set from_tag = '".$from_tag."', to_tag = '".mysql_real_escape_string($to_tag)."'");
496                                        $written[$lower_to_tag] = 1;
497                                }
498                        }
499                }
500        }
501        function Header() { return $this->Action($this->GetConfigValue("header_action"), 0); } 
502        function Footer() { return $this->Action($this->GetConfigValue("footer_action"), 0); }
503
504        // FORMS
505        function FormOpen($method = "", $tag = "", $formMethod = "post")
506        {
507                $result = "<form action=\"".$this->href($method, $tag)."\" method=\"".$formMethod."\">\n";
508                if (!$this->config["rewrite_mode"]) $result .= "<input type=\"hidden\" name=\"wakka\" value=\"".$this->MiniHref($method, $tag)."\" />\n";
509                return $result;
510        }
511        function FormClose()
512        {
513                return "</form>\n";
514        }
515
516        // INTERWIKI STUFF
517        function ReadInterWikiConfig()
518        {
519                if ($lines = file("interwiki.conf"))
520                {
521                        foreach ($lines as $line)
522                        {
523                                if ($line = trim($line))
524                                {
525                                        list($wikiName, $wikiUrl) = explode(" ", trim($line));
526                                        $this->AddInterWiki($wikiName, $wikiUrl);
527                                }
528                        }
529                }
530        }
531        function AddInterWiki($name, $url)
532        {
533                $this->interWiki[strtolower($name)] = $url;
534        }
535        function GetInterWikiUrl($name, $tag) {
536                if (isset($this->interWiki[strtolower($name)]))
537                {
538                        return $this->interWiki[strtolower($name)].$tag;
539                } 
540        }
541
542        // REFERRERS
543        function LogReferrer($tag = "", $referrer = "")
544        {
545                // fill values
546                if (!$tag = trim($tag)) $tag = $this->GetPageTag();
547                if (!$referrer = trim($referrer)) $referrer = $_SERVER["HTTP_REFERER"];
548
549                // check if it's coming from another site
550                if ($referrer && !preg_match("/^".preg_quote($this->GetConfigValue("base_url"), "/")."/", $referrer))
551                {
552                        $parsed_url = parse_url($referrer);
553                        $spammer = $parsed_url["host"];
554                        $blacklist = $this->LoadSingle("select * from ".$this->config["table_prefix"]."referrer_blacklist WHERE spammer = '".mysql_real_escape_string($spammer)."'");
555                        if (!$blacklist) {
556                        $this->Query("insert into ".$this->config["table_prefix"]."referrers set ".
557                                "page_tag = '".mysql_real_escape_string($tag)."', ".
558                                "referrer = '".mysql_real_escape_string($referrer)."', ".
559                                "time = now()");
560                        }
561                }
562        }
563        function LoadReferrers($tag = "")
564        {
565                return $this->LoadAll("select referrer, count(referrer) as num from ".$this->config["table_prefix"]."referrers ".($tag = trim($tag) ? "where page_tag = '".mysql_real_escape_string($tag)."'" : "")." group by referrer order by num desc");
566        }
567
568        // PLUGINS
569        function Action($action, $forceLinkTracking = 0)
570        {
571                $action = trim($action);
572                $vars=array();
573
574                // only search for parameters if there is a space
575                if (is_int(strpos($action, ' ')))
576                {
577                        // treat everything after the first whitespace as parameter
578                        preg_match("/^([A-Za-z0-9]*)\s+(.*)$/", $action, $matches);
579                        // extract $action and $vars_temp ("raw" attributes)
580                        list(, $action, $vars_temp) = $matches;
581
582                        if ($action) { 
583                                // match all attributes (key and value)
584                                preg_match_all("/([A-Za-z0-9]*)=\"(.*)\"/U", $vars_temp, $matches);
585       
586                                // prepare an array for extract() to work with (in $this->IncludeBuffered())
587                                if (is_array($matches)) {
588                                        for ($a = 0; $a < count($matches[0]); $a++) {
589                                        $vars[$matches[1][$a]] = $matches[2][$a];
590                                        }
591                                }
592                                $vars["wikka_vars"] = trim($vars_temp); // <<< add the buffered parameter-string to the array
593                        } else {
594                                return "<span class='error'><em>Unknown action; the action name must not contain special characters.</em></span>"; // <<< the pattern ([A-Za-z0-9])\s+ didn't match!
595                        }
596                }
597                if (!preg_match("/^[a-zA-Z0-9]+$/", $action)) return "<span class='error'><em>Unknown action; the action name must not contain special characters.</em></span>";
598                if (!$forceLinkTracking) $this->StopLinkTracking();
599                $result = $this->IncludeBuffered(strtolower($action).".php", "<em>Unknown action \"$action\"</em>", $vars, $this->config["action_path"]);
600                $this->StartLinkTracking();
601                return $result;
602        }
603        function Method($method)
604        {
605                if (strstr('/', $method))
606                {
607                        $method = substr($method, strrpos('/', $method));
608                }
609                if (!$handler = $this->page["handler"]) $handler = "page";
610                $methodLocation = $handler."/".$method.".php";
611                return $this->IncludeBuffered($methodLocation, "<em>Unknown method \"$methodLocation\"</em>", "", $this->config["handler_path"]);
612        }
613        function Format($text, $formatter = "wakka") { return $this->IncludeBuffered("formatters/".$formatter.".php", "<em>Formatter \"$formatter\" not found</em>", compact("text")); }
614
615        // USERS
616        function LoadUser($name, $password = 0) { return $this->LoadSingle("select * from ".$this->config["table_prefix"]."users where name = '".mysql_real_escape_string($name)."' ".($password === 0 ? "" : "and password = '".mysql_real_escape_string($password)."'")." limit 1"); }
617        function LoadUsers() { return $this->LoadAll("select * from ".$this->config["table_prefix"]."users order by name"); }
618        function GetUserName() { if ($user = $this->GetUser()) $name = $user["name"]; else if (!$name = gethostbyaddr($_SERVER["REMOTE_ADDR"])) $name = $_SERVER["REMOTE_ADDR"]; return $name; }
619        function GetUser() { return (isset($_SESSION["user"])) ? $_SESSION["user"] : null; }
620        function SetUser($user) { $_SESSION["user"] = $user; $this->SetPersistentCookie("name", $user["name"]); $this->SetPersistentCookie("password", $user["password"]); }
621        function LogoutUser() { $_SESSION["user"] = ""; $this->DeleteCookie("name"); $this->DeleteCookie("password"); }
622        function UserWantsComments() { if (!$user = $this->GetUser()) return false; return ($user["show_comments"] == "Y"); }
623
624
625        // COMMENTS
626        function LoadComments($tag) { return $this->LoadAll("SELECT * FROM ".$this->config["table_prefix"]."comments WHERE page_tag = '".mysql_real_escape_string($tag)."' ORDER BY time"); }
627        function LoadRecentComments($limit = 50) { return $this->LoadAll("SELECT * FROM ".$this->config["table_prefix"]."comments ORDER BY time DESC LIMIT ".$limit); }
628        function LoadRecentlyCommented($limit = 50)
629        {
630                $sql = "SELECT comments.id, comments.page_tag, comments.time, comments.comment, comments.user"
631                . " FROM ".$this->config["table_prefix"]."comments AS comments"
632                . " LEFT JOIN ".$this->config["table_prefix"]."comments AS c2 ON comments.page_tag = c2.page_tag AND comments.id < c2.id"
633                . " WHERE c2.page_tag IS NULL "
634                . " ORDER BY time DESC "
635                . " LIMIT ".$limit; 
636                return $this->LoadAll($sql);
637        }
638        function SaveComment($page_tag, $comment)
639        {
640                // get current user
641                $user = $this->GetUserName();
642
643                // add new comment
644                $this->Query("INSERT INTO ".$this->config["table_prefix"]."comments SET ".
645                        "page_tag = '".mysql_real_escape_string($page_tag)."', ".
646                        "time = now(), ".
647                        "comment = '".mysql_real_escape_string($comment)."', ".
648                        "user = '".mysql_real_escape_string($user)."'");
649        }
650
651        // ACCESS CONTROL
652        // returns true if logged in user is owner of current page, or page specified in $tag
653        function UserIsOwner($tag = "")
654        {
655                // check if user is logged in
656                if (!$this->GetUser()) return false;
657
658                // if user is admin, return true. Admin can do anything!
659                if ($this->IsAdmin()) return true;
660
661                // set default tag
662                if (!$tag = trim($tag)) $tag = $this->GetPageTag();
663
664                // check if user is owner
665                if ($this->GetPageOwner($tag) == $this->GetUserName()) return true;
666        }
667        //returns true if user is listed in configuration list as admin
668        function IsAdmin() {
669                $adminstring = $this->config["admin_users"];
670                $adminarray = explode(',' , $adminstring);
671       
672                foreach ($adminarray as $admin) {
673                        if (trim($admin) == $this->GetUserName()) return true;
674                }
675        }
676        function GetPageOwner($tag = "", $time = "") { if (!$tag = trim($tag)) $tag = $this->GetPageTag(); if ($page = $this->LoadPage($tag, $time)) return $page["owner"]; }
677        function SetPageOwner($tag, $user)
678        {
679                // check if user exists
680                if( $user <> '' && ($this->LoadUser($user) || $user == "(Public)" || $user == "(Nobody)"))
681                {
682                        if ($user == "(Nobody)") $user = ""; 
683                        // update latest revision with new owner
684                        $this->Query("update ".$this->config["table_prefix"]."pages set owner = '".mysql_escape_string($user)."' where tag = '".mysql_escape_string($tag)."' and latest = 'Y' limit 1");
685                }
686        }
687        function LoadACL($tag, $privilege, $useDefaults = 1)
688        {
689                if ((!$acl = $this->LoadSingle("SELECT ".mysql_real_escape_string($privilege)."_acl FROM ".$this->config["table_prefix"]."acls WHERE page_tag = '".mysql_real_escape_string($tag)."' LIMIT 1")) && $useDefaults)
690                {
691                        $acl = array("page_tag" => $tag, $privilege."_acl" => $this->GetConfigValue("default_".$privilege."_acl"));
692                }
693                return $acl;
694        }
695        function LoadAllACLs($tag, $useDefaults = 1)
696        {
697                if ((!$acl = $this->LoadSingle("SELECT * FROM ".$this->config["table_prefix"]."acls WHERE page_tag = '".mysql_real_escape_string($tag)."' LIMIT 1")) && $useDefaults)
698                {
699                        $acl = array("page_tag" => $tag, "read_acl" => $this->GetConfigValue("default_read_acl"), "write_acl" => $this->GetConfigValue("default_write_acl"), "comment_acl" => $this->GetConfigValue("default_comment_acl"));
700                }
701                return $acl;
702        }
703        function SaveACL($tag, $privilege, $list) {
704                if ($this->LoadACL($tag, $privilege, 0)) $this->Query("UPDATE ".$this->config["table_prefix"]."acls SET ".mysql_real_escape_string($privilege)."_acl = '".mysql_real_escape_string(trim(str_replace("\r", "", $list)))."' WHERE page_tag = '".mysql_real_escape_string($tag)."' LIMIT 1");
705                else $this->Query("INSERT INTO ".$this->config["table_prefix"]."acls SET page_tag = '".mysql_real_escape_string($tag)."', ".mysql_real_escape_string($privilege)."_acl = '".mysql_real_escape_string(trim(str_replace("\r", "", $list)))."'");
706        }
707        function TrimACLs($list) {
708                foreach (explode("\n", $list) as $line)
709                {
710                        $line = trim($line);
711                        $trimmed_list .= $line."\n";
712                }
713                return $trimmed_list;
714        }
715        // returns true if $user (defaults to current user) has access to $privilege on $page_tag (defaults to current page)
716        function HasAccess($privilege, $tag = "", $user = "")
717        {
718                // set defaults
719                if (!$tag) $tag = $this->GetPageTag();
720                if (!$user) $user = $this->GetUserName();
721
722                // if current user is owner, return true. owner can do anything!
723                if ($this->UserIsOwner($tag)) return true;
724
725                // see whether user is registered and logged in
726                if ($this->GetUser()) $registered = true;
727
728                // load acl
729                if ($tag == $this->GetPageTag())
730                {       
731                        $acl = $this->ACLs[$privilege."_acl"];
732                }
733                else
734                {
735                        $tag_ACLs = $this->LoadAllACLs($tag);
736                        $acl = $tag_ACLs[$privilege."_acl"];
737                }
738
739                // fine fine... now go through acl
740                foreach (explode("\n", $acl) as $line)
741                {
742                        // check for inversion character "!"
743                        if (preg_match("/^[!](.*)$/", $line, $matches))
744                        {
745                                $negate = 1;
746                                $line = $matches[1];
747                        }
748                        else
749                        {
750                                $negate = 0;
751                        }
752
753                        // if there's still anything left... lines with just a "!" don't count!
754                        if ($line)
755                        {
756                                switch ($line[0])
757                                {
758                                // comments
759                                case "#":
760                                        break;
761                                // everyone
762                                case "*":
763                                        return !$negate;
764                                // only registered users
765                                case "+":
766                                        // return ($registered) ? !$negate : false;
767                                        return ($registered) ? !$negate : $negate;
768                                // aha! a user entry.
769                                default:
770                                        if ($line == $user)
771                                        {
772                                                return !$negate;
773                                        }
774                                }
775                        }
776                }
777
778                // tough luck.
779                return false;
780        }
781
782        // MAINTENANCE
783        function Maintenance()
784        {
785                // purge referrers
786                if ($days = $this->GetConfigValue("referrers_purge_time")) {
787                        $this->Query("DELETE FROM ".$this->config["table_prefix"]."referrers WHERE time < date_sub(now(), interval '".mysql_real_escape_string($days)."' day)");
788                }
789
790                // purge old page revisions
791                if ($days = $this->GetConfigValue("pages_purge_time")) {
792                        $this->Query("delete from ".$this->config["table_prefix"]."pages where time < date_sub(now(), interval '".mysql_real_escape_string($days)."' day) and latest = 'N'");
793                }
794        }
795
796        // THE BIG EVIL NASTY ONE!
797        function Run($tag, $method = "")
798        {
799                // do our stuff!
800                if (!$this->method = trim($method)) $this->method = "show";
801                if (!$this->tag = trim($tag)) $this->Redirect($this->href("", $this->config["root_page"]));
802                if ((!$this->GetUser() && isset($_COOKIE["name"])) && ($user = $this->LoadUser($_COOKIE["name"], $_COOKIE["password"]))) $this->SetUser($user);
803                $this->SetPage($this->LoadPage($tag, (isset($_REQUEST["time"]) ? $_REQUEST["time"] :'')));
804
805                $this->LogReferrer();
806                $this->ACLs = $this->LoadAllACLs($this->tag);
807                $this->ReadInterWikiConfig();
808                if(!($this->GetMicroTime()%3)) $this->Maintenance(); 
809
810                if (preg_match('/\.(xml|mm)$/', $this->method))
811                {
812                        header("Content-type: text/xml");
813                        print($this->Method($this->method));
814                }
815                elseif (preg_match('/\.(gif|jpg|png)$/', $this->method))
816                {
817                        header('Location: images/' . $this->method);
818                }
819                elseif (preg_match('/\.css$/', $this->method))
820                {
821                        header('Location: css/' . $this->method);
822                }
823                else
824                {
825                        print($this->Header().$this->Method($this->method).$this->Footer());
826                }
827        }
828}
829
830// stupid version check
831if (!isset($_REQUEST)) die('$_REQUEST[] not found. Wakka requires PHP 4.1.0 or higher!');
832
833// workaround for the amazingly annoying magic quotes.
834function magicQuotesSuck(&$a)
835{
836        if (is_array($a))
837        {
838                foreach ($a as $k => $v)
839                {
840                        if (is_array($v))
841                                magicQuotesSuck($a[$k]);
842                        else
843                                $a[$k] = stripslashes($v);
844                }
845        }
846}
847set_magic_quotes_runtime(0);
848if (get_magic_quotes_gpc())
849{
850        magicQuotesSuck($_POST);
851        magicQuotesSuck($_GET);
852        magicQuotesSuck($_COOKIE);
853}
854
855
856// default configuration values
857$wakkaDefaultConfig = array(
858        "mysql_host"                    => "localhost",
859        "mysql_database"                        => "wikka",
860        "mysql_user"                    => "wikka",
861        "table_prefix"                  => "wikka_",
862
863        "root_page"                             => "HomePage",
864        "wakka_name"                    => "MyWikkaSite",
865        "base_url"                              => "http://".$_SERVER["SERVER_NAME"].($_SERVER["SERVER_PORT"] != 80 ? ":".$_SERVER["SERVER_PORT"] : "").$_SERVER["REQUEST_URI"].(preg_match("/".preg_quote("wikka.php")."$/", $_SERVER["REQUEST_URI"]) ? "?wakka=" : ""),
866        "rewrite_mode"                  => (preg_match("/".preg_quote("wikka.php")."$/", $_SERVER["REQUEST_URI"]) ? "0" : "1"),
867
868        "action_path"                   => "actions",
869        "handler_path"                  => "handlers",
870        "gui_editor"                    => 1,
871        "stylesheet"                    => "wikka.css",
872
873        "header_action"                 => "header",
874        "footer_action"                 => "footer",
875       
876        "navigation_links" => "[[CategoryCategory Categories]] :: PageIndex ::  RecentChanges :: RecentlyCommented :: [[UserSettings Login/Register]]",
877        "logged_in_navigation_links" => "[[CategoryCategory Categories]] :: PageIndex :: RecentChanges :: RecentlyCommented :: [[UserSettings Change settings/Logout]]", 
878
879        "referrers_purge_time"          => 30,
880        "pages_purge_time"              => 0,
881        "xml_recent_changes"            => 10,
882        "hide_comments"                 => 0,
883        "anony_delete_own_comments"     => 1,
884        "double_doublequote_html"       => "safe",
885        "external_link_tail"            => "<span class='exttail'>&#8734;</span>",
886        "sql_debugging"                 => 0,
887        "admin_users"                   => "",
888        "admin_email"                   => "",
889        "upload_path"                   => "uploads",
890        "mime_types"                    => "mime_types.txt",
891
892        "wikiping_server"               => "",
893
894        "default_write_acl"             => "*",
895        "default_read_acl"              => "*",
896        "default_comment_acl"           => "*");
897
898
899// load config
900if (file_exists("wakka.config.php")) rename("wakka.config.php", "wikka.config.php");
901if (!$configfile = GetEnv("WAKKA_CONFIG")) $configfile = "wikka.config.php";
902if (file_exists($configfile)) include($configfile);
903
904$wakkaConfigLocation = $configfile;
905$wakkaConfig = array_merge($wakkaDefaultConfig, $wakkaConfig);
906
907// check for locking
908if (file_exists("locked")) {
909        // read password from lockfile
910        $lines = file("locked");
911        $lockpw = trim($lines[0]);
912
913        // is authentification given?
914        if (isset($_SERVER["PHP_AUTH_USER"])) {
915                if (!(($_SERVER["PHP_AUTH_USER"] == "admin") && ($_SERVER["PHP_AUTH_PW"] == $lockpw))) {
916                        $ask = 1;
917                }
918        } else {
919                $ask = 1;
920        }
921
922        if ($ask) {
923                header("WWW-Authenticate: Basic realm=\"".$wakkaConfig["wakka_name"]." Install/Upgrade Interface\"");
924                header("HTTP/1.0 401 Unauthorized");
925                print("This site is currently being upgraded. Please try again later.");
926                exit;
927    }
928}
929
930// compare versions, start installer if necessary
931if ($wakkaConfig["wakka_version"] != WAKKA_VERSION)
932{
933        // start installer
934        if (!$installAction = trim($_REQUEST["installAction"])) $installAction = "default";
935        include("setup/header.php");
936        if (file_exists("setup/".$installAction.".php")) include("setup/".$installAction.".php"); else print("<em>Invalid action</em>");
937        include("setup/footer.php");
938        exit;
939}
940
941// start session
942session_start();
943
944// fetch wakka location
945$wakka = $_REQUEST["wakka"];
946
947// remove leading slash
948$wakka = preg_replace("/^\//", "", $wakka);
949
950// split into page/method
951if (preg_match("#^(.+?)/(.*)$#", $wakka, $matches)) list(, $page, $method) = $matches;
952else if (preg_match("#^(.*)$#", $wakka, $matches)) list(, $page) = $matches;
953
954// create wakka object
955$wakka = new Wakka($wakkaConfig);
956// check for database access
957if (!$wakka->dblink) 
958{ 
959        echo "<p>The wiki is currently unavailable. <br /><br />Error: Unable to connect to the MySQL database.</p>"; 
960      exit; 
961} 
962
963
964// go!
965if (!isset($method)) $method='';
966$wakka->Run($page, $method);
967if (!preg_match("/\.(xml|raw|mm)$/", $method))
968{
969           $tend = getmicrotime();
970        //Calculate the difference
971            $totaltime = ($tend - $tstart);     
972        //Output result
973            printf ("<div class=\"smallprint\">Page was generated in %.4f seconds</div>\n</body>\n</html>", $totaltime);
974}
975
976$content =  ob_get_contents();
977if (strstr ($HTTP_SERVER_VARS['HTTP_ACCEPT_ENCODING'], 'gzip') && function_exists('gzencode') )
978{ 
979   // Tell the browser the content is compressed with gzip
980        header ("Content-Encoding: gzip"); 
981        $page_output = gzencode($content); 
982        $page_length = strlen($page_output); 
983} else {
984        $page_output = $content;
985        $page_length = strlen($page_output); 
986}
987
988// header("Cache-Control: pre-check=0");
989header("Cache-Control: no-cache");
990// header("Pragma: ");
991// header("Expires: ");
992
993$etag =  md5($content);
994header('ETag: '.$etag); 
995
996header('Content-Length: '.$page_length); 
997ob_end_clean(); 
998echo $page_output;
999
1000?>
Note: See TracBrowser for help on using the browser.