aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorIsmaël Bouya <ismael.bouya@normalesup.org>2019-02-03 20:58:18 +0100
committerIsmaël Bouya <ismael.bouya@normalesup.org>2021-02-08 01:23:56 +0100
commita19c24edc1057bd411821f9e3e7d1d309d38b1bb (patch)
treeee572568811b8ed56630156ab88b0a2278785e65
parent630ebca2b6359e942e5b6c057cca2b6069c1093a (diff)
downloadShaarli-gitolite_local/ldap.tar.gz
Shaarli-gitolite_local/ldap.tar.zst
Shaarli-gitolite_local/ldap.zip
Add ldap connectiongitolite_local/ldap
-rw-r--r--.htaccess13
-rw-r--r--application/ApplicationUtils.php3
-rw-r--r--application/config/ConfigManager.php77
-rw-r--r--application/security/LoginManager.php42
-rw-r--r--application/security/SessionManager.php4
-rw-r--r--index.php63
6 files changed, 164 insertions, 38 deletions
diff --git a/.htaccess b/.htaccess
index 4c004271..5acd708e 100644
--- a/.htaccess
+++ b/.htaccess
@@ -6,10 +6,23 @@ RewriteEngine On
6# Prevent accessing subdirectories not managed by SCM 6# Prevent accessing subdirectories not managed by SCM
7RewriteRule ^(.git|doxygen|vendor) - [F] 7RewriteRule ^(.git|doxygen|vendor) - [F]
8 8
9RewriteCond %{REQUEST_URI}::$1 ^(/.+)/(.*)::\2$
10RewriteRule ^(.*) - [E=BASE:%1]
11
12RewriteCond %{ENV:REDIRECT_BASE} (.+)
13RewriteRule .* - [E=BASE:%1]
14
9# Forward the "Authorization" HTTP header 15# Forward the "Authorization" HTTP header
10RewriteCond %{HTTP:Authorization} ^(.*) 16RewriteCond %{HTTP:Authorization} ^(.*)
11RewriteRule .* - [e=HTTP_AUTHORIZATION:%1] 17RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]
12 18
19RewriteCond %{REQUEST_FILENAME} !-f
20RewriteCond %{REQUEST_FILENAME} !-d
21RewriteRule ^((?!api/)[^/]*)/?(.*)$ $2?%{QUERY_STRING} [E=USERSPACE:$1]
22
23RewriteCond %{ENV:REDIRECT_USERSPACE} (.+)
24RewriteRule .* - [E=USERSPACE:%1]
25
13# REST API 26# REST API
14RewriteCond %{REQUEST_FILENAME} !-f 27RewriteCond %{REQUEST_FILENAME} !-f
15RewriteCond %{REQUEST_FILENAME} !-d 28RewriteCond %{REQUEST_FILENAME} !-d
diff --git a/application/ApplicationUtils.php b/application/ApplicationUtils.php
index 911873a0..f21a1ef3 100644
--- a/application/ApplicationUtils.php
+++ b/application/ApplicationUtils.php
@@ -191,6 +191,9 @@ class ApplicationUtils
191 $conf->get('resource.page_cache'), 191 $conf->get('resource.page_cache'),
192 $conf->get('resource.raintpl_tmp'), 192 $conf->get('resource.raintpl_tmp'),
193 ) as $path) { 193 ) as $path) {
194 if (! is_dir($path)) {
195 mkdir($path, 0755, true);
196 }
194 if (! is_readable(realpath($path))) { 197 if (! is_readable(realpath($path))) {
195 $errors[] = '"'.$path.'" '. t('directory is not readable'); 198 $errors[] = '"'.$path.'" '. t('directory is not readable');
196 } 199 }
diff --git a/application/config/ConfigManager.php b/application/config/ConfigManager.php
index 32aaea48..99efc156 100644
--- a/application/config/ConfigManager.php
+++ b/application/config/ConfigManager.php
@@ -22,6 +22,11 @@ class ConfigManager
22 public static $DEFAULT_PLUGINS = array('qrcode'); 22 public static $DEFAULT_PLUGINS = array('qrcode');
23 23
24 /** 24 /**
25 * @var string User space.
26 */
27 protected $userSpace;
28
29 /**
25 * @var string Config folder. 30 * @var string Config folder.
26 */ 31 */
27 protected $configFile; 32 protected $configFile;
@@ -41,12 +46,36 @@ class ConfigManager
41 * 46 *
42 * @param string $configFile Configuration file path without extension. 47 * @param string $configFile Configuration file path without extension.
43 */ 48 */
44 public function __construct($configFile = 'data/config') 49 public function __construct($configFile = null, $userSpace = null)
45 { 50 {
46 $this->configFile = $configFile; 51 $this->userSpace = $this->findLDAPUser($userSpace);
52 if ($configFile !== null) {
53 $this->configFile = $configFile;
54 } else {
55 $this->configFile = ($this->userSpace === null) ? 'data/config' : 'data/' . $this->userSpace . '/config';
56 }
47 $this->initialize(); 57 $this->initialize();
48 } 58 }
49 59
60 public function findLDAPUser($login, $password = null) {
61 $connect = ldap_connect(getenv('SHAARLI_LDAP_HOST'));
62 ldap_set_option($connect, LDAP_OPT_PROTOCOL_VERSION, 3);
63 if (!$connect || !ldap_bind($connect, getenv('SHAARLI_LDAP_DN'), getenv('SHAARLI_LDAP_PASSWORD'))) {
64 return false;
65 }
66
67 $search_query = str_replace('%login%', ldap_escape($login), getenv('SHAARLI_LDAP_FILTER'));
68
69 $search = ldap_search($connect, getenv('SHAARLI_LDAP_BASE'), $search_query);
70 $info = ldap_get_entries($connect, $search);
71
72 if (ldap_count_entries($connect, $search) == 1 && (is_null($password) || ldap_bind($connect, $info[0]["dn"], $password))) {
73 return $login;
74 } else {
75 return null;
76 }
77 }
78
50 /** 79 /**
51 * Reset the ConfigManager instance. 80 * Reset the ConfigManager instance.
52 */ 81 */
@@ -270,6 +299,16 @@ class ConfigManager
270 } 299 }
271 300
272 /** 301 /**
302 * Get the current userspace.
303 *
304 * @return mixed User space.
305 */
306 public function getUserSpace()
307 {
308 return $this->userSpace;
309 }
310
311 /**
273 * Recursive function which find asked setting in the loaded config. 312 * Recursive function which find asked setting in the loaded config.
274 * 313 *
275 * @param array $settings Ordered array which contains keys to find. 314 * @param array $settings Ordered array which contains keys to find.
@@ -342,19 +381,31 @@ class ConfigManager
342 */ 381 */
343 protected function setDefaultValues() 382 protected function setDefaultValues()
344 { 383 {
345 $this->setEmpty('resource.data_dir', 'data'); 384 if ($this->userSpace === null) {
346 $this->setEmpty('resource.config', 'data/config.php'); 385 $data = 'data';
347 $this->setEmpty('resource.datastore', 'data/datastore.php'); 386 $tmp = 'tmp';
348 $this->setEmpty('resource.ban_file', 'data/ipbans.php'); 387 $cache = 'cache';
349 $this->setEmpty('resource.updates', 'data/updates.txt'); 388 $pagecache = 'pagecache';
350 $this->setEmpty('resource.log', 'data/log.txt'); 389 } else {
351 $this->setEmpty('resource.update_check', 'data/lastupdatecheck.txt'); 390 $data = 'data/' . ($this->userSpace);
352 $this->setEmpty('resource.history', 'data/history.php'); 391 $tmp = 'tmp/' . ($this->userSpace);
392 $cache = 'cache/' . ($this->userSpace);
393 $pagecache = 'pagecache/' . ($this->userSpace);
394 }
395
396 $this->setEmpty('resource.data_dir', $data);
397 $this->setEmpty('resource.config', $data . '/config.php');
398 $this->setEmpty('resource.datastore', $data . '/datastore.php');
399 $this->setEmpty('resource.ban_file', $data . '/ipbans.php');
400 $this->setEmpty('resource.updates', $data . '/updates.txt');
401 $this->setEmpty('resource.log', $data . '/log.txt');
402 $this->setEmpty('resource.update_check', $data . '/lastupdatecheck.txt');
403 $this->setEmpty('resource.history', $data . '/history.php');
353 $this->setEmpty('resource.raintpl_tpl', 'tpl/'); 404 $this->setEmpty('resource.raintpl_tpl', 'tpl/');
354 $this->setEmpty('resource.theme', 'default'); 405 $this->setEmpty('resource.theme', 'default');
355 $this->setEmpty('resource.raintpl_tmp', 'tmp/'); 406 $this->setEmpty('resource.raintpl_tmp', $tmp);
356 $this->setEmpty('resource.thumbnails_cache', 'cache'); 407 $this->setEmpty('resource.thumbnails_cache', $cache);
357 $this->setEmpty('resource.page_cache', 'pagecache'); 408 $this->setEmpty('resource.page_cache', $pagecache);
358 409
359 $this->setEmpty('security.ban_after', 4); 410 $this->setEmpty('security.ban_after', 4);
360 $this->setEmpty('security.ban_duration', 1800); 411 $this->setEmpty('security.ban_duration', 1800);
diff --git a/application/security/LoginManager.php b/application/security/LoginManager.php
index d6784d6d..bdfaca7b 100644
--- a/application/security/LoginManager.php
+++ b/application/security/LoginManager.php
@@ -32,6 +32,9 @@ class LoginManager
32 /** @var string User sign-in token depending on remote IP and credentials */ 32 /** @var string User sign-in token depending on remote IP and credentials */
33 protected $staySignedInToken = ''; 33 protected $staySignedInToken = '';
34 34
35 protected $lastErrorReason = '';
36 protected $lastErrorIsBanishable = false;
37
35 /** 38 /**
36 * Constructor 39 * Constructor
37 * 40 *
@@ -83,7 +86,7 @@ class LoginManager
83 */ 86 */
84 public function checkLoginState($cookie, $clientIpId) 87 public function checkLoginState($cookie, $clientIpId)
85 { 88 {
86 if (! $this->configManager->exists('credentials.login')) { 89 if (! $this->configManager->exists('credentials.login') || (isset($_SESSION['username']) && $_SESSION['username'] && $this->configManager->get('credentials.login') !== $_SESSION['username'])) {
87 // Shaarli is not configured yet 90 // Shaarli is not configured yet
88 $this->isLoggedIn = false; 91 $this->isLoggedIn = false;
89 return; 92 return;
@@ -133,20 +136,40 @@ class LoginManager
133 */ 136 */
134 public function checkCredentials($remoteIp, $clientIpId, $login, $password) 137 public function checkCredentials($remoteIp, $clientIpId, $login, $password)
135 { 138 {
136 $hash = sha1($password . $login . $this->configManager->get('credentials.salt')); 139 $this->lastErrorIsBanishable = false;
140
141 if ($this->configManager->getUserSpace() !== null && $this->configManager->getUserSpace() !== $login) {
142 logm($this->configManager->get('resource.log'),
143 $remoteIp,
144 'Trying to login to wrong user space');
145 $this->lastErrorReason = 'You’re trying to access the wrong account.';
146 return false;
147 }
137 148
138 if ($login != $this->configManager->get('credentials.login') 149 logm($this->configManager->get('resource.log'),
139 || $hash != $this->configManager->get('credentials.hash') 150 $remoteIp,
140 ) { 151 'Trying LDAP connection');
152 $result = $this->configManager->findLDAPUser($login, $password);
153 if ($result === false) {
141 logm( 154 logm(
142 $this->configManager->get('resource.log'), 155 $this->configManager->get('resource.log'),
143 $remoteIp, 156 $remoteIp,
144 'Login failed for user ' . $login 157 'Impossible to connect to LDAP'
145 ); 158 );
159 $this->lastErrorReason = 'Server error.';
160 return false;
161 } else if (is_null($result)) {
162 logm(
163 $this->configManager->get('resource.log'),
164 $remoteIp,
165 'Login failed for user ' . $login
166 );
167 $this->lastErrorIsBanishable = true;
168 $this->lastErrorReason = 'Wrong login/password.';
146 return false; 169 return false;
147 } 170 }
148 171
149 $this->sessionManager->storeLoginInfo($clientIpId); 172 $this->sessionManager->storeLoginInfo($clientIpId, $login);
150 logm( 173 logm(
151 $this->configManager->get('resource.log'), 174 $this->configManager->get('resource.log'),
152 $remoteIp, 175 $remoteIp,
@@ -187,6 +210,10 @@ class LoginManager
187 */ 210 */
188 public function handleFailedLogin($server) 211 public function handleFailedLogin($server)
189 { 212 {
213 if (!$this->lastErrorIsBanishable) {
214 return $this->lastErrorReason ?: 'Error during login.';
215 };
216
190 $ip = $server['REMOTE_ADDR']; 217 $ip = $server['REMOTE_ADDR'];
191 $trusted = $this->configManager->get('security.trusted_proxies', []); 218 $trusted = $this->configManager->get('security.trusted_proxies', []);
192 219
@@ -215,6 +242,7 @@ class LoginManager
215 ); 242 );
216 } 243 }
217 $this->writeBanFile(); 244 $this->writeBanFile();
245 return $this->lastErrorReason ?: 'Error during login.';
218 } 246 }
219 247
220 /** 248 /**
diff --git a/application/security/SessionManager.php b/application/security/SessionManager.php
index b8b8ab8d..5eb4aac5 100644
--- a/application/security/SessionManager.php
+++ b/application/security/SessionManager.php
@@ -111,10 +111,10 @@ class SessionManager
111 * 111 *
112 * @param string $clientIpId Client IP address identifier 112 * @param string $clientIpId Client IP address identifier
113 */ 113 */
114 public function storeLoginInfo($clientIpId) 114 public function storeLoginInfo($clientIpId, $login = null)
115 { 115 {
116 $this->session['ip'] = $clientIpId; 116 $this->session['ip'] = $clientIpId;
117 $this->session['username'] = $this->conf->get('credentials.login'); 117 $this->session['username'] = $login ?: $this->conf->get('credentials.login');
118 $this->extendTimeValidityBy(self::$SHORT_TIMEOUT); 118 $this->extendTimeValidityBy(self::$SHORT_TIMEOUT);
119 } 119 }
120 120
diff --git a/index.php b/index.php
index 4b86a3e2..58ae2ddb 100644
--- a/index.php
+++ b/index.php
@@ -121,7 +121,32 @@ if (isset($_COOKIE['shaarli']) && !SessionManager::checkId($_COOKIE['shaarli']))
121 $_COOKIE['shaarli'] = session_id(); 121 $_COOKIE['shaarli'] = session_id();
122} 122}
123 123
124$conf = new ConfigManager(); 124$folderBase = getenv("BASE");
125
126if (getenv("USERSPACE")) {
127 if (isset($_GET["do"]) && $_GET["do"] == "login") {
128 header("Location: $folderBase/?do=login");
129 exit;
130 }
131 $userspace = preg_replace("/[^-_A-Za-z0-9]/", '', getenv("USERSPACE"));
132} else if (isset($_SESSION["username"]) && $_SESSION["username"]) {
133 header("Location: " . $folderBase . "/" . $_SESSION["username"] . "?");
134 exit;
135} else if (!isset($_GET["do"]) || $_GET["do"] != "login") {
136 header("Location: $folderBase/?do=login");
137 exit;
138}
139
140if (!isset($userspace) && isset($_POST["login"])) {
141 $userspace = preg_replace("/[^-_A-Za-z0-9]/", '', $_POST["login"]);
142 error_log("debugImmae: setting userspace from POST: " . $userspace);
143}
144
145if (isset($userspace)) {
146 $conf = new ConfigManager(null, $userspace);
147} else {
148 $conf = new ConfigManager();
149}
125$sessionManager = new SessionManager($_SESSION, $conf); 150$sessionManager = new SessionManager($_SESSION, $conf);
126$loginManager = new LoginManager($GLOBALS, $conf, $sessionManager); 151$loginManager = new LoginManager($GLOBALS, $conf, $sessionManager);
127$loginManager->generateStaySignedInToken($_SERVER['REMOTE_ADDR']); 152$loginManager->generateStaySignedInToken($_SERVER['REMOTE_ADDR']);
@@ -175,7 +200,7 @@ if (! is_file($conf->getConfigFileExt())) {
175 } 200 }
176 201
177 // Display the installation form if no existing config is found 202 // Display the installation form if no existing config is found
178 install($conf, $sessionManager, $loginManager); 203 install($conf, $sessionManager, $loginManager, $userspace);
179} 204}
180 205
181$loginManager->checkLoginState($_COOKIE, $clientIpId); 206$loginManager->checkLoginState($_COOKIE, $clientIpId);
@@ -205,6 +230,7 @@ if (isset($_POST['login'])) {
205 && $loginManager->checkCredentials($_SERVER['REMOTE_ADDR'], $clientIpId, $_POST['login'], $_POST['password']) 230 && $loginManager->checkCredentials($_SERVER['REMOTE_ADDR'], $clientIpId, $_POST['login'], $_POST['password'])
206 ) { 231 ) {
207 $loginManager->handleSuccessfulLogin($_SERVER); 232 $loginManager->handleSuccessfulLogin($_SERVER);
233 $userspace = $_POST['login'];
208 234
209 $cookiedir = ''; 235 $cookiedir = '';
210 if (dirname($_SERVER['SCRIPT_NAME']) != '/') { 236 if (dirname($_SERVER['SCRIPT_NAME']) != '/') {
@@ -241,25 +267,25 @@ if (isset($_POST['login'])) {
241 $uri .= '&'.$param.'='.urlencode($_GET[$param]); 267 $uri .= '&'.$param.'='.urlencode($_GET[$param]);
242 } 268 }
243 } 269 }
244 header('Location: '. $uri); 270 header('Location: '. $userspace . $uri);
245 exit; 271 exit;
246 } 272 }
247 273
248 if (isset($_GET['edit_link'])) { 274 if (isset($_GET['edit_link'])) {
249 header('Location: ?edit_link='. escape($_GET['edit_link'])); 275 header('Location: ' . $userspace . '?edit_link='. escape($_GET['edit_link']));
250 exit; 276 exit;
251 } 277 }
252 278
253 if (isset($_POST['returnurl'])) { 279 if (isset($_POST['returnurl'])) {
254 // Prevent loops over login screen. 280 // Prevent loops over login screen.
255 if (strpos($_POST['returnurl'], 'do=login') === false) { 281 if (strpos($_POST['returnurl'], 'do=login') === false) {
256 header('Location: '. generateLocation($_POST['returnurl'], $_SERVER['HTTP_HOST'])); 282 header('Location: ' . generateLocation($_POST['returnurl'], $_SERVER['HTTP_HOST']));
257 exit; 283 exit;
258 } 284 }
259 } 285 }
260 header('Location: ?'); exit; 286 header('Location: '. $userspace . '?'); exit;
261 } else { 287 } else {
262 $loginManager->handleFailedLogin($_SERVER); 288 $errorReason = $loginManager->handleFailedLogin($_SERVER);
263 $redir = '&username='. urlencode($_POST['login']); 289 $redir = '&username='. urlencode($_POST['login']);
264 if (isset($_GET['post'])) { 290 if (isset($_GET['post'])) {
265 $redir .= '&post=' . urlencode($_GET['post']); 291 $redir .= '&post=' . urlencode($_GET['post']);
@@ -270,7 +296,7 @@ if (isset($_POST['login'])) {
270 } 296 }
271 } 297 }
272 // Redirect to login screen. 298 // Redirect to login screen.
273 echo '<script>alert("'. t("Wrong login/password.") .'");document.location=\'?do=login'.$redir.'\';</script>'; 299 echo '<script>alert("'. t($errorReason) .'");document.location=\'?do=login'.$redir.'\';</script>';
274 exit; 300 exit;
275 } 301 }
276} 302}
@@ -1719,7 +1745,7 @@ function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager)
1719 * @param SessionManager $sessionManager SessionManager instance 1745 * @param SessionManager $sessionManager SessionManager instance
1720 * @param LoginManager $loginManager LoginManager instance 1746 * @param LoginManager $loginManager LoginManager instance
1721 */ 1747 */
1722function install($conf, $sessionManager, $loginManager) { 1748function install($conf, $sessionManager, $loginManager, $userspace) {
1723 // On free.fr host, make sure the /sessions directory exists, otherwise login will not work. 1749 // On free.fr host, make sure the /sessions directory exists, otherwise login will not work.
1724 if (endsWith($_SERVER['HTTP_HOST'],'.free.fr') && !is_dir($_SERVER['DOCUMENT_ROOT'].'/sessions')) mkdir($_SERVER['DOCUMENT_ROOT'].'/sessions',0705); 1750 if (endsWith($_SERVER['HTTP_HOST'],'.free.fr') && !is_dir($_SERVER['DOCUMENT_ROOT'].'/sessions')) mkdir($_SERVER['DOCUMENT_ROOT'].'/sessions',0705);
1725 1751
@@ -1755,7 +1781,7 @@ function install($conf, $sessionManager, $loginManager) {
1755 } 1781 }
1756 1782
1757 1783
1758 if (!empty($_POST['setlogin']) && !empty($_POST['setpassword'])) 1784 if (true)
1759 { 1785 {
1760 $tz = 'UTC'; 1786 $tz = 'UTC';
1761 if (!empty($_POST['continent']) && !empty($_POST['city']) 1787 if (!empty($_POST['continent']) && !empty($_POST['city'])
@@ -1764,15 +1790,15 @@ function install($conf, $sessionManager, $loginManager) {
1764 $tz = $_POST['continent'].'/'.$_POST['city']; 1790 $tz = $_POST['continent'].'/'.$_POST['city'];
1765 } 1791 }
1766 $conf->set('general.timezone', $tz); 1792 $conf->set('general.timezone', $tz);
1767 $login = $_POST['setlogin']; 1793 $conf->set('credentials.login', $userspace);
1768 $conf->set('credentials.login', $login);
1769 $salt = sha1(uniqid('', true) .'_'. mt_rand()); 1794 $salt = sha1(uniqid('', true) .'_'. mt_rand());
1770 $conf->set('credentials.salt', $salt); 1795 $conf->set('credentials.salt', $salt);
1771 $conf->set('credentials.hash', sha1($_POST['setpassword'] . $login . $salt)); 1796 $hash = sha1(uniqid('', true) .'_'. mt_rand());
1797 $conf->set('credentials.hash', $hash);
1772 if (!empty($_POST['title'])) { 1798 if (!empty($_POST['title'])) {
1773 $conf->set('general.title', escape($_POST['title'])); 1799 $conf->set('general.title', escape($_POST['title']));
1774 } else { 1800 } else {
1775 $conf->set('general.title', 'Shared links on '.escape(index_url($_SERVER))); 1801 $conf->set('general.title', ucwords(str_replace("_", " ", $userspace)));
1776 } 1802 }
1777 $conf->set('translation.language', escape($_POST['language'])); 1803 $conf->set('translation.language', escape($_POST['language']));
1778 $conf->set('updates.check_updates', !empty($_POST['updateCheck'])); 1804 $conf->set('updates.check_updates', !empty($_POST['updateCheck']));
@@ -1841,7 +1867,12 @@ $container['history'] = $history;
1841$app = new \Slim\App($container); 1867$app = new \Slim\App($container);
1842 1868
1843// REST API routes 1869// REST API routes
1844$app->group('/api/v1', function() { 1870if (isset($userspace)) {
1871 $mountpoint = '/' . $userspace . '/api/v1';
1872} else {
1873 $mountpoint = '/api/v1';
1874}
1875$app->group($mountpoint, function() {
1845 $this->get('/info', '\Shaarli\Api\Controllers\Info:getInfo')->setName('getInfo'); 1876 $this->get('/info', '\Shaarli\Api\Controllers\Info:getInfo')->setName('getInfo');
1846 $this->get('/links', '\Shaarli\Api\Controllers\Links:getLinks')->setName('getLinks'); 1877 $this->get('/links', '\Shaarli\Api\Controllers\Links:getLinks')->setName('getLinks');
1847 $this->get('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:getLink')->setName('getLink'); 1878 $this->get('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:getLink')->setName('getLink');
@@ -1860,7 +1891,7 @@ $app->group('/api/v1', function() {
1860$response = $app->run(true); 1891$response = $app->run(true);
1861// Hack to make Slim and Shaarli router work together: 1892// Hack to make Slim and Shaarli router work together:
1862// If a Slim route isn't found and NOT API call, we call renderPage(). 1893// If a Slim route isn't found and NOT API call, we call renderPage().
1863if ($response->getStatusCode() == 404 && strpos($_SERVER['REQUEST_URI'], '/api/v1') === false) { 1894if ($response->getStatusCode() == 404 && strpos($_SERVER['REQUEST_URI'], $mountpoint) === false) {
1864 // We use UTF-8 for proper international characters handling. 1895 // We use UTF-8 for proper international characters handling.
1865 header('Content-Type: text/html; charset=utf-8'); 1896 header('Content-Type: text/html; charset=utf-8');
1866 renderPage($conf, $pluginManager, $linkDb, $history, $sessionManager, $loginManager); 1897 renderPage($conf, $pluginManager, $linkDb, $history, $sessionManager, $loginManager);