Overview
Comment:Remove plugin routing from _route.php
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | dev
Files: files | file ages | folders
SHA3-256: 35de3d51e3f6ac4532f57976fd8003a02f96967dce5cdbba77d64b6610624168
User & Date: bohwaz on 2022-09-09 19:29:55
Other Links: branch diff | manifest | tags
Context
2022-09-13
16:29
Try to get around pixel flood attacks on image upload check-in: 6ebeabd74f user: bohwaz tags: dev
2022-09-09
19:29
Remove plugin routing from _route.php check-in: 35de3d51e3 user: bohwaz tags: dev
19:07
Move plugin routing to Router class check-in: 6fd1956bc9 user: bohwaz tags: dev
Changes

Modified src/include/lib/Garradin/Plugin.php from [09ab31cb0b] to [8eb2feaed6].

185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221

222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250

251
252

253
254
255
256
257
258
259
260
261
262




263
264
265


266






267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
	public function id()
	{
		return $this->id;
	}

	public function route(bool $public, string $uri): void
	{
		define('Garradin\PLUGIN_ROOT', $this->path());
		define('Garradin\PLUGIN_URL', WWW_URL . 'p/' . $this->id() . '/');
		define('Garradin\PLUGIN_QSP', '?');

		if (!$uri || substr($uri, -1) == '/') {
			$uri .= 'index.php';
		}

		if (!$public) {
			require ROOT . '/www/admin/_inc.php';
		}

		$tpl = Template::getInstance();

		$tpl->assign('plugin', $plugin->getInfos());
		$tpl->assign('plugin_url', PLUGIN_URL);
		$tpl->assign('plugin_root', PLUGIN_ROOT);

		try {
			$prefix = $public ? 'public/' : 'admin/';
			$plugin->call($prefix . $page);
		}
		catch (\UnexpectedValueException $e) {
			http_response_code(404);
			throw new UserException($e->getMessage());
		}
	}

	/**
	 * Inclure un fichier depuis le plugin (dynamique ou statique)

	 * @param  string $file Chemin du fichier à aller chercher : si c'est un .php il sera inclus,
	 * sinon il sera juste affiché
	 * @return void
	 * @throws UserException Si le fichier n'existe pas ou fait partie des fichiers qui ne peuvent
	 * être appelés que par des méthodes de Plugin.
	 * @throws \RuntimeException Si le chemin indiqué tente de sortir du contexte du PHAR
	 */
	public function call($file)
	{
		$file = preg_replace('!^[./]*!', '', $file);

		if (preg_match('!(?:\.\.|[/\\\\]\.|\.[/\\\\])!', $file))
		{
			throw new \UnexpectedValueException('Chemin de fichier incorrect.');
		}

		$forbidden = ['install.php', 'garradin_plugin.ini', 'upgrade.php', 'uninstall.php'];

		if (in_array($file, $forbidden))
		{
			throw new UserException('Le fichier ' . $file . ' ne peut être appelé par cette méthode.');
		}

		$path = $this->path();

		if (!$path) {
			throw new UserException('Cette extension n\'est pas disponible.');
		}


		if (!file_exists($path . '/www/' . $file))
		{

			throw new UserException('Le fichier ' . $file . ' n\'existe pas dans le plugin ' . $this->id);
		}

		if (is_dir($path . '/www/' . $file))
		{
			throw new UserException(sprintf('Sécurité : impossible de lister le répertoire "%s" du plugin "%s".', $file, $this->id));
		}

		if (substr($file, -4) === '.php')
		{




			// Créer l'environnement d'exécution du plugin
			$plugin = $this;
			global $tpl, $config, $session, $form;









			include $this->path() . '/www/' . $file;
		}
		else
		{
			// Récupération du type MIME à partir de l'extension
			$pos = strrpos($file, '.');
			$ext = substr($file, $pos+1);

			if (isset($this->mimes[$ext]))
			{
				$mime = $this->mimes[$ext];
			}
			else
			{
				$mime = 'text/plain';
			}

			header('Content-Type: ' .$mime);
			header('Content-Length: ' . filesize($this->path() . '/www/' . $file));

			readfile($this->path() . '/www/' . $file);
		}
	}

	/**
	 * Désinstaller le plugin
	 * @return boolean TRUE si la suppression a fonctionné
	 */







<
<
<
<




<
<
<
<
<
<
<
<
<
<

<
|









>
|






|










|










>
|
|
>
|


|
<



|

>
>
>
>


|
>
>
|
>
>
>
>
>
>
|




|
|











|

|







185
186
187
188
189
190
191




192
193
194
195










196

197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244

245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
	public function id()
	{
		return $this->id;
	}

	public function route(bool $public, string $uri): void
	{




		if (!$uri || substr($uri, -1) == '/') {
			$uri .= 'index.php';
		}











		try {

			$this->call($public, $uri);
		}
		catch (\UnexpectedValueException $e) {
			http_response_code(404);
			throw new UserException($e->getMessage());
		}
	}

	/**
	 * Inclure un fichier depuis le plugin (dynamique ou statique)
	 * @param bool   $public TRUE si le fichier est situé dans 'public', sinon dans 'admin'
	 * @param string $file   Chemin du fichier à aller chercher : si c'est un .php il sera inclus,
	 * sinon il sera juste affiché
	 * @return void
	 * @throws UserException Si le fichier n'existe pas ou fait partie des fichiers qui ne peuvent
	 * être appelés que par des méthodes de Plugin.
	 * @throws \RuntimeException Si le chemin indiqué tente de sortir du contexte du PHAR
	 */
	public function call(bool $public, string $file)
	{
		$file = preg_replace('!^[./]*!', '', $file);

		if (preg_match('!(?:\.\.|[/\\\\]\.|\.[/\\\\])!', $file))
		{
			throw new \UnexpectedValueException('Chemin de fichier incorrect.');
		}

		$forbidden = ['install.php', 'garradin_plugin.ini', 'upgrade.php', 'uninstall.php'];

		if (in_array(basename($file), $forbidden))
		{
			throw new UserException('Le fichier ' . $file . ' ne peut être appelé par cette méthode.');
		}

		$path = $this->path();

		if (!$path) {
			throw new UserException('Cette extension n\'est pas disponible.');
		}

		$path .= $public ? '/public/' : '/admin/';
		$path .= $file;

		if (!file_exists($path)) {
			throw new UserException(sprintf('Le fichier "%s" n\'existe pas dans le plugin "%s"', substr($path, strlen($this->path())), $this->id));
		}

		if (is_dir($path)) {

			throw new UserException(sprintf('Sécurité : impossible de lister le répertoire "%s" du plugin "%s".', $file, $this->id));
		}

		if (substr($path, -4) === '.php')
		{
			define('Garradin\PLUGIN_ROOT', $this->path());
			define('Garradin\PLUGIN_URL', WWW_URL . ($public ? 'p/' : 'admin/p/') . $this->id() . '/');
			define('Garradin\PLUGIN_QSP', '?');

			// Créer l'environnement d'exécution du plugin
			$plugin = $this;

			if (!$public) {
				require ROOT . '/www/admin/_inc.php';
			}

			$tpl = Template::getInstance();
			$tpl->assign('plugin', $this->getInfos());
			$tpl->assign('plugin_url', PLUGIN_URL);
			$tpl->assign('plugin_root', PLUGIN_ROOT);

			include $path;
		}
		else
		{
			// Récupération du type MIME à partir de l'extension
			$pos = strrpos($path, '.');
			$ext = substr($path, $pos+1);

			if (isset($this->mimes[$ext]))
			{
				$mime = $this->mimes[$ext];
			}
			else
			{
				$mime = 'text/plain';
			}

			header('Content-Type: ' .$mime);
			header('Content-Length: ' . filesize($path));

			readfile($path);
		}
	}

	/**
	 * Désinstaller le plugin
	 * @return boolean TRUE si la suppression a fonctionné
	 */
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
	 * Liste les plugins qui doivent être affichés dans le menu
	 * @return array Tableau associatif id => nom (ou un tableau vide si aucun plugin ne doit être affiché)
	 */
	static public function listMenu(Session $session)
	{
		$list = [];

		// First let plugins handle
		self::fireSignal('menu.item', compact('session'), $list);
		ksort($list);

		return $list;
	}

	/**







|







443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
	 * Liste les plugins qui doivent être affichés dans le menu
	 * @return array Tableau associatif id => nom (ou un tableau vide si aucun plugin ne doit être affiché)
	 */
	static public function listMenu(Session $session)
	{
		$list = [];

		// Let plugins handle their listing
		self::fireSignal('menu.item', compact('session'), $list);
		ksort($list);

		return $list;
	}

	/**

Modified src/include/lib/Garradin/Web/Router.php from [584a9876f4] to [59d4f774bb].

45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
		if ($uri == 'feed/atom/') {
			Utils::redirect('/atom.xml');
		}
		elseif ($uri == 'favicon.ico') {
			header('Location: ' . Config::getInstance()->fileURL('favicon'), true);
			return;
		}
		elseif (preg_match('!^(admin/plugin|p)/(' . Plugin::PLUGIN_ID_REGEXP . ')/(.*)$/', $uri, $match)) {
			$plugin = new Plugin($match[2]);
			$public = $match[1] == 'p';
			$plugin->route($public, $match[3]);
			return;
		}
		elseif ('admin' == $first || 'p' == $first) {
			http_response_code(404);







|







45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
		if ($uri == 'feed/atom/') {
			Utils::redirect('/atom.xml');
		}
		elseif ($uri == 'favicon.ico') {
			header('Location: ' . Config::getInstance()->fileURL('favicon'), true);
			return;
		}
		elseif (preg_match('!^(admin/p|p)/(' . Plugin::PLUGIN_ID_REGEXP . ')/(.*)$!', $uri, $match)) {
			$plugin = new Plugin($match[2]);
			$public = $match[1] == 'p';
			$plugin->route($public, $match[3]);
			return;
		}
		elseif ('admin' == $first || 'p' == $first) {
			http_response_code(404);

Deleted src/www/_inc.php version [68654f2da9].

1
2
3
4
5
<?php

namespace Garradin;

require_once __DIR__ . '/../include/init.php';
<
<
<
<
<










Modified src/www/_route.php from [4954de1da6] to [9d02bde67e].

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
	if (PHP_SAPI != 'cli-server') {
		http_response_code(500);
		die('Erreur de configuration du serveur web: cette URL ne devrait pas être traitée par Garradin');
	}

	return false;
}
elseif (preg_match('!/p/(.+?)/(.*)!', $uri, $match))
{
	$_GET['_p'] = $match[1];
	$_GET['_u'] = $match[2];
	require __DIR__ . '/plugin.php';
}
elseif (preg_match('!/admin/plugin/(.+?)/(.*)!', $uri, $match))
{
	$_GET['_p'] = $match[1];
	$_GET['_u'] = $match[2];
	require __DIR__ . '/admin/plugin.php';
}
else
{
	require __DIR__ . '/../include/init.php';
	Router::route();
}







<
<
<
<
<
<
<
<
<
<
<
<





28
29
30
31
32
33
34












35
36
37
38
39
	if (PHP_SAPI != 'cli-server') {
		http_response_code(500);
		die('Erreur de configuration du serveur web: cette URL ne devrait pas être traitée par Garradin');
	}

	return false;
}












else
{
	require __DIR__ . '/../include/init.php';
	Router::route();
}

Modified src/www/index.php from [cf9ed053b4] to [e1189265e9].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

namespace Garradin;

use Garradin\Email\Emails;
use Garradin\Web\Router;

require __DIR__ . '/_inc.php';

// Handle __un__subscribe URL
if (!empty($_GET['un'])) {
	$params = array_intersect_key($_GET, ['un' => null, 'v' => null]);

	// RFC 8058
	if (!empty($_POST['Unsubscribe']) && $_POST['Unsubscribe'] == 'Yes') {







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

namespace Garradin;

use Garradin\Email\Emails;
use Garradin\Web\Router;

require_once __DIR__ . '/../include/init.php';

// Handle __un__subscribe URL
if (!empty($_GET['un'])) {
	$params = array_intersect_key($_GET, ['un' => null, 'v' => null]);

	// RFC 8058
	if (!empty($_POST['Unsubscribe']) && $_POST['Unsubscribe'] == 'Yes') {