Subversion Repositories Applications.wikini

Rev

Rev 54 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
45 mathias 1
<?php
2
/*
3
$Id: wakka.php 864 2007-11-28 12:44:52Z nepote $
4
Copyright (c) 2002, Hendrik Mans <hendrik@mans.de>
5
Copyright 2003 Carlo Zottmann
6
Copyright 2002, 2003, 2005 David DELON
7
Copyright 2002, 2003, 2004, 2006 Charles N?POTE
8
Copyright 2002, 2003 Patrick PAUL
9
Copyright 2003 Eric DELORD
10
Copyright 2003 Eric FELDSTEIN
11
Copyright 2004-2006 Jean-Christophe ANDR?
12
Copyright 2005-2006 Didier LOISEAU
13
All rights reserved.
14
Redistribution and use in source and binary forms, with or without
15
modification, are permitted provided that the following conditions
16
are met:
17
1. Redistributions of source code must retain the above copyright
18
notice, this list of conditions and the following disclaimer.
19
2. Redistributions in binary form must reproduce the above copyright
20
notice, this list of conditions and the following disclaimer in the
21
documentation and/or other materials provided with the distribution.
22
3. The name of the author may not be used to endorse or promote products
23
derived from this software without specific prior written permission.
24
 
25
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
 */
36
 
37
/*
38
	Yes, most of the formatting used in this file is HORRIBLY BAD STYLE. However,
39
	most of the action happens outside of this file, and I really wanted the code
40
	to look as small as what it does. Basically. Oh, I just suck. :)
41
 */
42
 
43
 
44
 
45
// do not change this line, you fool. In fact, don't change anything! Ever!
46
define("WAKKA_VERSION", "0.1.1");
47
define("WIKINI_VERSION", "0.5.0");
48
require 'includes/constants.php';
49
include 'includes/urlutils.inc.php';
50
 
51
// start the compute time
52
list($g_usec, $g_sec) = explode(" ",microtime());
53
define ("t_start", (float)$g_usec + (float)$g_sec);
54
$t_SQL=0;
55
 
56
 
57
 
58
class Wiki
59
{
60
	var $dblink;
61
	var $page;
62
	var $tag;
63
	var $parameter = array();
64
	var $queryLog = array();
65
	var $interWiki = array();
66
	var $VERSION;
67
	var $CookiePath = '/';
68
	var $inclusions = array();
69
	/**
70
	 * an array containing all the actions that are implemented by an object
71
	 * @access private
72
	 */
73
	var $actionObjects;
74
 
75
	// LinkTrackink
76
	var $isTrackingLinks = false;
77
	var $linktable = array();
78
 
79
	var $pageCache = array();
80
	var $_groupsCache = array();
81
	var $_actionsAclsCache = array();
82
 
83
	// constructor
84
	function Wiki($config)
85
	{
86
		$this->config = $config;
87
		// some host do not allow mysql_pconnect
88
		$this->dblink = @mysql_connect (
89
			$this->config["mysql_host"],
90
			$this->config["mysql_user"],
91
			$this->config["mysql_password"]);
92
		if ($this->dblink)
93
		{
94
			if (!@mysql_select_db($this->config["mysql_database"], $this->dblink))
95
			{
96
				@mysql_close($this->dblink);
97
				$this->dblink = false;
98
			}
99
		}
100
		$this->VERSION = WAKKA_VERSION;
101
 
102
		// determine le chemin pour les cookies
103
		$a = parse_url($this->GetConfigValue('base_url'));
104
		$this->CookiePath = dirname($a['path']);
105
		// Fixe la gestion des cookie sous les OS utilisant le \ comme s?parteur de chemin
106
		$this->CookiePath = str_replace("\\","/",$this->CookiePath);
107
		// ajoute un '/' terminal sauf si on est ? la racine web
108
		if ($this->CookiePath != '/') $this->CookiePath .= '/';
109
	}
110
 
111
 
112
 
113
	// DATABASE
114
	function Query($query)
115
	{
116
		if($this->GetConfigValue("debug")) $start = $this->GetMicroTime();
117
		if (!$result = mysql_query($query, $this->dblink))
118
		{
119
			ob_end_clean();
120
			die("Query failed: ".$query." (".mysql_error().")");
121
		}
122
		if($this->GetConfigValue("debug"))
123
		{
124
			$time = $this->GetMicroTime() - $start;
125
			$this->queryLog[] = array(
126
				"query"		=> $query,
127
				"time"		=> $time);
128
		}
129
		return $result;
130
	}
131
	function LoadSingle($query) {
132
		if ($data = $this->LoadAll($query)) return $data[0];
133
		return null;
134
	}
135
	function LoadAll($query)
136
	{
137
		$data=array();
138
		if ($r = $this->Query($query))
139
		{
140
			while ($row = mysql_fetch_assoc($r)) $data[] = $row;
141
			mysql_free_result($r);
142
		}
143
		return $data;
144
	}
145
 
146
 
147
 
148
	// MISC
149
	function GetMicroTime()
150
	{
151
		list($usec, $sec) = explode(" ",microtime()); return ((float)$usec + (float)$sec);
152
	}
153
	function IncludeBuffered($filename, $notfoundText = "", $vars = "", $path = "")
154
	{
155
		if ($path) $dirs = explode(":", $path);
156
		else $dirs = array("");
157
 
158
		foreach($dirs as $dir)
159
		{
160
			if ($dir) $dir .= "/";
161
			$fullfilename = $dir.$filename;
162
			if (file_exists($fullfilename))
163
			{
164
				if (is_array($vars)) extract($vars);
165
 
166
				ob_start();
167
				include($fullfilename);
168
				$output = ob_get_contents();
169
				ob_end_clean();
170
				return $output;
171
			}
172
		}
173
		if ($notfoundText) return $notfoundText;
174
		else return false;
175
	}
176
 
177
 
178
 
179
	// VARIABLES
180
	function GetPageTag() { return $this->tag; }
181
	function GetPageTime() { return $this->page["time"]; }
182
	function GetMethod() { return $this->method; }
183
	function GetConfigValue($name) { return isset($this->config[$name]) ? trim($this->config[$name]) : ''; }
184
	function GetWakkaName() { return $this->GetConfigValue("wakka_name"); }
185
	function GetWakkaVersion() { return $this->VERSION; }
186
	function GetWikiNiVersion() { return WIKINI_VERSION; }
187
 
188
	/**
189
	 * Retrieves all the triples that match some criteria.
190
	 * This allows to search triples by their approximate resource or property names.
191
	 * The allowed operators are the sql LIKE and the sql =
192
	 * @param string $resource The resource of the triples
193
	 * @param string $property The property of the triple to retrieve or null
194
	 * @param string $res_op The operator of comparison between the effective resource and $resource (default: 'LIKE')
195
	 * @param string $prop_op The operator of comparison between the effective property and $property (default: '=')
196
	 * @return array The list of all the triples that match the asked criteria
197
	 */
198
	function GetMatchingTriples($resource, $property = null, $res_op = 'LIKE', $prop_op = '=')
199
	{
200
		static $operators = array('=', 'LIKE'); // we might want to add other operators later
201
		$res_op = strtoupper($res_op);
202
		if (!in_array($res_op, $operators)) $res_op = '=';
203
		$sql = 'SELECT * FROM ' . $this->GetConfigValue('table_prefix') . 'triples '
204
			. 'WHERE resource ' . $res_op . ' "' . addslashes($resource) . '"';
205
		if ($property !== null)
206
		{
207
			$prop_op = strtoupper($prop_op);
208
			if (!in_array($prop_op, $operators)) $prop_op = '=';
209
			$sql .= ' AND property ' . $prop_op . ' "' . addslashes($property) . '"';
210
		}
211
		return $this->LoadAll($sql);
212
	}
213
 
214
	/**
215
	 * Retrieves all the values for a given couple (resource, property)
216
	 * @param string $resource The resource of the triples
217
	 * @param string $property The property of the triple to retrieve
218
	 * @param string $re_prefix The prefix to add to $resource (defaults to THISWIKI_PREFIX)
219
	 * @param string $prop_prefix The prefix to add to $property (defaults to WIKINI_VOC_PREFIX)
220
	 * @return array An array of the retrieved values, in the form
221
	 * array(
222
	 * 	0 => array(id = 7 , 'value' => $value1),
223
	 * 	1 => array(id = 34, 'value' => $value2),
224
	 * 	...
225
	 * )
226
	 */
227
	function GetAllTriplesValues($resource, $property, $re_prefix = THISWIKI_PREFIX, $prop_prefix = WIKINI_VOC_PREFIX)
228
	{
229
		$sql = 'SELECT id, value FROM ' . $this->GetConfigValue('table_prefix') . 'triples '
230
			. 'WHERE resource = "' . addslashes($re_prefix . $resource) . '" '
231
			. 'AND property = "' . addslashes($prop_prefix . $property) . '" ';
232
		return $this->LoadAll($sql);
233
	}
234
 
235
	/**
236
	 * Retrieves a single value for a given couple (resource, property)
237
	 * @param string $resource The resource of the triples
238
	 * @param string $property The property of the triple to retrieve
239
	 * @param string $re_prefix The prefix to add to $resource (defaults to <tt>THISWIKI_PREFIX</tt>)
240
	 * @param string $prop_prefix The prefix to add to $property (defaults to <tt>WIKINI_VOC_PREFIX</tt>)
241
	 * @return string The value corresponding to ($resource, $property) or null if
242
	 * there is no such couple in the triples table.
243
	 */
244
	function GetTripleValue($resource, $property, $re_prefix = THISWIKI_PREFIX, $prop_prefix = WIKINI_VOC_PREFIX)
245
	{
246
		$res = $this->GetAllTriplesValues($resource, $property, $re_prefix, $prop_prefix);
247
		if ($res) return $res[0]['value'];
248
		return null;
249
	}
250
 
251
	/**
252
	 * Checks whether a triple exists or not
253
	 * @param string $resource The resource of the triple to find
254
	 * @param string $property The property of the triple to find
255
	 * @param string $value The value of the triple to find
256
	 * @param string $re_prefix The prefix to add to $resource (defaults to <tt>THISWIKI_PREFIX</tt>)
257
	 * @param string $prop_prefix The prefix to add to $property (defaults to <tt>WIKINI_VOC_PREFIX</tt>)
258
	 * @param int The id of the found triple or 0 if there is no such triple.
259
	 */
260
	function TripleExists($resource, $property, $value, $re_prefix = THISWIKI_PREFIX, $prop_prefix = WIKINI_VOC_PREFIX)
261
	{
262
		$sql = 'SELECT id FROM ' . $this->GetConfigValue('table_prefix') . 'triples '
263
			. 'WHERE resource = "' . addslashes($re_prefix . $resource) . '" '
264
			. 'AND property = "' . addslashes($prop_prefix . $property) . '" '
265
			. 'AND value = "' . addslashes($value) . '"';
266
		$res = $this->LoadSingle($sql);
267
		if (!$res) return 0;
268
		return $res['id'];
269
	}
270
 
271
	/**
272
	 * Inserts a new triple ($resource, $property, $value) in the triples' table
273
	 * @param string $resource The resource of the triple to insert
274
	 * @param string $property The property of the triple to insert
275
	 * @param string $value The value of the triple to insert
276
	 * @param string $re_prefix The prefix to add to $resource (defaults to <tt>THISWIKI_PREFIX</tt>)
277
	 * @param string $prop_prefix The prefix to add to $property (defaults to <tt>WIKINI_VOC_PREFIX</tt>)
278
	 * @return int An error code: 0 (success), 1 (failure) or 3 (already exists)
279
	 */
280
	function InsertTriple($resource, $property, $value, $re_prefix = THISWIKI_PREFIX, $prop_prefix = WIKINI_VOC_PREFIX)
281
	{
282
		if ($this->TripleExists($resource, $property, $value, $re_prefix, $prop_prefix))
283
		{
284
			return 3;
285
		}
286
		$sql = 'INSERT INTO ' . $this->GetConfigValue('table_prefix') . 'triples (resource, property, value)'
287
			. 'VALUES ("' . addslashes($re_prefix . $resource) . '", "'
288
				. addslashes($prop_prefix . $property) . '", "'
289
				. addslashes($value) . '")';
290
		return $this->Query($sql) ? 0 : 1;
291
	}
292
 
293
	/**
294
	 * Updates a triple ($resource, $property, $value) in the triples' table
295
	 * @param string $resource The resource of the triple to update
296
	 * @param string $property The property of the triple to update
297
	 * @param string $oldvalue The old value of the triple to update
298
	 * @param string $newvalue The new value of the triple to update
299
	 * @param string $re_prefix The prefix to add to $resource (defaults to <tt>THISWIKI_PREFIX</tt>)
300
	 * @param string $prop_prefix The prefix to add to $property (defaults to <tt>WIKINI_VOC_PREFIX</tt>)
301
	 * @return int An error code: 0 (succ?s), 1 (?chec),
302
	 * 		2 ($resource, $property, $oldvalue does not exist)
303
	 * 		or 3 ($resource, $property, $newvalue already exists)
304
	 */
305
	function UpdateTriple($resource, $property, $oldvalue, $newvalue, $re_prefix = THISWIKI_PREFIX, $prop_prefix = WIKINI_VOC_PREFIX)
306
	{
307
		$id = $this->TripleExists($resource, $property, $oldvalue, $re_prefix, $prop_prefix);
308
		if (!$id) return 2;
309
		if ($this->TripleExists($resource, $property, $newvalue, $re_prefix, $prop_prefix))
310
		{
311
			return 3;
312
		}
313
		$sql = 'UPDATE ' . $this->GetConfigValue('table_prefix') . 'triples '
314
			. 'SET value = "' . addslashes($newvalue) . '" '
315
			. 'WHERE id = ' . $id;
316
		return $this->Query($sql) ? 0 : 1;
317
	}
318
 
319
	/**
320
	 * Deletes a triple ($resource, $property, $value) from the triples' table
321
	 * @param string $resource The resource of the triple to delete
322
	 * @param string $property The property of the triple to delete
323
	 * @param string $value The value of the triple to delete. If set to <tt>null</tt>,
324
	 * deletes all the triples corresponding to ($resource, $property). (defaults to <tt>null</tt>)
325
	 * @param string $re_prefix The prefix to add to $resource (defaults to <tt>THISWIKI_PREFIX</tt>)
326
	 * @param string $prop_prefix The prefix to add to $property (defaults to <tt>WIKINI_VOC_PREFIX</tt>)
327
	 */
328
	function DeleteTriple($resource, $property, $value = null, $re_prefix = THISWIKI_PREFIX, $prop_prefix = WIKINI_VOC_PREFIX)
329
	{
330
		$sql = 'DELETE FROM ' . $this->GetConfigValue('table_prefix') . 'triples '
331
			. 'WHERE resource = "' . addslashes($re_prefix . $resource) . '" '
332
			. 'AND property = "' . addslashes($prop_prefix . $property) . '" ';
333
		if ($value !== null) $sql .= 'AND value = "' . addslashes($value) . '"';
334
		$this->Query($sql);
335
	}
336
 
337
	// inclusions
338
	/**
339
	 * Enr?gistre une nouvelle inclusion dans la pile d'inclusions.
340
	 *
341
	 * @param string $pageTag Le nom de la page qui va ?tre inclue
342
	 * @return int Le nombre d'?l?ments dans la pile
343
	 */
344
	function RegisterInclusion($pageTag)
345
	{
346
		return array_unshift($this->inclusions, strtolower(trim($pageTag)));
347
	}
348
	/**
349
	 * Retire le dernier ?l?ment de la pile d'inclusions.
350
	 *
351
	 * @return string Le nom de la page dont l'inclusion devrait se terminer.
352
	 * null s'il n'y a plus d'inclusion dans la pile.
353
	 */
354
	function UnregisterLastInclusion()
355
	{
356
		return array_shift($this->inclusions);
357
	}
358
	/**
359
	 * Renvoie le nom de la page en cours d'inclusion.
360
	 *
361
	 * @example // dans le cas d'une action comme l'ActionEcrivezMoi
362
	 * if($inc = $this->CurrentInclusion() && strtolower($this->GetPageTag()) != $inc)
363
	 * 	echo 'Cette action ne peut ?tre appel?e depuis une page inclue';
364
	 * @return string Le nom (tag) de la page (en minuscules)
365
	 * false si la pile est vide.
366
	 */
367
	function GetCurrentInclusion()
368
	{
369
		return isset($this->inclusions[0]) ? $this->inclusions[0]: false ;
370
	}
371
	/**
372
	 * V?rifie si on est ? l'int?rieur d'une inclusion par $pageTag (sans tenir compte de la casse)
373
	 *
374
	 * @param string $pageTag Le nom de la page ? v?rifier
375
	 * @return bool True si on est ? l'int?rieur d'une inclusion par $pageTag (false sinon)
376
	 */
377
	function IsIncludedBy($pageTag)
378
	{
379
		return in_array(strtolower($pageTag), $this->inclusions);
380
	}
381
	/**
382
	 *
383
	 * @return array La pile d'inclusions
384
	 * L'?l?ment 0 sera la derni?re inclusion, l'?l?ment 1 sera son parent et ainsi de suite.
385
	 */
386
	function GetAllInclusions()
387
	{
388
		return $this->inclusions;
389
	}
390
	/**
391
	 * Remplace la pile des inclusions par une nouvelle pile (par d?faut une pile vide)
392
	 * Permet de formatter une page sans tenir compte des inclusions pr?c?dentes.
393
	 *
394
	 * @param array $ La nouvelle pile d'inclusions.
395
	 * L'?l?ment 0 doit repr?senter la derni?re inclusion, l'?l?ment 1 son parent et ainsi de suite.
396
	 * @return array L'ancienne pile d'inclusions, avec les noms des pages en minuscules.
397
	 */
398
	function SetInclusions($pile = array())
399
	{
400
		$temp = $this->inclusions;
401
		$this->inclusions = $pile;
402
		return $temp;
403
	}
404
 
405
	// PAGES
406
	function LoadPage($tag, $time = "", $cache = 1)
407
	{
408
		// retrieve from cache
409
		if (!$time && $cache && (($cachedPage = $this->GetCachedPage($tag)) !== false))
410
		{
411
			$page = $cachedPage;
412
		}
413
		else // load page
414
		{
415
			$sql = "SELECT * FROM ".$this->config["table_prefix"]."pages"
416
				. " WHERE tag = '".mysql_real_escape_string($tag)."' AND "
417
				. ($time ? "time = '".mysql_real_escape_string($time)."'" : "latest = 'Y'") . " LIMIT 1";
418
			$page = $this->LoadSingle($sql);
419
			// cache result
420
			if (!$time) $this->CachePage($page, $tag);
421
		}
422
		return $page;
423
	}
424
	/**
425
	 * Retrieves the cached version of a page.
426
	 *
427
	 * Notice that this method null or false, use
428
	 * 	$this->GetCachedPage($tag) === false
429
	 * to check if a page is not in the cache.
430
	 * @return mixed The cached version of a page:
431
	 * 	- the page DB line if the page exists and is in cache
432
	 * 	- null if the cache knows that the page does not exists
433
	 * 	- false is the cache does not know the page
434
	 */
435
	function GetCachedPage($tag) {return (array_key_exists($tag, $this->pageCache) ? $this->pageCache[$tag] : false); }
436
	/**
437
	 * Caches a page's DB line.
438
	 *
439
	 * @param array $page The page (full) DB line or null if the page does not exists
440
	 * @param string $pageTag The tag of the page to cache. Defaults to $page['tag'] but is mendatory when $page === null
441
	 */
442
	function CachePage($page, $pageTag = null) {
443
		if ($pageTag === null)
444
		{
445
			$pageTag = $page["tag"];
446
		}
447
		$this->pageCache[$pageTag] = $page;
448
	}
449
	function SetPage($page) { $this->page = $page; if ($this->page["tag"]) $this->tag = $this->page["tag"]; }
450
	function LoadPageById($id) { return $this->LoadSingle("select * from ".$this->config["table_prefix"]."pages where id = '".mysql_real_escape_string($id)."' limit 1"); }
451
	function LoadRevisions($page) { return $this->LoadAll("select * from ".$this->config["table_prefix"]."pages where tag = '".mysql_real_escape_string($page)."' order by time desc"); }
452
	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"); }
453
	function LoadRecentlyChanged($limit=50)
454
	{
455
		$limit= (int) $limit;
456
		if ($pages = $this->LoadAll("select id, tag, time, user, owner from ".$this->config["table_prefix"]."pages where latest = 'Y' and comment_on = '' order by time desc limit $limit"))
457
		{
458
			foreach ($pages as $page)
459
			{
460
				$this->CachePage($page);
461
			}
462
			return $pages;
463
		}
464
	}
465
	function LoadAllPages() { return $this->LoadAll("select * from ".$this->config["table_prefix"]."pages where latest = 'Y' order by tag"); }
466
	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)."')"); }
467
	function LoadWantedPages() {
468
		$p = $this->config["table_prefix"];
469
		$r = "SELECT ${p}links.to_tag AS tag, COUNT(${p}links.from_tag) AS count "
470
			. "FROM ${p}links LEFT JOIN ${p}pages ON ${p}links.to_tag = ${p}pages.tag "
471
			. "WHERE ${p}pages.tag IS NULL GROUP BY ${p}links.to_tag ORDER BY count DESC, tag ASC";
472
		return $this->LoadAll($r);
473
	}
474
	function LoadOrphanedPages() { return $this->LoadAll("select distinct tag from ".$this->config["table_prefix"]."pages as p left join ".$this->config["table_prefix"]."links as l on p.tag = l.to_tag where l.to_tag is NULL and p.comment_on = '' and p.latest = 'Y' order by tag"); }
475
	function IsOrphanedPage($tag) { return $this->LoadAll("select distinct tag from ".$this->config['table_prefix']."pages as p left join ".$this->config['table_prefix']."links as l on p.tag = l.to_tag where l.to_tag is NULL and p.latest = 'Y' and tag = '".mysql_real_escape_string($tag)."'"); }
476
	function DeleteOrphanedPage($tag) {
477
		$p = $this->config["table_prefix"];
478
		$this->Query("DELETE FROM ${p}pages WHERE tag='".mysql_real_escape_string($tag)."' OR comment_on='".mysql_real_escape_string($tag)."'");
479
		$this->Query("DELETE FROM ${p}links WHERE from_tag='".mysql_real_escape_string($tag)."' ");
480
		$this->Query("DELETE FROM ${p}acls WHERE page_tag='".mysql_real_escape_string($tag)."' ");
481
		$this->Query("DELETE FROM ${p}referrers WHERE page_tag='".mysql_real_escape_string($tag)."' ");
482
	}
483
 
484
	/**
485
	 * SavePage
486
	 * Sauvegarde un contenu dans une page donn?e
487
	 *
488
	 * @param string $body Contenu ? sauvegarder dans la page
489
	 * @param string $tag Nom de la page
490
	 * @param string $comment_on Indication si c'est un commentaire
491
	 * @param boolean $bypass_acls Indication si on bypasse les droits d'?criture
492
	 * @return int Code d'erreur : 0 (succ?s), 1 (l'utilisateur n'a pas les droits)
493
	 */
494
	function SavePage($tag, $body, $comment_on = "", $bypass_acls = false)
495
	{
496
		// get current user
497
		$user = $this->GetUserName();
498
 
499
		// check bypass of rights or write privilege
500
		$rights = $bypass_acls || ($comment_on ? $this->HasAccess("comment", $comment_on) : $this->HasAccess("write", $tag));
501
		if ($rights)
502
		{
503
			// is page new?
504
			if (!$oldPage = $this->LoadPage($tag))
505
			{
506
				// create default write acl. store empty write ACL for comments.
507
				$this->SaveAcl($tag, "write", ($comment_on ? $user : $this->GetConfigValue("default_write_acl")));
508
 
509
				// create default read acl
510
				$this->SaveAcl($tag, "read", $this->GetConfigValue("default_read_acl"));
511
 
512
				// create default comment acl.
513
				$this->SaveAcl($tag, "comment", ($comment_on ? "" : $this->GetConfigValue("default_comment_acl")));
514
 
515
				// current user is owner; if user is logged in! otherwise, no owner.
516
				if ($this->GetUser()) $owner = $user;
517
				else $owner = '';
518
			}
519
			else
520
			{
521
				// aha! page isn't new. keep owner!
522
				$owner = $oldPage["owner"];
523
 
524
				// ...and comment_on, eventualy?
525
				if ($comment_on == '') $comment_on = $oldPage['comment_on'];
526
			}
527
 
528
 
529
			// set all other revisions to old
530
			$this->Query("update ".$this->config["table_prefix"]."pages set latest = 'N' where tag = '".mysql_real_escape_string($tag)."'");
531
 
532
			// add new revision
533
			$this->Query("insert into ".$this->config["table_prefix"]."pages set ".
534
				"tag = '".mysql_real_escape_string($tag)."', ".
535
				($comment_on ? "comment_on = '".mysql_real_escape_string($comment_on)."', " : "").
536
				"time = now(), ".
537
				"owner = '".mysql_real_escape_string($owner)."', ".
538
				"user = '".mysql_real_escape_string($user)."', ".
539
				"latest = 'Y', ".
540
				"body = '".mysql_real_escape_string(chop($body))."'");
541
 
542
			unset($this->pageCache[$tag]);
543
			return 0;
544
		}
545
		else return 1;
546
	}
547
 
548
 
549
	/**
550
	 * AppendContentToPage
551
	 * Ajoute du contenu ? la fin d'une page
552
	 *
553
	 * @param string $content Contenu ? ajouter ? la page
554
	 * @param string $page Nom de la page
555
	 * @param boolean $bypass_acls Boul?en pour savoir s'il faut bypasser les ACLs
556
	 * @return int Code d'erreur : 0 (succ?s), 1 (pas de contenu sp?cifi?)
557
	 */
558
	function AppendContentToPage($content, $page, $bypass_acls = false)
559
	{
560
		// Si un contenu est sp?cifi?
561
		if (isset($content))
562
		{
563
			// -- D?termine quelle est la page :
564
			//    -- pass?e en param?tre (que se passe-t'il si elle n'existe pas ?)
565
			//    -- ou la page en cours par d?faut
566
			$page = isset($page) ? $page : $this->GetPageTag();
567
 
568
			// -- Chargement de la page
569
			$result = $this->LoadPage($page);
570
			$body = $result['body'];
571
			// -- Ajout du contenu ? la fin de la page
572
			$body .= $content;
573
 
574
			// -- Sauvegarde de la page
575
			// TODO : que se passe-t-il si la page est pleine ou si l'utilisateur n'a pas les droits ?
576
			$this->SavePage($page, $body, "", $bypass_acls);
577
 
578
			// now we render it internally so we can write the updated link table.
579
			$this->ClearLinkTable();
580
			$this->StartLinkTracking();
581
			$temp = $this->SetInclusions();
582
			$this->RegisterInclusion($this->GetPageTag()); // on simule totalement un affichage normal
583
			$this->Format($body);
584
			$this->SetInclusions($temp);
585
			if($user = $this->GetUser())
586
			{
587
				$this->TrackLinkTo($user['name']);
588
			}
589
			if($owner = $this->GetPageOwner())
590
			{
591
				$this->TrackLinkTo($owner);
592
			}
593
			$this->StopLinkTracking();
594
			$this->WriteLinkTable();
595
			$this->ClearLinkTable();/**/
596
 
597
			// Retourne 0 seulement si tout c'est bien pass?
598
			return 0;
599
		}
600
		else return 1;
601
	}
602
 
603
	/**
604
	 * LogAdministrativeAction($user, $content, $page = "")
605
	 *
606
	 * @param string $user Utilisateur
607
	 * @param string $content Contenu de l'enregistrement
608
	 * @param string $page Page de log
609
	 *
610
	 * @return int Code d'erreur : 0 (succ?s), 1 (pas de contenu sp?cifi?)
611
	 */
612
	function LogAdministrativeAction($user, $content, $page = "")
613
	{
614
		$order   = array("\r\n", "\n", "\r");
615
		$replace = '\\n';
616
		$content = str_replace($order, $replace, $content);
617
		$contentToAppend = "\n" . date("Y-m-d H:i:s") . " . . . . " . $user . " . . . . " . $content . "\n";
618
		$page = $page ? $page : "LogDesActionsAdministratives" . date("Ymd");
619
		return $this->AppendContentToPage($contentToAppend, $page, true);
620
	}
621
 
622
 
623
	/**
624
	 * Make the purge of page versions that are older than the last version older than 3 "pages_purge_time"
625
	 * This method permits to allways keep a version that is older than that period.
626
	 */
627
	function PurgePages() {
628
		if ($days = $this->GetConfigValue("pages_purge_time")) { // is purge active ?
629
			// let's search which pages versions we have to remove
630
			// this is necessary beacause even MySQL does not handel multi-tables deletes before version 4.0
631
			$wnPages = $this->GetConfigValue('table_prefix') . 'pages';
632
			$sql = 'SELECT DISTINCT a.id FROM ' . $wnPages . ' a,' . $wnPages . ' b WHERE a.latest = \'N\' AND a.time < date_sub(now(), INTERVAL \'' . addslashes($days) . '\' DAY) AND a.tag = b.tag AND a.time < b.time AND b.time < date_sub(now(), INTERVAL \'' . addslashes($days) . '\' DAY)';
633
			$ids = $this->LoadAll($sql);
634
 
635
			if (count($ids)) { // there are some versions to remove from DB
636
				// let's build one big request, that's better...
637
				$sql = 'DELETE FROM ' . $wnPages . ' WHERE id IN (';
638
				foreach($ids as $key => $line){
639
					$sql .= ($key ? ', ':'') . $line['id']; // NB.: id is an int, no need of quotes
640
				}
641
				$sql .= ')';
642
 
643
				// ... and send it !
644
				$this->Query($sql);
645
			}
646
		}
647
	}
648
 
649
 
650
 
651
	// COOKIES
652
	function SetSessionCookie($name, $value)
653
	{
654
		SetCookie($name, $value, 0, $this->CookiePath);
655
		$_COOKIE[$name] = $value;
656
	}
657
	function SetPersistentCookie($name, $value, $remember = 0)
658
	{
659
		SetCookie($name, $value, time() + ($remember ? 90*24*60*60 : 60 * 60), $this->CookiePath);
660
		$_COOKIE[$name] = $value;
661
	}
662
	function DeleteCookie($name)
663
	{
664
		SetCookie($name, "", 1, $this->CookiePath);
665
		$_COOKIE[$name] = "";
666
	}
667
	function GetCookie($name) { return $_COOKIE[$name]; }
668
 
669
 
670
 
671
	// HTTP/REQUEST/LINK RELATED
672
	function SetMessage($message) { $_SESSION["message"] = $message; }
673
	function GetMessage()
674
	{
675
		if (isset($_SESSION["message"])) $message = $_SESSION["message"];
676
		else $message = "";
677
		$_SESSION["message"] = "";
678
		return $message;
679
	}
680
	function Redirect($url)
681
	{
682
		header("Location: $url");
683
		exit;
684
	}
685
	// returns just PageName[/method].
686
	function MiniHref($method = "", $tag = "")
687
	{
688
		if (!$tag = trim($tag)) $tag = $this->tag;
689
		return $tag.($method ? "/".$method : "");
690
	}
691
	// returns the full url to a page/method.
692
	function Href($method = "", $tag = "", $params = "", $htmlspchars = true)
693
	{
694
		$href = $this->config["base_url"].$this->MiniHref($method, $tag);
695
		if ($params)
696
		{
697
			$href .= ($this->config["rewrite_mode"] ? "?" : ($htmlspchars ? "&amp;" : '&')).$params;
698
		}
699
		return $href;
700
	}
701
	function Link($tag, $method = "", $text = "", $track = 1)
702
	{
703
		$displayText = $text ? $text : $tag;
704
		// is this an interwiki link?
705
		if (preg_match('/^' . WN_INTERWIKI_CAPTURE . '$/', $tag, $matches))
706
		{
707
			if ($tagInterWiki = $this->GetInterWikiUrl($matches[1], $matches[2])) {
708
				return '<a href="'.htmlspecialchars($tagInterWiki).'">'
709
					.htmlspecialchars($displayText).' (interwiki)</a>';
710
			}
711
			else return '<a href="'.htmlspecialchars($tag).'">'
712
				.htmlspecialchars($displayText).' (interwiki inconnu)</a>';
713
		}
714
		// is this a full link? ie, does it contain non alpha-numeric characters?
715
		// Note : [:alnum:] is equivalent [0-9A-Za-z]
716
		//		  [^[:alnum:]] means : some caracters other than [0-9A-Za-z]
717
		// For example : "www.adress.com", "mailto:adress@domain.com", "http://www.adress.com"
718
		else if (preg_match("/[^[:alnum:]]/", $tag))
719
		{
720
			// check for various modifications to perform on $tag
721
			if (preg_match("/^[\w.-]+\@[\w.-]+$/", $tag))
722
			{ // email addresses
723
				$tag = 'mailto:'.$tag;
724
			}
725
			// Note : in Perl regexp, (?: ... ) is a non-catching cluster
726
			else if (preg_match('/^[[:alnum:]][[:alnum:].-]*(?:\/|$)/', $tag))
727
			{ // protocol-less URLs
728
				$tag = 'http://'.$tag;
729
			}
730
			// Finally, block script schemes (see RFC 3986 about
731
			// schemes) and allow relative link & protocol-full URLs
732
			else if (preg_match('/^[a-z0-9.+-]*script[a-z0-9.+-]*:/i', $tag)
733
				|| !(preg_match('/^\.?\.?\//', $tag)
734
					|| preg_match('/^[a-z0-9.+-]+:\/\//i', $tag)))
735
			{
736
				// If does't fit, we can't qualify $tag as an URL.
737
				// There is a high risk that $tag is just XSS (bad
738
				// javascript: code) or anything nasty. So we must not
739
				// produce any link at all.
740
				return htmlspecialchars($tag.($text ? ' '.$text : ''));
741
			}
742
			// Important: Here, we know that $tag is not something bad
743
			// and that we must produce a link with it
744
 
745
			// An inline image? (text!=tag and url ends by png,gif,jpeg)
746
			if ($text and preg_match("/\.(gif|jpeg|png|jpg)$/i",$tag))
747
			{
748
				return '<img src="'.htmlspecialchars($tag)
749
					.'" alt="'.htmlspecialchars($displayText).'"/>';
750
			}
751
			else
752
			{
753
				// Even if we know $tag is harmless, we MUST encode it
754
				// in HTML with htmlspecialchars() before echoing it.
755
				// This is not about being paranoiac. This is about
756
				// being compliant to the HTML standard.
757
				return '<a href="'.htmlspecialchars($tag).'">'
758
					.htmlspecialchars($displayText).'</a>';
759
			}
760
		}
761
		else
762
		{
763
			// it's a Wiki link!
764
			if (!empty($track)) $this->TrackLinkTo($tag);
765
			if ($this->LoadPage($tag))
766
				return '<a href="'.htmlspecialchars($this->href($method, $tag)).'">'
767
					.htmlspecialchars($displayText).'</a>';
768
			else
769
				return '<span class="missingpage">'.htmlspecialchars($displayText)
770
				.'</span><a href="'.htmlspecialchars($this->href("edit", $tag)).'">?</a>';
771
		}
772
	}
773
	function ComposeLinkToPage($tag, $method = "", $text = "", $track = 1) {
774
		if (!$text) $text = $tag;
775
		$text = htmlspecialchars($text);
776
		if ($track)
777
			$this->TrackLinkTo($tag);
778
		return '<a href="'.$this->href($method, $tag).'">'.$text.'</a>';
779
	}
780
	function IsWikiName($text) {
781
		return preg_match('/^' . WN_CAMEL_CASE . '$/', $text);
782
	}
783
 
784
	// LinkTracking management
785
	/**
786
	 * Tracks the link to a given page (only if the LinkTracking is activated)
787
	 * @param string $tag The tag (name) of the page to track a link to.
788
	 */
789
	function TrackLinkTo($tag) {
790
		if ($this->LinkTracking()) $this->linktable[] = $tag;
791
	}
792
	/**
793
	 * @return array The current link tracking table
794
	 */
795
	function GetLinkTable() { return $this->linktable; }
796
	/**
797
	 * Clears the link tracking table
798
	 */
799
	function ClearLinkTable() { $this->linktable = array(); }
800
	/**
801
	 * Starts the LinkTracking
802
	 * @return bool The previous state of the link tracking
803
	 */
804
	function StartLinkTracking() {
805
		return $this->LinkTracking(true);
806
	}
807
	/**
808
	 * Stops the LinkTracking
809
	 * @return bool The previous state of the link tracking
810
	 */
811
	function StopLinkTracking() {
812
		return $this->LinkTracking(false);
813
	}
814
	/**
815
	 * Sets and/or retrieve the state of the LinkTracking
816
	 * @param bool $newStatus The new status of the LinkTracking
817
	 * (defaults to <tt>null</tt> which lets it unchanged)
818
	 * @return bool The previous state of the link tracking
819
	 */
820
	function LinkTracking($newStatus = null)
821
	{
822
		$old = $this->isTrackingLinks;
823
		if ($newStatus !== null) $this->isTrackingLinks = $newStatus;
824
		return $old;
825
	}
826
	function WriteLinkTable() {
827
		// delete old link table
828
		$this->Query("delete from ".$this->config["table_prefix"]."links where from_tag = '".mysql_real_escape_string($this->GetPageTag())."'");
829
		if ($linktable = $this->GetLinkTable())
830
		{
831
			$from_tag = mysql_real_escape_string($this->GetPageTag());
832
			foreach ($linktable as $to_tag)
833
			{
834
				$lower_to_tag = strtolower($to_tag);
835
				if (!isset($written[$lower_to_tag]))
836
				{
837
					$this->Query("insert into ".$this->config["table_prefix"]."links set from_tag = '".$from_tag."', to_tag = '".mysql_real_escape_string($to_tag)."'");
838
					$written[$lower_to_tag] = 1;
839
				}
840
			}
841
		}
842
	}
843
 
844
	function Header() {
845
		$action = $this->GetConfigValue("header_action");
846
		if (($actionObj = &$this->GetActionObject($action)) && is_object($actionObj))
847
		{
848
			return $actionObj->GenerateHeader();
849
		}
850
		return $this->Action($action, 1);
851
	}
852
 
853
	function Footer() {
854
		$action = $this->GetConfigValue("footer_action");
855
		if (($actionObj = &$this->GetActionObject($action)) && is_object($actionObj))
856
		{
857
			return $actionObj->GenerateFooter();
858
		}
859
		return $this->Action($action, 1);
860
	}
861
 
862
	// FORMS
863
	function FormOpen($method = "", $tag = "", $formMethod = "post") {
864
		$result = "<form action=\"".$this->href($method, $tag)."\" method=\"".$formMethod."\">\n";
865
		if (!$this->config["rewrite_mode"]) $result .= "<input type=\"hidden\" name=\"wiki\" value=\"".$this->MiniHref($method, $tag)."\" />\n";
866
		return $result;
867
	}
868
	function FormClose() {
869
		return "</form>\n";
870
	}
871
 
872
 
873
 
874
	// INTERWIKI STUFF
875
	function ReadInterWikiConfig() {
876
		if ($lines = file("interwiki.conf"))
877
		{
878
			foreach ($lines as $line)
879
			{
880
				if ($line = trim($line))
881
				{
882
					list($wikiName, $wikiUrl) = explode(" ", trim($line));
883
					$this->AddInterWiki($wikiName, $wikiUrl);
884
				}
885
			}
886
		}
887
	}
888
	function AddInterWiki($name, $url) {
889
		$this->interWiki[strtolower($name)] = $url;
890
	}
891
	function GetInterWikiUrl($name, $tag)
892
	{
893
		if (isset($this->interWiki[strtolower($name)])) return $this->interWiki[strtolower($name)].$tag;
894
		else return FALSE;
895
	}
896
 
897
 
898
 
899
	// REFERRERS
900
	function LogReferrer($tag = "", $referrer = "") {
901
		// fill values
902
		if (!$tag = trim($tag)) $tag = $this->GetPageTag();
903
		if (!$referrer = trim($referrer) AND isset($_SERVER["HTTP_REFERER"])) $referrer = $_SERVER["HTTP_REFERER"];
904
 
905
		// check if it's coming from another site
906
		if ($referrer && !preg_match("/^".preg_quote($this->GetConfigValue("base_url"), "/")."/", $referrer))
907
		{
908
			// avoid XSS (with urls like "javascript:alert()" and co)
909
			// by forcing http/https prefix
910
			// NB.: this does NOT exempt to htmlspecialchars() the collected URIs !
911
			if (!preg_match('`^https?://`', $referrer)) return;
912
 
913
			$this->Query("insert into ".$this->config["table_prefix"]."referrers set ".
914
				"page_tag = '".mysql_real_escape_string($tag)."', ".
915
				"referrer = '".mysql_real_escape_string($referrer)."', ".
916
				"time = now()");
917
		}
918
	}
919
	function LoadReferrers($tag = "") {
920
		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");
921
	}
922
	function PurgeReferrers() {
923
		if ($days = $this->GetConfigValue("referrers_purge_time")) {
924
			$this->Query("delete from ".$this->config["table_prefix"]."referrers where time < date_sub(now(), interval '".mysql_real_escape_string($days)."' day)");
925
		}
926
	}
927
 
928
 
929
 
930
	// PLUGINS
931
	/**
932
	 * Exacutes an "action" module and returns the generated output
933
	 * @param string $action The name of the action and its eventual parameters,
934
	 * as it appears in the page between "{{" and "}}"
935
	 * @param boolean $forceLinkTracking By default, the link tracking will be disabled
936
	 * during the call of an action. Set this value to <code>true</code> to allow it.
937
	 * @param array $vars An array of additionnal parameters to give to the action, in the form
938
	 * array( 'param' => 'value').
939
	 * This allows you to call Action() internally, setting $action to the name of the action
940
	 * you want to call and it's parameters in an array, wich is more efficient than
941
	 * the pattern-matching algorithm used to extract the parameters from $action.
942
	 * @return The output generated by the action.
943
	 */
944
	function Action($action, $forceLinkTracking = 0, $vars = array())
945
	{
946
		$cmd = trim($action);
947
		// extract $action and $vars_temp ("raw" attributes)
948
		if (!preg_match("/^([a-zA-Z-0-9]+)\/?(.*)$/", $cmd, $matches))
949
		{
950
			return '<i>Action invalide &quot;' . htmlspecialchars($cmd) . '&quot;</i>';
951
		}
952
		list(, $action, $vars_temp) = $matches;
953
		$vars[$vars_temp] = $vars_temp; // usefull for {{action/vars_temp}}
954
 
955
		// now that we have the action's name, we can check if the user satisfies the ACLs
956
		if (!$this->CheckModuleACL($action, 'action'))
957
		{
958
			return 'Erreur: vous n\'avez pas acc&egrave;s &agrave; l\'action ' . $action . '.';
959
		}
960
 
961
		// match all attributes (key and value)
962
		// prepare an array for extract() to work with (in $this->IncludeBuffered())
963
		if (preg_match_all("/([a-zA-Z0-9]*)=\"(.*)\"/U", $vars_temp, $matches))
964
		{
965
			for ($a = 0; $a < count($matches[1]); $a++)
966
			{
967
				$vars[$matches[1][$a]] = $matches[2][$a];
968
			}
969
		}
970
 
971
		if (!$forceLinkTracking) $this->StopLinkTracking();
972
		if ($actionObj = &$this->GetActionObject($action))
973
		{
974
			if (is_object($actionObj))
975
			{
976
				$result = $actionObj->PerformAction($vars, $cmd);
977
			}
978
			else // $actionObj is an error message
979
			{
980
				$result = $actionObj;
981
			}
982
		}
983
		else // $actionObj == null (not found, no error message)
984
		{
985
			$this->parameter = &$vars;
986
			$result = $this->IncludeBuffered(strtolower($action).".php", "<i>Action inconnue &quot;$action&quot;</i>", $vars, $this->config["action_path"]);
987
			unset($this->parameter);
988
		}
989
		$this->StartLinkTracking(); // shouldn't we restore the previous status ?
990
		return $result;
991
	}
992
 
993
	/**
994
	 * Finds the object corresponding to an action, if it exists.
995
	 * @param string $name The name of an action (should be alphanumeric)
996
	 * @return mixed
997
	 * 	- null if the corresponding file was not found or the corresponding class didn't exist after inclusion
998
	 *  - an error string if the corresponding file was found but an error append while loading it
999
	 *  - the object corresponding to this action if no problem happend
1000
	 * To check the result, you should use is_object() on it.
1001
	 * You should always assign the result of this method by referrence
1002
	 * to avoid copying the object, which is the default beheviour in PHP4.
1003
	 * @example
1004
	 * $var = &$wiki->GetActionObject('actionname');
1005
	 * if (is_object($var))
1006
	 * {
1007
	 * 		// normal behaviour
1008
	 * }
1009
	 * elseif ($var) // $var is not an object but an error string
1010
	 * {
1011
	 * 		// threat error
1012
	 * }
1013
	 * else // action was not found
1014
	 * {
1015
	 * 		// rescue from inexising action or sth
1016
	 * }
1017
	 */
1018
	function &GetActionObject($name)
1019
	{
1020
		$name = strtolower($name);
1021
		$actionObj = null; // the generated object
1022
		if (!preg_match('/^[a-z0-9]+$/', $name)) // paranoiac
1023
		{
1024
			return $actionObj;
1025
		}
1026
 
1027
		// already tried to load this object ? (may be null)
1028
		if (isset($this->actionObjects[$name]))
1029
		{
1030
			return $this->actionObjects[$name];
1031
		}
1032
 
1033
		// object not loaded, try to load it
1034
		$filename = $name . '.class.php';
1035
		// load parent class for all action objects (only once)
1036
		require_once 'includes/action.class.php';
1037
		// include the action file, this should return an empty string
1038
		$result = $this->IncludeBuffered($filename, null, null, $this->GetConfigValue('action_path'));
1039
		if ($result) // the result was not an empty string, certainly an error message
1040
		{
1041
			$actionObj = $result;
1042
		}
1043
		elseif ($result !== false) // the result was empty but the file was found
1044
		{
1045
			$class = 'Action' . ucfirst($name);
1046
			if (class_exists($class))
1047
			{
1048
				$actionObj = new $class($this);
1049
				if (!is_a($actionObj, 'WikiniAction'))
1050
				{
1051
					die("Action invalide '$name': classe incorrecte");
1052
				}
1053
			}
1054
		}
1055
		$this->actionObjects[$name] = &$actionObj;
1056
		return $actionObj;
1057
	}
1058
 
1059
	/**
1060
	 * Retrieves the list of existing actions
1061
	 * @return array An unordered array of all the available actions.
1062
	 */
1063
	function GetActionsList()
1064
	{
1065
		$action_path = $this->GetConfigValue('action_path');
1066
		$list = array();
1067
		if ($dh = opendir($action_path))
1068
		{
1069
			while (($file = readdir($dh)) !== false)
1070
			{
1071
				if (preg_match('/^([a-zA-Z-0-9]+)(.class)?.php$/', $file, $matches))
1072
				{
1073
					$list[] = $matches[1];
1074
				}
1075
			}
1076
		}
1077
		return $list;
1078
	}
1079
 
1080
	/**
1081
	 * Retrieves the list of existing handlers
1082
	 * @return array An unordered array of all the available handlers.
1083
	 */
1084
	function GetHandlersList()
1085
	{
1086
		$handler_path = $this->GetConfigValue('handler_path') . '/page/';
1087
		$list = array();
1088
		if ($dh = opendir($handler_path))
1089
		{
1090
			while (($file = readdir($dh)) !== false)
1091
			{
1092
				if (preg_match('/^([a-zA-Z-0-9]+)(.class)?.php$/', $file, $matches))
1093
				{
1094
					$list[] = $matches[1];
1095
				}
1096
			}
1097
		}
1098
		return $list;
1099
	}
1100
 
1101
	function Method($method) {
1102
		if (!$handler = $this->page["handler"]) $handler = "page";
1103
		$methodLocation = $handler."/".$method.".php";
1104
		return $this->IncludeBuffered($methodLocation, "<i>M&eacute;thode inconnue \"$methodLocation\"</i>", "", $this->config["handler_path"]);
1105
	}
1106
	function Format($text, $formatter = "wakka") {
1107
		return $this->IncludeBuffered("formatters/".$formatter.".php", "<i>Impossible de trouver le formateur \"$formatter\"</i>", compact("text"));
1108
	}
1109
 
1110
 
1111
 
1112
	// USERS
1113
	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"); }
1114
	function LoadUsers() { return $this->LoadAll("select * from ".$this->config["table_prefix"]."users order by name"); }
1115
	function GetUserName() { if ($user = $this->GetUser()) $name = $user["name"]; else if (!$name = gethostbyaddr($_SERVER["REMOTE_ADDR"])) $name = $_SERVER["REMOTE_ADDR"]; return $name; }
1116
	function GetUser() { return (isset($_SESSION["user"]) ? $_SESSION["user"] : '');}
1117
	function SetUser($user, $remember=0) { $_SESSION["user"] = $user; $this->SetPersistentCookie("name", $user["name"], $remember); $this->SetPersistentCookie("password", $user["password"], $remember); $this->SetPersistentCookie("remember", $remember, $remember); }
1118
	function LogoutUser() { $_SESSION["user"] = ""; $this->DeleteCookie("name"); $this->DeleteCookie("password"); }
1119
	function UserWantsComments() { if (!$user = $this->GetUser()) return false; return ($user["show_comments"] == "Y"); }
1120
	function GetParameter($parameter, $default = '') { return (isset($this->parameter[$parameter]) ? $this->parameter[$parameter] : $default); }
1121
 
1122
 
1123
	// COMMENTS
1124
	/**
1125
	 * Charge les commentaires relatifs ? une page.
1126
	 *
1127
	 * @param string $tag Nom de la page. Ex : "PagePrincipale"
1128
	 * @return array Tableau contenant tous les commentaires et leurs
1129
	 * propri?t?s correspondantes.
1130
	 */
1131
	function LoadComments($tag)
1132
	{
1133
		return $this->LoadAll(
1134
			"select * " .
1135
			"from ".$this->config["table_prefix"]."pages " .
1136
			"where comment_on = '".mysql_real_escape_string($tag)."' " .
1137
			"and latest = 'Y' " .
1138
			"order by substring(tag, 8) + 0");
1139
	}
1140
	/**
1141
	 * Charge les derniers commentaires de toutes les pages.
1142
	 *
1143
	 * @param int $limit Nombre de commentaires charg?s.
1144
	 *                   0 par d?faut (ie tous les commentaires).
1145
	 * @return array Tableau contenant chaque commentaire et ses
1146
	 *               propri?t?s associ?es.
1147
	 * @todo Ajouter le param?tre $start pour permettre une pagination
1148
	 *       des commentaires : ->LoadRecentComments(10, 10)
1149
	 */
1150
	function LoadRecentComments($limit = 0)
1151
	{
1152
		// The part of the query which limit the number of comments
1153
		if(is_numeric($limit) && $limit > 0) $lim = " limit ".$limit;
1154
		else $lim = "";
1155
 
1156
		// Query
1157
		return $this->LoadAll(
1158
			"select * " .
1159
			"from " . $this->config["table_prefix"] . "pages " .
1160
			"where comment_on != '' " .
1161
			"and latest = 'Y' " .
1162
			"order by time desc " .
1163
			$lim);
1164
	}
1165
	function LoadRecentlyCommented($limit = 50)
1166
	{
1167
		$pages = array();
1168
 
1169
		// NOTE: this is really stupid. Maybe my SQL-Fu is too weak, but apparently there is no easier way to simply select
1170
		//       all comment pages sorted by their first revision's (!) time. ugh!
1171
 
1172
		// load ids of the first revisions of latest comments. err, huh?
1173
		if ($ids = $this->LoadAll("select min(id) as id from ".$this->config["table_prefix"]."pages where comment_on != '' group by tag order by id desc"))
1174
		{
1175
			// load complete comments
1176
			$num=0;
1177
			foreach ($ids as $id)
1178
			{
1179
				$comment = $this->LoadSingle("select * from ".$this->config["table_prefix"]."pages where id = '".$id["id"]."' limit 1");
1180
				if (!isset($comments[$comment["comment_on"]]) && $num < $limit)
1181
				{
1182
					$comments[$comment["comment_on"]] = $comment;
1183
					$num++;
1184
				}
1185
			}
1186
 
1187
			// now load pages
1188
			if ($comments)
1189
			{
1190
				// now using these ids, load the actual pages
1191
				foreach ($comments as $comment)
1192
				{
1193
					$page = $this->LoadPage($comment["comment_on"]);
1194
					$page["comment_user"] = $comment["user"];
1195
					$page["comment_time"] = $comment["time"];
1196
					$page["comment_tag"] = $comment["tag"];
1197
					$pages[] = $page;
1198
				}
1199
			}
1200
		}
1201
		// load tags of pages
1202
		//return $this->LoadAll("select comment_on as tag, max(time) as time, tag as comment_tag, user from ".$this->config["table_prefix"]."pages where comment_on != '' group by comment_on order by time desc");
1203
		return $pages;
1204
	}
1205
 
1206
 
1207
 
1208
	// ACCESS CONTROL
1209
	// returns true if logged in user is owner of current page, or page specified in $tag
1210
	function UserIsOwner($tag = "") {
1211
		// check if user is logged in
1212
		if (!$this->GetUser()) return false;
1213
 
1214
		// set default tag
1215
		if (!$tag = trim($tag)) $tag = $this->GetPageTag();
1216
 
1217
		// check if user is owner
1218
		if ($this->GetPageOwner($tag) == $this->GetUserName()) return true;
1219
	}
1220
 
1221
	/**
1222
	 * @param string $group The name of a group
1223
	 * @return string the ACL associated with the group $gname
1224
	 * @see UserIsInGroup to check if a user belongs to some group
1225
	 */
1226
	function GetGroupACL($group)
1227
	{
1228
		if (array_key_exists($group, $this->_groupsCache))
1229
		{
1230
			return $this->_groupsCache[$group];
1231
		}
1232
		return $this->_groupsCache[$group] = $this->GetTripleValue($group, WIKINI_VOC_ACLS, GROUP_PREFIX);
1233
	}
1234
 
1235
	/**
1236
	 * Checks if a new group acl is not defined recursively
1237
	 * (this method expects that groups that are already defined are not themselves defined recursively...)
1238
	 * @param string $gname The name of the group
1239
	 * @param string $acl The new acl for that group
1240
	 * @return boolean True iff the new acl defines the group recursively
1241
	 */
1242
	function MakesGroupRecursive($gname, $acl, $origin = null, $checked = array())
1243
	{
1244
		$gname = strtolower($gname);
1245
		if ($origin === null)
1246
		{
1247
			$origin = $gname;
1248
		}
1249
		elseif ($gname === $origin)
1250
		{
1251
			return true;
1252
		}
1253
		foreach (explode("\n", $acl) as $line)
1254
		{
1255
			if (!$line) continue;
1256
			if ($line[0] == '!')
1257
			{
1258
				$line = substr($line, 1);
1259
			}
1260
			if (!$line) continue;
1261
			if ($line[0] == '@')
1262
			{
1263
				$line = substr($line, 1);
1264
				if (!in_array($line, $checked))
1265
				{
1266
					if ($this->MakesGroupRecursive($line, $this->GetGroupACL($line), $origin, $checked))
1267
					{
1268
						return true;
1269
					}
1270
				}
1271
			}
1272
		}
1273
		$checked[] = $gname;
1274
		return false;
1275
	}
1276
 
1277
	/**
1278
	 * Sets a new ACL to a given group
1279
	 * @param string $gname The name of a group
1280
	 * @param string $acl The new ACL to associate with the group $gname
1281
	 * @return int 0 if successful, a triple error code or a specific error code:
1282
	 * 	1000 if the new value would define the group recursively
1283
	 * 	1001 if $gname is not named with alphanumeric chars
1284
	 * @see GetGroupACL
1285
	 */
1286
	function SetGroupACL($gname, $acl)
1287
	{
1288
		if (preg_match('/[^A-Za-z0-9]/', $gname))
1289
		{
1290
			return 1001;
1291
		}
1292
		$old = $this->GetGroupACL($gname);
1293
		if ($this->MakesGroupRecursive($gname, $acl))
1294
		{
1295
			return 1000;
1296
		}
1297
		$this->_groupsCache[$gname] = $acl;
1298
		if ($old === null)
1299
		{
1300
			return $this->InsertTriple($gname, WIKINI_VOC_ACLS, $acl, GROUP_PREFIX);
1301
		}
1302
		elseif ($old === $acl)
1303
		{
1304
			return 0; // nothing has changed
1305
		}
1306
		else
1307
		{
1308
			return $this->UpdateTriple($gname, WIKINI_VOC_ACLS, $old, $acl, GROUP_PREFIX);
1309
		}
1310
	}
1311
	/**
1312
	 * @return array The list of all group names
1313
	 */
1314
	function GetGroupsList()
1315
	{
1316
		$res = $this->GetMatchingTriples(GROUP_PREFIX . '%', WIKINI_VOC_ACLS_URI);
1317
		$prefix_len = strlen(GROUP_PREFIX);
1318
		$list = array();
1319
		foreach ($res as $line)
1320
		{
1321
			$list[] = substr($line['resource'], $prefix_len);
1322
		}
1323
		return $list;
1324
	}
1325
 
1326
	/**
1327
	 * @param string $group The name of a group
1328
	 * @return boolean true iff the user is in the given $group
1329
	 */
1330
	function UserIsInGroup($group, $user = null, $admincheck = true)
1331
	{
1332
		return $this->CheckACL($this->GetGroupACL($group), $user, $admincheck);
1333
	}
1334
 
1335
	/**
1336
	 * Checks if a given user is andministrator
1337
	 * @param string $user The name of the user (defaults to the current user if not given)
1338
	 * @return boolean true iff the user is an administrator
1339
	 */
1340
	function UserIsAdmin($user = null)
1341
	{
1342
		return $this->UserIsInGroup(ADMIN_GROUP, $user, false);
1343
	}
1344
 
1345
	function GetPageOwner($tag = "", $time = "") { if (!$tag = trim($tag)) $tag = $this->GetPageTag(); if ($page = $this->LoadPage($tag, $time)) return $page["owner"]; }
1346
	function SetPageOwner($tag, $user) {
1347
		// check if user exists
1348
		if (!$this->LoadUser($user)) return;
1349
 
1350
		// updated latest revision with new owner
1351
		$this->Query("update ".$this->config["table_prefix"]."pages set owner = '".mysql_real_escape_string($user)."' where tag = '".mysql_real_escape_string($tag)."' and latest = 'Y' limit 1");
1352
	}
1353
	function LoadAcl($tag, $privilege, $useDefaults = 1) {
1354
		if ((!$acl = $this->LoadSingle("select * from ".$this->config["table_prefix"]."acls where page_tag = '".mysql_real_escape_string($tag)."' and privilege = '".mysql_real_escape_string($privilege)."' limit 1")) && $useDefaults)
1355
		{
1356
			$acl = array("page_tag" => $tag, "privilege" => $privilege, "list" => $this->GetConfigValue("default_".$privilege."_acl"));
1357
		}
1358
		return $acl;
1359
	}
1360
	function SaveAcl($tag, $privilege, $list) {
1361
		if ($this->LoadAcl($tag, $privilege, 0)) $this->Query("update ".$this->config["table_prefix"]."acls set list = '".mysql_real_escape_string(trim(str_replace("\r", "", $list)))."' where page_tag = '".mysql_real_escape_string($tag)."' and privilege = '".mysql_real_escape_string($privilege)."' limit 1");
1362
		else $this->Query("insert into ".$this->config["table_prefix"]."acls set list = '".mysql_real_escape_string(trim(str_replace("\r", "", $list)))."', page_tag = '".mysql_real_escape_string($tag)."', privilege = '".mysql_real_escape_string($privilege)."'");
1363
	}
1364
	// returns true if $user (defaults to current user) has access to $privilege on $page_tag (defaults to current page)
1365
	function HasAccess($privilege, $tag = "", $user = "") {
1366
		// set defaults
1367
		if (!$tag = trim($tag)) $tag = $this->GetPageTag();
1368
		if (!$user)
1369
		{
1370
			// if current user is owner, return true. owner can do anything!
1371
			if ($this->UserIsOwner($tag)) return true;
1372
			$user = $this->GetUserName();
1373
		}
1374
 
1375
		// TODO: we might want to check if a given $user (other than the current user)
1376
		// has access to a given page. If the $user is the owner of that page,
1377
		// this method might give a wrong result (because we can't check that)
1378
 
1379
		// load acl
1380
		$acl = $this->LoadAcl($tag, $privilege);
1381
 
1382
		// fine fine... now go through acl
1383
		return $this->CheckACL($acl["list"], $user);
1384
	}
1385
 
1386
	/**
1387
	 * Checks if some $user satisfies the given $acl
1388
	 * @param string $acl The acl to check, in the same format than for pages ACL's
1389
	 * @param string $user The name of the user that must satisfy the ACL. By default
1390
	 * the current remote user.
1391
	 * @return bool True if the $user satisfies the $acl, false otherwise
1392
	 */
1393
	function CheckACL($acl, $user = null, $admincheck = true)
1394
	{
1395
		if (!$user)
1396
		{
1397
			$user = $this->GetUserName();
1398
		}
1399
 
1400
		if ($admincheck && $this->UserIsAdmin($user))
1401
		{
1402
			return true;
1403
		}
1404
 
1405
 
1406
		foreach (explode("\n", $acl) as $line)
1407
		{
1408
			$line = trim($line);
1409
 
1410
			// check for inversion character "!"
1411
			if (preg_match("/^[!](.*)$/", $line, $matches))
1412
			{
1413
				$negate = 1;
1414
				$line = $matches[1];
1415
			}
1416
			else
1417
			{
1418
				$negate = 0;
1419
			}
1420
 
1421
			// if there's still anything left... lines with just a "!" don't count!
1422
			if ($line)
1423
			{
1424
				switch ($line[0])
1425
				{
1426
				case "#": // comments
1427
					break;
1428
				case "*": // everyone
1429
					return !$negate;
1430
				case "+": // registered users
1431
					if (!$this->LoadUser($user))
1432
					{
1433
						return $negate;
1434
					}
1435
					else
1436
					{
1437
						return !$negate;
1438
					}
1439
				case '@': // groups
1440
					$gname = substr($line, 1);
1441
					// paranoiac: avoid line = '@'
1442
					if ($gname && $this->UserIsInGroup($gname, $user, false /* we have allready checked if user was an admin */))
1443
					{
1444
						return !$negate;
1445
					}
1446
					break;
1447
				default: // simple user entry
1448
					if ($line == $user)
1449
					{
1450
						return !$negate;
1451
					}
1452
				}
1453
			}
1454
		}
1455
 
1456
		// tough luck.
1457
		return false;
1458
	}
1459
 
1460
	/**
1461
	 * Loads the module ACL for a certain module
1462
	 * @param string $module The name of the module
1463
	 * @param string $module_type The type of module: 'action' or 'handler'
1464
	 * @return mixed The ACL for the given module or <tt>null</tt> if no such
1465
	 * ACL was found (which should probably be interpreted as '*').
1466
	 */
1467
	function GetModuleACL($module, $module_type)
1468
	{
1469
		$module = strtolower($module);
1470
		switch ($module_type)
1471
		{
1472
		case 'action':
1473
			if (array_key_exists($module, $this->_actionsAclsCache))
1474
			{
1475
				$acl = $this->_actionsAclsCache[$module];
1476
				break;
1477
			}
1478
			$this->_actionsAclsCache[$module] = $acl = $this->GetTripleValue($module, WIKINI_VOC_ACLS, WIKINI_VOC_ACTIONS_PREFIX);
1479
			if ($acl === null)
1480
			{
1481
				$action = &$this->GetActionObject($module);
1482
				if (is_object($action))
1483
				{
1484
					return $this->_actionsAclsCache[$module] = $action->GetDefaultACL();
1485
				}
1486
			}
1487
			break;
1488
		case 'handler':
1489
			$acl = $this->GetTripleValue($module, WIKINI_VOC_ACLS, WIKINI_VOC_HANDLERS_PREFIX);
1490
			break;
1491
		default:
1492
			return null; // TODO error msg ?
1493
		}
1494
		return $acl === null ? '*' : $acl;
1495
	}
1496
 
1497
	/**
1498
	 * Sets the $acl for a given $module
1499
	 * @param string $module The name of the module
1500
	 * @param string $module_type The type of module ('action' or 'handler')
1501
	 * @param string $acl The new ACL for that module
1502
	 * @return 0 on success, > 0 on error (see InsertTriple and UpdateTriple)
1503
	 */
1504
	function SetModuleACL($module, $module_type, $acl)
1505
	{
1506
		$module = strtolower($module);
1507
		$voc_prefix = $module_type == 'action' ? WIKINI_VOC_ACTIONS_PREFIX : WIKINI_VOC_HANDLERS_PREFIX;
1508
		$old = $this->GetTripleValue($module, WIKINI_VOC_ACLS, $voc_prefix);
1509
		if ($module_type == 'action')
1510
		{
1511
			$this->_actionsAclsCache[$module] = $acl;
1512
		}
1513
		if ($old === null)
1514
		{
1515
			return $this->InsertTriple($module, WIKINI_VOC_ACLS, $acl, $voc_prefix);
1516
		}
1517
		elseif ($old === $acl)
1518
		{
1519
			return 0; // nothing has changed
1520
		}
1521
		else
1522
		{
1523
			return $this->UpdateTriple($module, WIKINI_VOC_ACLS, $old, $acl, $voc_prefix);
1524
		}
1525
	}
1526
 
1527
	/**
1528
	 * Checks if a $user satisfies the ACL to access a certain $module
1529
	 * @param string $module The name of the module to access
1530
	 * @param string $module_type The type of the module ('action' or 'handler')
1531
	 * @param string $user The name of the user. By default
1532
	 * the current remote user.
1533
	 * @return bool True if the $user has access to the given $module, false otherwise.
1534
	 */
1535
	function CheckModuleACL($module, $module_type, $user = null)
1536
	{
1537
		$acl = $this->GetModuleACL($module, $module_type);
1538
		if ($acl === null) return true; // undefined ACL means everybody has access
1539
		return $this->CheckACL($acl, $user);
1540
	}
1541
 
1542
 
1543
	// MAINTENANCE
1544
	function Maintenance() {
1545
		// purge referrers
1546
		$this->PurgeReferrers();
1547
		// purge old page revisions
1548
		$this->PurgePages();
1549
	}
1550
 
1551
 
1552
 
1553
	// THE BIG EVIL NASTY ONE!
1554
	function Run($tag, $method = "") {
1555
		if(!($this->GetMicroTime()%9)) $this->Maintenance();
1556
 
1557
		$this->ReadInterWikiConfig();
1558
 
1559
		// do our stuff!
1560
		if (!$this->method = trim($method)) $this->method = "show";
1561
		if (!$this->tag = trim($tag)) $this->Redirect($this->href("", $this->config["root_page"]));
1562
		if ((!$this->GetUser() && isset($_COOKIE["name"])) && ($user = $this->LoadUser($_COOKIE["name"], $_COOKIE["password"]))) $this->SetUser($user, $_COOKIE["remember"]);
1563
		$this->SetPage($this->LoadPage($tag, (isset($_REQUEST["time"]) ? $_REQUEST["time"] :'')));
1564
		$this->LogReferrer();
1565
 
1566
		//correction pour un support plus facile de nouveaux handlers
1567
		if ($this->CheckModuleACL($this->method, 'handler'))
1568
		{
1569
			echo $this->Method($this->method);
1570
		}
1571
		else
1572
		{
1573
			echo 'Vous ne pouvez pas acc&eacute;der &agrave; cette page par le handler sp&eacute;cifi&eacute;.';
1574
		}
1575
 
1576
		// action redirect: aucune redirection n'a eu lieu, effacer la liste des redirections pr?c?dentes
1577
		if(!empty($_SESSION['redirects'])) session_unregister('redirects');
1578
	}
1579
}
1580
 
1581
 
1582
 
1583
// stupid version check
1584
if (!isset($_REQUEST)) die('$_REQUEST[] not found. Wakka requires PHP 4.1.0 or higher!');
1585
 
1586
// workaround for the amazingly annoying magic quotes.
1587
function magicQuotesSuck(&$a)
1588
{
1589
	if (is_array($a))
1590
	{
1591
		foreach ($a as $k => $v)
1592
		{
1593
			if (is_array($v))
1594
				magicQuotesSuck($a[$k]);
1595
			else
1596
				$a[$k] = stripslashes($v);
1597
		}
1598
	}
1599
}
1600
 
1601
if (get_magic_quotes_runtime())
1602
{
1603
    // Deactivate
1604
    set_magic_quotes_runtime(false);
1605
}
1606
 
1607
if (get_magic_quotes_gpc())
1608
{
1609
	magicQuotesSuck($_POST);
1610
	magicQuotesSuck($_GET);
1611
	magicQuotesSuck($_COOKIE);
1612
}
1613
 
1614
 
1615
// default configuration values
1616
$wakkaConfig= array();
1617
$_rewrite_mode = detectRewriteMode();
1618
$wakkaDefaultConfig = array(
1619
	'wakka_version'		=> '',
1620
	'wikini_version'	=> '',
1621
	'debug'				=> 'no',
1622
	"mysql_host"		=> "localhost",
1623
	"mysql_database"	=> "wikini",
1624
	"mysql_user"		=> "wikini",
1625
	"mysql_password"	=> '',
1626
	"table_prefix"		=> "wikini_",
1627
	"root_page"			=> "PagePrincipale",
1628
	"wakka_name"		=> "MonSiteWikiNi",
1629
	"base_url"			=> computeBaseURL($_rewrite_mode),
1630
	"rewrite_mode"		=> $_rewrite_mode,
1631
	'meta_keywords'		=> '',
1632
	'meta_description'	=> '',
1633
	"action_path"		=> "actions",
1634
	"handler_path"		=> "handlers",
1635
	"header_action"		=> "header",
1636
	"footer_action"		=> "footer",
1637
	"navigation_links"		=> "DerniersChangements :: DerniersCommentaires :: ParametresUtilisateur",
1638
	"referrers_purge_time"	=> 24,
1639
	"pages_purge_time"	=> 90,
1640
	"default_write_acl"	=> "*",
1641
	"default_read_acl"	=> "*",
1642
	"default_comment_acl"	=> "*",
1643
	"preview_before_save"	=> 0,
1644
	'allow_raw_html'	=> false);
1645
unset($_rewrite_mode);
1646
 
1647
// load config
1648
if (!$configfile = GetEnv("WAKKA_CONFIG")) $configfile = "wakka.config.php";
1649
if (file_exists($configfile)) include($configfile);
1650
$wakkaConfigLocation = $configfile;
1651
$wakkaConfig = array_merge($wakkaDefaultConfig, $wakkaConfig);
1652
 
1653
// check for locking
1654
if (file_exists("locked")) {
1655
	// read password from lockfile
1656
	$lines = file("locked");
1657
	$lockpw = trim($lines[0]);
1658
 
1659
	// is authentification given?
1660
	if (isset($_SERVER["PHP_AUTH_USER"])) {
1661
		if (!(($_SERVER["PHP_AUTH_USER"] == "admin") && ($_SERVER["PHP_AUTH_PW"] == $lockpw))) {
1662
			$ask = 1;
1663
		}
1664
	} else {
1665
		$ask = 1;
1666
	}
1667
 
1668
	if ($ask) {
1669
		header("WWW-Authenticate: Basic realm=\"".$wakkaConfig["wakka_name"]." Install/Upgrade Interface\"");
1670
		header("HTTP/1.0 401 Unauthorized");
1671
		echo "Ce site est en cours de mise &agrave; jour. Veuillez essayer plus tard." ;
1672
		exit;
1673
	}
1674
}
1675
 
1676
 
1677
// compare versions, start installer if necessary
1678
if ($wakkaConfig["wakka_version"] && (!$wakkaConfig["wikini_version"])) { $wakkaConfig["wikini_version"]=$wakkaConfig["wakka_version"]; }
1679
if (($wakkaConfig["wakka_version"] != WAKKA_VERSION) || ($wakkaConfig["wikini_version"] != WIKINI_VERSION)) {
1680
	// start installer
1681
	if (!isset($_REQUEST["installAction"]) OR !$installAction = trim($_REQUEST["installAction"])) $installAction = "default";
1682
	include("setup/header.php");
1683
	if (file_exists("setup/".$installAction.".php")) include("setup/".$installAction.".php"); else echo "<i>Invalid action</i>" ;
1684
	include("setup/footer.php");
1685
	exit;
1686
}
1687
 
1688
// Check if the server is configured to automatically compress the output
1689
if (!ini_get('zlib.output_compression') && !ini_get('zlib.output_handler'))
1690
{
1691
	// Check if we can use ob_gzhandler (requires the zlib extension)
1692
	if (function_exists('ob_gzhandler'))
1693
	{
1694
		// let ob_gzhandler do the dirty job
1695
		// NB.: this must be done BEFORE session_start() when session.use_trans_sid is on
1696
		ob_start('ob_gzhandler');
1697
	}
1698
	// else lets do the dirty job by ourselves...
1699
	elseif (!empty($_SERVER['HTTP_ACCEPT_ENCODING']) && strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') && function_exists('gzencode'))
1700
	{
1701
		ob_start ('gzencode');
1702
		// Tell the browser the content is compressed with gzip
1703
		header ("Content-Encoding: gzip");
1704
	}
1705
}
1706
 
1707
// configuration du cookie de session
1708
// determine le chemin pour les cookies
1709
$a = parse_url($wakkaConfig['base_url']);
1710
$CookiePath = dirname($a['path']);
1711
// Fixe la gestion des cookie sous les OS utilisant le \ comme s?parteur de chemin
1712
$CookiePath = str_replace("\\","/",$CookiePath);
1713
// ajoute un '/' terminal sauf si on est ? la racine web
1714
if ($CookiePath != '/') $CookiePath .= '/';
1715
$a = session_get_cookie_params();
1716
session_set_cookie_params($a['lifetime'],$CookiePath);
1717
unset($a);
1718
unset($CookiePath);
1719
 
1720
// start session
1721
session_start();
1722
 
1723
// create wiki object
1724
$wiki = new Wiki($wakkaConfig);
1725
// check for database access
1726
if (!$wiki->dblink)
1727
{
1728
	echo	"<p>Pour des raisons ind&eacute;pendantes de notre volont&eacute;, ".
1729
		"le contenu de ce Wiki est temporairement inaccessible. Veuillez ".
1730
		"r&eacute;essayer ult&eacute;rieurement, merci de votre ".
1731
		"compr&eacute;hension.</p>";
1732
	// Log error (useful to find the buggy server in a load balancing platform)
1733
	trigger_error("WikiNi : DB connection failed");
1734
	exit;
1735
}
1736
 
1737
 
1738
// go!
1739
if (!isset($method)) $method='';
1740
 
1741
 
1742
// Security (quick hack)  : Check method syntax
1743
if (!(preg_match('#^[A-Za-z0-9_]*$#',$method))) {
1744
	$method='';
1745
}
1746
include('tools/prepend.php');//$wiki->Run($page, $method);
1747
?>