<?php
/***************************************************************************
- * Copyright (C) 2003-2008 Polytechnique.org *
+ * Copyright (C) 2003-2010 Polytechnique.org *
* http://opensource.polytechnique.org/ *
* *
* This program is free software; you can redistribute it and/or modify *
define('PL_DO_AUTH', 300);
define('PL_FORBIDDEN', 403);
define('PL_NOT_FOUND', 404);
+define('PL_WIKI', 500);
+
+abstract class PlHook
+{
+ protected $auth;
+ protected $perms;
+ protected $type;
+
+ protected function __construct($auth = AUTH_PUBLIC, $perms = 'user', $type = DO_AUTH)
+ {
+ $this->auth = $auth;
+ $this->perms = $perms;
+ $this->type = $type;
+ }
+
+ public function needAuth()
+ {
+ return $this->auth > S::i('auth', AUTH_PUBLIC);
+ }
+
+ public function checkPerms()
+ {
+ if (!$this->perms || $this->auth == AUTH_PUBLIC) { // No perms, no check
+ return true;
+ }
+ $s_perms = S::v('perms');
+ return $s_perms->hasFlagCombination($this->perms);
+ }
+
+ public function hasType($type)
+ {
+ return ($this->type & $type) == $type;
+ }
+
+ abstract protected function run(PlPage &$page, array $args);
+
+ public function call(PlPage &$page, array $args)
+ {
+ global $globals, $session, $platal;
+ if ($this->needAuth()) {
+ if ($this->hasType(DO_AUTH)) {
+ if (!$session->start($this->auth)) {
+ $platal->force_login($page);
+ return PL_FORBIDDEN;
+ }
+ } else {
+ return PL_FORBIDDEN;
+ }
+ }
+ if (!$this->checkPerms()) {
+ if (Platal::notAllowed()) {
+ return PL_FORBIDDEN;
+ }
+ }
+ return $this->run($page, $args);
+ }
+}
+
+class PlStdHook extends PlHook
+{
+ private $hook;
+
+ public function __construct($callback, $auth = AUTH_PUBLIC, $perms = 'user', $type = DO_AUTH)
+ {
+ parent::__construct($auth, $perms, $type);
+ $this->hook = $callback;
+ }
+
+ protected function run(PlPage &$page, array $args)
+ {
+ global $session, $platal;
+
+ $args[0] = $page;
+ $val = call_user_func_array($this->hook, $args);
+ if ($val == PL_DO_AUTH) {
+ if (!$session->start($session->loggedLevel())) {
+ $platal->force_login($page);
+ }
+ $val = call_user_func_array($this->hook, $args);
+ }
+ return $val;
+ }
+}
+
+class PlWikiHook extends PlHook
+{
+ public function __construct($auth = AUTH_PUBLIC, $perms = 'user', $type = DO_AUTH)
+ {
+ parent::__construct($auth, $perms, $type);
+ }
+
+ protected function run(PlPage &$page, array $args)
+ {
+ return PL_WIKI;
+ }
+}
+
+class PlHookTree
+{
+ public $hook = null;
+ public $aliased = null;
+ public $children = array();
+
+ public function addChild(array $path, PlHook $hook)
+ {
+ global $platal;
+ $next = array_shift($path);
+ $alias = null;
+ if ($next && $next{0} == '%') {
+ $alias = $next;
+ $next = $platal->hook_map(substr($next, 1));
+ }
+ if (!$next) {
+ return;
+ }
+ @$child =& $this->children[$next];
+ if (!$child) {
+ $child = new PlHookTree();
+ $this->children[$next] =& $child;
+ $child->aliased = $alias;
+ }
+ if (empty($path)) {
+ $child->hook = $hook;
+ } else {
+ $child->addChild($path, $hook);
+ }
+ }
+
+ private function findChildAux(array $remain, array $matched, array $aliased)
+ {
+ $next = @$remain[0];
+ if ($this->aliased) {
+ $aliased = $matched;
+ }
+ if (!empty($next)) {
+ $child = @$this->children[$next];
+ if ($child) {
+ array_shift($remain);
+ $matched[] = $next;
+ return $child->findChildAux($remain, $matched, $aliased);
+ }
+ }
+ return array($this->hook, $matched, $remain, $aliased);
+ }
+
+ public function findChild(array $path)
+ {
+ return $this->findChildAux($path, array(), array());
+ }
+
+ private function findNearestChildAux(array $remain, array $matched, array $aliased)
+ {
+ $next = @$remain[0];
+ if ($this->aliased) {
+ $aliased = $matched;
+ }
+ if (!empty($next)) {
+ $child = @$this->children[$next];
+ if (!$child) {
+ $nearest_lev = 50;
+ $nearest_sdx = 50;
+ $match = null;
+ foreach ($this->children as $path=>$hook) {
+ $lev = levenshtein($next, $path);
+ if ($lev <= $nearest_lev
+ && ($lev < strlen($next) / 2 || strpos($next, $path) !== false
+ || strpos($path, $next) !== false)) {
+ $sdx = levenshtein(soundex($next), soundex($path));
+ if ($lev == $nearest_lev || $sdx < $nearest_sdx) {
+ $child = $hook;
+ $nearest_lev = $lev;
+ $nearest_sdx = $sdx;
+ $match = $path;
+ }
+ }
+ }
+ $next = $match;
+ }
+ if ($child) {
+ array_shift($remain);
+ $matched[] = $next;
+ return $child->findNearestChildAux($remain, $matched, $aliased);
+ }
+ if (($pos = strpos($next, '.php')) !== false) {
+ $remain[0] = substr($next, 0, $pos);
+ return $this->findNearestChildAux($remain, $matched, $aliased);
+ }
+ }
+ return array($this->hook, $matched, $remain, $aliased);
+ }
+
+ public function findNearestChild(array $path)
+ {
+ return $this->findNearestChildAux($path, array(), array());
+ }
+}
+
abstract class Platal
{
- private $__mods;
- private $__hooks;
+ private $mods;
+ private $hooks;
protected $https;
public $ns;
public $path;
- public $argv;
+ public $argv = array();
static private $_page = null;
public function __construct()
{
global $platal, $session, $globals;
- $platal =& $this;
- $globalclass = PL_GLOBALS_CLASS;
- $globals = new $globalclass();
+ $platal = $this;
+
+ /* Assign globals first, then call init: init must be used for operations
+ * that requires access to the content of $globals (e.g. XDB requires
+ * $globals to be assigned.
+ */
+ $globals = $this->buildGlobals();
$globals->init();
- $sessionclass = PL_SESSION_CLASS;
- $session = new $sessionclass();
+
+ /* Get the current session: assign first, then activate the session.
+ */
+ $session = $this->buildSession();
if (!$session->startAvailableAuth()) {
- Platal::page()->trigError('Données d\'authentification invalide.');
+ Platal::page()->trigError("Données d'authentification invalides.");
}
$modules = func_get_args();
}
$this->path = trim(Get::_get('n', null), '/');
- $this->__mods = array();
- $this->__hooks = array();
+ $this->mods = array();
+ $this->hooks = new PlHookTree();
array_unshift($modules, 'core');
foreach ($modules as $module) {
$module = strtolower($module);
- $this->__mods[$module] = $m = PLModule::factory($module);
- $this->__hooks += $m->handlers();
+ $this->mods[$module] = $m = PLModule::factory($module);
+ $hooks = $m->handlers();
+ foreach ($hooks as $path=>$hook) {
+ $this->hooks->addChild(explode('/', $path), $hook);
+ }
}
if ($globals->mode == '') {
return join('/', array_slice($this->argv, 0, $n));
}
- protected function find_hook()
+ public static function wiki_hook($auth = AUTH_PUBLIC, $perms = 'user', $type = DO_AUTH)
{
- $p = $this->path;
-
- while ($p) {
- if (array_key_exists($p, $this->__hooks))
- break;
-
- $p = substr($p, 0, strrpos($p, '/'));
- }
-
- if (empty($this->__hooks[$p])) {
- return null;
- }
-
- $hook = $this->__hooks[$p];
-
- if (!is_callable($hook['hook'])) {
- return null;
- }
-
- $this->https = ($hook['type'] & NO_HTTPS) ? false : true;
- $this->argv = explode('/', substr($this->path, strlen($p)));
- $this->argv[0] = $p;
-
- return $hook;
+ return new PlWikiHook($auth, $perms, $type);
}
- protected function find_nearest_key($key, array &$array)
+ public function hook_map($name)
{
- $keys = array_keys($array);
- if (in_array($key, $keys)) {
- return $key;
- }
-
- if (($pos = strpos($key, '.php')) !== false) {
- $key = substr($key, 0, $pos);
- }
-
- $has_end = in_array("#final#", $keys);
- if (strlen($key) > 24 && $has_end) {
- return "#final#";
- }
-
- foreach ($keys as $k) {
- if ($k == "#final#") {
- continue;
- }
- $lev = levenshtein($key, $k);
-
- if ((!isset($val) || $lev < $val)
- && ($lev <= strlen($k)/2 || strpos($k, $key) !== false || strpos($key, $k) !== false)) {
- $val = $lev;
- $best = $k;
- }
- }
- if (!isset($best) && $has_end) {
- return "#final#";
- } else if (isset($best)) {
- return $best;
- }
return null;
}
- public function near_hook()
+ protected function find_hook()
{
- $hooks = array();
- $leafs = array();
- foreach ($this->__hooks as $hook=>$handler) {
- if (!$this->check_perms($handler['perms'])) {
- continue;
- }
- $parts = split('/', $hook);
- $place =& $hooks;
- foreach ($parts as $part) {
- if (!isset($place[$part])) {
- $place[$part] = array();
- }
- $place =& $place[$part];
- }
- $leaf = $parts[count($parts)-1];
- if (!isset($leafs[$leaf])) {
- $leafs[$leaf] = $hook;
- } else if (is_array($leafs[$leaf])) {
- $leafs[$leaf][] = $hook;
- } else {
- $leafs[$leaf] = array($hook, $leafs[$leaf]);
- }
- $place["#final#"] = array();
- }
-
- // search for the nearest full path
- $p = split('/', $this->path);
- $place =& $hooks;
- $link = '';
- foreach ($p as $k) {
- if (!isset($ended)) {
- $key = $this->find_nearest_key($k, $place);
- } else {
- $key = $k;
- }
- if ($key == "#final#") {
- if (!array_key_exists($link, $this->__hooks)) {
- $link = '';
- break;
- }
- $key = $k;
- $ended = true;
- }
- if (!is_null($key)) {
- if (!empty($link)) {
- $link .= '/';
- }
- $link .= $key;
- $place =& $place[$key];
- } else {
- $link = '';
- break;
- }
- }
- if ($link == $this->path) {
- $link = '';
- }
- if ($link && levenshtein($link, $this->path) < strlen($link)/3) {
- return $link;
- }
-
- // search for missing namespace (the given name is a leaf)
- $leaf = array_shift($p);
- $args = count($p) ? '/' . implode('/', $p) : '';
- if (isset($leafs[$leaf]) && !is_array($leafs[$leaf]) && $leafs[$leaf] != $this->path) {
- return $leafs[$leaf] . $args;
+ $p = explode('/', $this->path);
+ list($hook, $matched, $remain, $aliased) = $this->hooks->findChild($p);
+ if (empty($hook)) {
+ return null;
}
- unset($val);
- $best = null;
- foreach ($leafs as $k=>&$path) {
- if (is_array($path)) {
- continue;
- }
- $lev = levenshtein($leaf, $k);
-
- if ((!isset($val) || $lev < $val)
- && ($lev <= strlen($k)/2 || strpos($k, $leaf) !== false || strpos($leaf, $k) !== false)) {
- $val = $lev;
- $best = $path;
- }
+ $this->argv = $remain;
+ array_unshift($this->argv, implode('/', $matched));
+ if (!empty($aliased)) {
+ $this->ns = implode('/', $aliased) . '/';
}
- return $best == null ? ( $link ? $link : null ) : $best . $args;
+ $this->https = !$hook->hasType(NO_HTTPS);
+ return $hook;
}
- protected function check_perms($perms)
+ public function near_hook()
{
- if (!$perms) { // No perms, no check
- return true;
+ $p = explode('/', $this->path);
+ list($hook, $matched, $remain, $aliased) = $this->hooks->findNearestChild($p);
+ if (empty($hook)) {
+ return null;
}
- $s_perms = S::v('perms');
- return $s_perms->hasFlagCombination($perms);
+ $url = implode('/', $matched);
+ if (!empty($remain)) {
+ $url .= '/' . implode('/', $remain);
+ }
+ if ($url == $this->path || levenshtein($url, $this->path) > strlen($url) / 3
+ || !$hook->checkPerms()) {
+ return null;
+ }
+ return $url;
}
private function call_hook(PlPage &$page)
http_redirect('https://' . $globals->core->secure_domain . $_SERVER['REQUEST_URI']);
}
- $args = $this->argv;
- $args[0] =& $page;
-
- if ($hook['auth'] > S::v('auth', AUTH_PUBLIC)) {
- if ($hook['type'] & DO_AUTH) {
- if (!$session->start($hook['auth'])) {
- $this->force_login($page);
- }
- } else {
- return PL_FORBIDDEN;
- }
- }
- if ($hook['auth'] != AUTH_PUBLIC && !$this->check_perms($hook['perms'])) {
- if (S::admin()) {
- $page->trigWarning('Tu accèdes à cette page car tu es administrateur du site.');
- } else {
- return PL_FORBIDDEN;
- }
- }
-
- $val = call_user_func_array($hook['hook'], $args);
- if ($val == PL_DO_AUTH) {
- // The handler need a better auth with the current args
- if (!$session->start($session->loggedLevel())) {
- $this->force_login($page);
- }
- $val = call_user_func_array($hook['hook'], $args);
- }
- return $val;
+ return $hook->call($page, $this->argv);
}
/** Show the authentication form.
$this->path = 'index';
}
- $page->assign('platal', $this);
- switch ($this->call_hook($page)) {
- case PL_FORBIDDEN:
- $this->__mods['core']->handler_403($page);
- break;
-
- case PL_NOT_FOUND:
- $this->__mods['core']->handler_404($page);
- break;
+ try {
+ $page->assign('platal', $this);
+ switch ($this->call_hook($page)) {
+ case PL_FORBIDDEN:
+ $this->mods['core']->handler_403($page);
+ break;
+
+ case PL_NOT_FOUND:
+ $this->mods['core']->handler_404($page);
+ break;
+
+ case PL_WIKI:
+ return PL_WIKI;
+ }
+ } catch (Exception $e) {
+ header($_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error');
+
+ $file = fopen(self::globals()->spoolroot . '/spool/tmp/site_errors', 'a');
+ fwrite($file, '<pre>' . date('Y-m-d G:i:s') . '</pre>'
+ . '<pre>' . pl_entities("" . $e) . '</pre>'
+ . '------------------------------------------------------------------' . "\n");
+ fclose($file);
+
+ if (self::globals()->debug) {
+ $page->kill(pl_entities($e->getMessage())
+ . '<pre>' . pl_entities("" . $e) . '</pre>');
+ } else {
+ $page->kill(pl_entities($e->getMessage()));
+ }
}
$page->assign('platal', $this);
{
$page =& self::page();
- $this->__mods['core']->handler_403($page);
+ $this->mods['core']->handler_403($page);
$page->assign('platal', $this);
$page->run();
}
{
$page =& self::page();
- $this->__mods['core']->handler_404($page);
+ $this->mods['core']->handler_404($page);
$page->assign('platal', $this);
$page->run();
}
+ public static function notAllowed()
+ {
+ if (S::admin()) {
+ self::page()->trigWarning('Tu accèdes à cette page car tu es administrateur du site.');
+ return false;
+ } else {
+ return true;
+ }
+ }
+
public static function load($modname, $include = null)
{
global $platal;
$modname = strtolower($modname);
- if (isset($platal->__mods[$modname])) {
+ if (isset($platal->mods[$modname])) {
if (is_null($include)) {
return;
}
- $platal->__mods[$modname]->load($include);
+ $platal->mods[$modname]->load($include);
} else {
if (is_null($include)) {
require_once PLModule::path($modname) . '.php';
}
}
+ public static function assert($cond, $error, $userfriendly = null)
+ {
+ if ($cond === false) {
+ if ($userfriendly == null) {
+ $userfriendly = "Une erreur interne s'est produite.
+ Merci de réessayer la manipulation qui a déclenché l'erreur ;
+ si cela ne fonctionne toujours pas, merci de nous signaler le problème rencontré.";
+ }
+ throw new PlException($userfriendly, $error);
+ }
+ }
+
+ public function &buildLogger($uid, $suid = 0)
+ {
+ if (defined('PL_LOGGER_CLASS')) {
+ $class = PL_LOGGER_CLASS;
+ $logger = new $class($uid, $suid);
+ return $logger;
+ } else {
+ return PlLogger::dummy($uid, $suid);
+ }
+ }
+
+ protected function &buildPage()
+ {
+ $pageclass = PL_PAGE_CLASS;
+ $page = new $pageclass();
+ return $page;
+ }
+
static public function &page()
{
- global $platal;
if (is_null(self::$_page)) {
- $pageclass = PL_PAGE_CLASS;
- self::$_page = new $pageclass();
+ global $platal;
+ self::$_page = $platal->buildPage();
}
return self::$_page;
}
+ protected function &buildSession()
+ {
+ $sessionclass = PL_SESSION_CLASS;
+ $session = new $sessionclass();
+ return $session;
+ }
+
static public function &session()
{
global $session;
return $session;
}
+ protected function &buildGlobals()
+ {
+ $globalclass = PL_GLOBALS_CLASS;
+ $globals = new $globalclass();
+ return $globals;
+ }
+
static public function &globals()
{
global $globals;