1<?php
2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
3//
4// All Rights Reserved. See copyright.txt for details and a complete list of authors.
5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
6// $Id$
7
8// Adding support for an other web server? Check the end of the file
9
10/**
11 * Routing method, receives the path portion of the URL relative to tiki root.
12 * http://example.com/tiki/hello-world?foo-bar
13 * $path is expectedto be hello-world
14 */
15function tiki_route($path)
16{
17	/*
18	// If you are converting to Tiki and want to preserve some URLs, map the urls and remove the comment block
19	$urlMapping = array(
20		'wiki/old-page-name' => 'PageName',
21		'corporate/Privacy+Policy.pdf' => 'dl123',
22	);
23
24	if (isset($urlMapping[$path])) {
25		$path = $urlMapping[$path];
26	}
27	*/
28
29
30	$simple = [
31		'articles' => 'tiki-view_articles.php',
32		'blogs' => 'tiki-list_blogs.php',
33		'calendar' => 'tiki-calendar.php',
34		'categories' => 'tiki-browse_categories.php',
35		'chat' => 'tiki-chat.php',
36		'contact' => 'tiki-contact.php',
37		'directories' => 'tiki-directory_browse.php',
38		'faqs' => 'tiki-list_faqs.php',
39		'filelist' => 'tiki-list_file_gallery.php',
40		'forums' => 'tiki-forums.php',
41		'galleries' => 'tiki-galleries.php',
42		'login' => 'tiki-login_scr.php',
43		'logout' => 'tiki-logout.php',
44		'me' => 'tiki-user_information.php',
45		'my' => 'tiki-my_tiki.php',
46		'newsletters' => 'tiki-newsletters.php',
47		'quizzes' => 'tiki-list_quizzes.php',
48		'register' => 'tiki-register.php',
49		'sheets' => 'tiki-sheets.php',
50		'statistics' => 'tiki-stats.php',
51		'surveys' => 'tiki-list_surveys.php',
52		'trackers' => 'tiki-list_trackers.php',
53		'users' => 'tiki-list_users.php',
54		'tiki-check' => 'tiki-check.php',
55	];
56
57	foreach ($simple as $key => $file) {
58		tiki_route_attempt("|^$key$|", $file);
59	}
60
61	/*
62		Valid:
63
64		art123
65		article123
66		art123-XYZ
67		article123-XYZ
68	*/
69	tiki_route_attempt('/^(art|article)(\d+)(\-.*)?$/', 'tiki-read_article.php', tiki_route_single(2, 'articleId'));
70
71	tiki_route_attempt('|^blog(\d+)(\-.*)?$|', 'tiki-view_blog.php', tiki_route_single(1, 'blogId'));
72	tiki_route_attempt('|^blogpost(\d+)(\-.*)?$|', 'tiki-view_blog_post.php', tiki_route_single(1, 'postId'));
73	tiki_route_attempt('|^cat(\d+)(\-.*)?$|', 'tiki-browse_categories.php', tiki_route_single(1, 'parentId'));
74	tiki_route_attempt_prefix('browseimage', 'tiki-browse_image.php', 'imageId');
75	tiki_route_attempt('/^event(\d+)(\-.*)?$/', 'tiki-calendar_edit_item.php', tiki_route_single(1, 'viewcalitemId'));
76
77	tiki_route_attempt(
78		'|^cal(\d[\d,]*)$|',
79		'tiki-calendar.php',
80		function ($parts) {
81			$ids = explode(',', $parts[1]);
82			$ids = array_filter($ids);
83			return ['calIds' => $ids];
84		}
85	);
86
87	tiki_route_attempt_prefix('directory', 'tiki-directory_browse.php', 'parent');
88	tiki_route_attempt_prefix('dirlink', 'tiki-directory_redirect.php', 'siteId');
89
90	tiki_route_attempt_prefix('faq', 'tiki-view_faq.php', 'faqId');
91	tiki_route_attempt_prefix('file', 'tiki-list_file_gallery.php', 'galleryId');
92	tiki_route_attempt_prefix('forum', 'tiki-view_forum.php', 'forumId');
93	tiki_route_attempt('|^forumthread(\d+)(\-.*)?$|', 'tiki-view_forum_thread.php', tiki_route_single(1, 'comments_parentId'));
94	tiki_route_attempt_prefix('calevent', 'tiki-calendar_edit_item.php', 'viewcalitemId');
95	tiki_route_attempt_prefix('gallery', 'tiki-browse_gallery.php', 'galleryId');
96	tiki_route_attempt_prefix('img', 'show_image.php', 'id');
97	tiki_route_attempt_prefix('image', 'show_image.php', 'id');
98	tiki_route_attempt(
99		'|^imagescale(\d+)/(\d+)$|',
100		'show_image.php',
101		function ($parts) {
102			return [
103				'id' => $parts[1],
104				'scalesize' => $parts[2],
105			];
106		}
107	);
108	tiki_route_attempt('|^item(\d+)(\-.*)?$|', 'tiki-view_tracker_item.php', tiki_route_single(1, 'itemId'));
109	tiki_route_attempt_prefix('int', 'tiki-integrator.php', 'repID');
110	tiki_route_attempt_prefix('newsletter', 'tiki-newsletters.php', 'nlId', ['info' => '1']);
111	tiki_route_attempt_prefix('nl', 'tiki-newsletters.php', 'nlId', ['info' => '1']);
112	tiki_route_attempt_prefix('poll', 'tiki-poll_form.php', 'pollId');
113	tiki_route_attempt_prefix('quiz', 'tiki-take_quiz.php', 'quizId');
114	tiki_route_attempt_prefix('survey', 'tiki-take_survey.php', 'surveyId');
115	tiki_route_attempt_prefix('tracker', 'tiki-view_tracker.php', 'trackerId');
116	tiki_route_attempt_prefix('sheet', 'tiki-view_sheets.php', 'sheetId');
117	tiki_route_attempt_prefix('user', 'tiki-user_information.php', 'userId');
118	tiki_route_attempt('|^userinfo$|', 'tiki-view_tracker_item.php', function () {
119		return ['view' => ' user'];
120	});
121
122	tiki_route_attempt_prefix('dl', 'tiki-download_file.php', 'fileId');
123	tiki_route_attempt_prefix('thumbnail', 'tiki-download_file.php', 'fileId', ['thumbnail' => '']);
124	tiki_route_attempt_prefix('display', 'tiki-download_file.php', 'fileId', ['display' => '']);
125	tiki_route_attempt_prefix('preview', 'tiki-download_file.php', 'fileId', ['preview' => '']);
126
127	tiki_route_attempt(
128		'/^(wiki|page)\-(.+)$/',
129		'tiki-index.php',
130		function ($parts) {
131			return ['page' => $parts[2]];
132		}
133	);
134	tiki_route_attempt(
135		'/^show:(.+)$/',
136		'tiki-slideshow.php',
137		function ($parts) {
138			return ['page' => urldecode($parts[1])];
139		}
140	);
141
142	tiki_route_attempt(
143		'/([^\/]+).xml$/',
144		'tiki-sitemap.php',
145		function ($parts) {
146			return ['file' => $parts[0]];
147		}
148	);
149
150	tiki_route_attempt(
151		'|^tiki\-(\w+)\-(\w+)$|',
152		'tiki-ajax_services.php',
153		function ($parts) {
154			if ($parts[2] == 'x') {
155				return [
156					'controller' => $parts[1],
157				];
158			} else {
159				return [
160					'controller' => $parts[1],
161					'action' => $parts[2],
162				];
163			}
164		}
165	);
166
167	if (false !== $dot = strrpos($path, '.')) {
168		// Prevent things that look like filenames from being considered for wiki page names
169		$extension = substr($path, $dot + 1);
170		if (in_array($extension, ['css', 'gif', 'jpg', 'png', 'php', 'html', 'js', 'htm', 'shtml', 'cgi', 'sql', 'phtml', 'txt', 'ihtml'])) {
171			return;
172		}
173	}
174
175	tiki_route_attempt_custom_route_redirect();
176
177	tiki_route_attempt(
178		'|.*|',
179		'tiki-index.php',
180		function ($parts) {
181			return ['page' => urldecode($parts[0])];
182		}
183	);
184}
185
186function tiki_route_attempt($pattern, $file, $callback = null, $extra = [])
187{
188	global $path, $inclusion, $base, $full;
189
190	if ($inclusion) {
191		return;
192	}
193
194	if (preg_match($pattern, $path, $parts)) {
195		$inclusion = $file;
196
197		$full = $base . $file;
198
199		if ($callback && is_callable($callback)) {
200			$_GET = array_merge($_GET, $callback($parts), $extra);
201		}
202	}
203}
204
205function tiki_route_attempt_prefix($prefix, $file, $key, $extra = [])
206{
207	tiki_route_attempt("|^$prefix(\d+)$|", $file, tiki_route_single(1, $key), $extra);
208}
209
210function tiki_route_single($index, $name)
211{
212	return function ($parts) use ($index, $name) {
213		return [$name => $parts[$index]];
214	};
215}
216
217/**
218 * Attempts to route based on custom routes, defined by the admin.
219 * If a suitable rule is found an HTTP redirect will be issued and the user sent to the right page/URL.
220 * Custom routes rules are only processed if none of the built in rules were successful
221 * This function also loads the minimal amount of framework to be able to query the db.
222 */
223function tiki_route_attempt_custom_route_redirect()
224{
225	global $path, $inclusion, $prefs, $tikiroot, $tikipath, $base, $full;
226
227	if ($inclusion || empty($path)) {
228		return;
229	}
230
231	// bootstrap the essentials to be able to use tiki db and libraries
232	// in a sane state that allows tiki to be fallback to the default entrypoints
233	// if a custom route is not match
234	require_once __DIR__ . '/tiki-filter-base.php'; // sets $tikiroot, $tikipath
235	$GLOBALS['tikiroot'] = $tikiroot;
236	$GLOBALS['tikipath'] = $tikipath;
237
238	require_once __DIR__ . '/db/tiki-db.php';
239
240	if (! TikiDb::get()) {
241		exit;
242	}
243
244	require_once __DIR__ . '/lib/tikilib.php';
245
246	$tikilib = new TikiLib;
247	$GLOBALS['tikilib'] = $tikilib;
248
249	$prefereces = [
250		'feature_sefurl' => 'n',
251		'feature_sefurl_routes' => 'n',
252	];
253
254	$tikilib->get_preferences($prefereces, true, true);
255	// ~ bootstrap
256
257	if ($prefs['feature_sefurl_routes'] === 'y') {
258		$route = \Tiki\CustomRoute\CustomRoute::matchRoute($path);
259		if ($route) {
260			$routeParameters = \Tiki\CustomRoute\CustomRoute::getInPlaceRoutingParameters($route, $path);
261			if ($routeParameters !== false) {
262				$inclusion = $routeParameters['file'];
263				$full = $base . $inclusion;
264				$_GET = array_merge($_GET, $routeParameters['get_param']);
265			} else {
266				require_once __DIR__ . '/tiki-setup.php';
267				// Reload necessary preferences for SEF url
268				$tikilib->get_preferences($prefereces, true, true);
269				\Tiki\CustomRoute\CustomRoute::executeRoute($route, $path);
270			}
271		}
272	}
273}
274
275$sapi = php_sapi_name();
276$base = null;
277$path = null;
278$inclusion = null;
279
280// This portion may need to vary depending on the webserver/configuration
281
282switch ($sapi) {
283	case 'apache2handler':
284	default:
285		// Fix $_SERVER['REQUEST_URI', which is ASCII encoded on IIS
286		//	Convert the SERVER variable itself, to fix $_SERVER['REQUEST_URI'] access everywhere
287		//	route.php comes first in the processing.  Avoid dependencies.
288		if (isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'IIS') !== false) {
289			if (mb_detect_encoding($_SERVER['REQUEST_URI'], 'UTF-8', true) == false) {
290				$_SERVER['REQUEST_URI'] = utf8_encode($_SERVER['REQUEST_URI']);
291			}
292		}
293
294		if (isset($_SERVER['SCRIPT_URL'])) {
295			$full = $_SERVER['SCRIPT_URL'];
296		} elseif (isset($_SERVER['REQUEST_URI'])) {
297			$full = $_SERVER['REQUEST_URI'];
298			if (strpos($full, '?') !== false) {
299				$full = substr($full, 0, strpos($full, '?'));
300			}
301		} elseif (isset($_SERVER['REDIRECT_URL'])) {
302			$full = $_SERVER['REDIRECT_URL'];
303		} elseif (isset($_SERVER['UNENCODED_URL'])) {	// For IIS
304			$full = $_SERVER['UNENCODED_URL'];
305		} else {
306			break;
307		}
308
309		$file = basename(__FILE__);
310		$base = substr($_SERVER['PHP_SELF'], 0, -strlen($file));
311		$path = substr($full, strlen($base));
312		break;
313}
314
315// Global check
316
317if (is_null($base) || is_null($path)) {
318	header('HTTP/1.0 500 Internal Server Error');
319	header('Content-Type: text/plain; charset=utf-8');
320
321	echo "Request could not be understood. Verify routing file.";
322	exit;
323}
324
325tiki_route($path);
326
327if ($inclusion) {
328	$_SERVER['PHP_SELF'] = $base . $inclusion;
329	$_SERVER['SCRIPT_NAME'] = $base . basename($inclusion);
330	include __DIR__ . '/' . $inclusion;
331} else {
332	error_log("No route found - full:$full query:{$_SERVER['QUERY_STRING']}");
333
334	// Route to the "no-route" URL, if found
335	require_once('lib/init/initlib.php');
336	$local_php = TikiInit::getCredentialsFile();
337	if (file_exists($local_php)) {
338		include($local_php);
339	}
340	if (empty($noroute_url)) {
341		// Fail
342		header('HTTP/1.0 404 Not Found');
343		header('Content-Type: text/plain; charset=utf-8');
344
345		echo "No route found. Please see http://dev.tiki.org/URL+Rewriting+Revamp";
346	} else {
347		header('Location: ' . $noroute_url);
348	}
349	exit;
350}
351