diff options
Diffstat (limited to 'index.php')
-rw-r--r-- | index.php | 355 |
1 files changed, 185 insertions, 170 deletions
@@ -1,9 +1,9 @@ | |||
1 | <?php | 1 | <?php |
2 | // Shaarli 0.0.41 beta - Shaare your links... | 2 | // Shaarli 0.0.42 beta - Shaare your links... |
3 | // The personal, minimalist, super-fast, no-database delicious clone. By sebsauvage.net | 3 | // The personal, minimalist, super-fast, no-database Delicious clone. By sebsauvage.net |
4 | // http://sebsauvage.net/wiki/doku.php?id=php:shaarli | 4 | // http://sebsauvage.net/wiki/doku.php?id=php:shaarli |
5 | // Licence: http://www.opensource.org/licenses/zlib-license.php | 5 | // Licence: http://www.opensource.org/licenses/zlib-license.php |
6 | // Requires: php 5.1.x (but autocomplete fields will only work if you have php 5.2.x) | 6 | // Requires: PHP 5.1.x (but autocomplete fields will only work if you have PHP 5.2.x) |
7 | // ----------------------------------------------------------------------------------------------- | 7 | // ----------------------------------------------------------------------------------------------- |
8 | // NEVER TRUST IN PHP.INI | 8 | // NEVER TRUST IN PHP.INI |
9 | // Some hosts do not define a default timezone in php.ini, | 9 | // Some hosts do not define a default timezone in php.ini, |
@@ -21,22 +21,28 @@ $GLOBALS['config']['BAN_AFTER'] = 4; // Ban IP after this many failures. | |||
21 | $GLOBALS['config']['BAN_DURATION'] = 1800; // Ban duration for IP address after login failures (in seconds) (1800 sec. = 30 minutes) | 21 | $GLOBALS['config']['BAN_DURATION'] = 1800; // Ban duration for IP address after login failures (in seconds) (1800 sec. = 30 minutes) |
22 | $GLOBALS['config']['OPEN_SHAARLI'] = false; // If true, anyone can add/edit/delete links without having to login | 22 | $GLOBALS['config']['OPEN_SHAARLI'] = false; // If true, anyone can add/edit/delete links without having to login |
23 | $GLOBALS['config']['HIDE_TIMESTAMPS'] = false; // If true, the moment when links were saved are not shown to users that are not logged in. | 23 | $GLOBALS['config']['HIDE_TIMESTAMPS'] = false; // If true, the moment when links were saved are not shown to users that are not logged in. |
24 | $GLOBALS['config']['SHOW_ATOM'] = false; // If true, an extra "ATOM feed" button will be displayed in the toolbar | ||
24 | $GLOBALS['config']['ENABLE_THUMBNAILS'] = true; // Enable thumbnails in links. | 25 | $GLOBALS['config']['ENABLE_THUMBNAILS'] = true; // Enable thumbnails in links. |
25 | $GLOBALS['config']['CACHEDIR'] = 'cache'; // Cache directory for thumbnails for SLOW services (like flickr) | 26 | $GLOBALS['config']['CACHEDIR'] = 'cache'; // Cache directory for thumbnails for SLOW services (like flickr) |
26 | $GLOBALS['config']['PAGECACHE'] = 'pagecache'; // Page cache directory. | 27 | $GLOBALS['config']['PAGECACHE'] = 'pagecache'; // Page cache directory. |
27 | $GLOBALS['config']['ENABLE_LOCALCACHE'] = true; // Enable Shaarli to store thumbnail in a local cache. Disable to reduce webspace usage. | 28 | $GLOBALS['config']['ENABLE_LOCALCACHE'] = true; // Enable Shaarli to store thumbnail in a local cache. Disable to reduce web space usage. |
28 | $GLOBALS['config']['PUBSUBHUB_URL'] = ''; // PubSubHubbub support. Put an empty string to disable, or put your hub url here to enable. | 29 | $GLOBALS['config']['PUBSUBHUB_URL'] = ''; // PubSubHubbub support. Put an empty string to disable, or put your hub url here to enable. |
30 | $GLOBALS['config']['RAINTPL_TMP'] = 'tmp/' ; // Raintpl cache directory (keep the trailing slash!) | ||
31 | $GLOBALS['config']['RAINTPL_TPL'] = 'tpl/' ; // Raintpl template directory (keep the trailing slash!) | ||
29 | $GLOBALS['config']['UPDATECHECK_FILENAME'] = $GLOBALS['config']['DATADIR'].'/lastupdatecheck.txt'; // For updates check of Shaarli. | 32 | $GLOBALS['config']['UPDATECHECK_FILENAME'] = $GLOBALS['config']['DATADIR'].'/lastupdatecheck.txt'; // For updates check of Shaarli. |
30 | $GLOBALS['config']['UPDATECHECK_INTERVAL'] = 86400 ; // Updates check frequency for Shaarli. 86400 seconds=24 hours | 33 | $GLOBALS['config']['UPDATECHECK_INTERVAL'] = 86400 ; // Updates check frequency for Shaarli. 86400 seconds=24 hours |
31 | // Note: You must have publisher.php in the same directory as Shaarli index.php | 34 | // Note: You must have publisher.php in the same directory as Shaarli index.php |
35 | $GLOBALS['config']['ARCHIVE_ORG'] = false; // For each link, add a link to an archived version on archive.org | ||
32 | // ----------------------------------------------------------------------------------------------- | 36 | // ----------------------------------------------------------------------------------------------- |
33 | // You should not touch below (or at your own risks !) | 37 | // You should not touch below (or at your own risks!) |
34 | // Optionnal config file. | 38 | // Optional config file. |
35 | if (is_file($GLOBALS['config']['DATADIR'].'/options.php')) require($GLOBALS['config']['DATADIR'].'/options.php'); | 39 | if (is_file($GLOBALS['config']['DATADIR'].'/options.php')) require($GLOBALS['config']['DATADIR'].'/options.php'); |
36 | 40 | ||
37 | define('shaarli_version','0.0.41 beta'); | 41 | define('shaarli_version','0.0.42 beta'); |
38 | define('PHPPREFIX','<?php /* '); // Prefix to encapsulate data in php code. | 42 | define('PHPPREFIX','<?php /* '); // Prefix to encapsulate data in PHP code. |
39 | define('PHPSUFFIX',' */ ?>'); // Suffix to encapsulate data in php code. | 43 | define('PHPSUFFIX',' */ ?>'); // Suffix to encapsulate data in PHP code. |
44 | // http://server.com/x/shaarli --> /shaarli/ | ||
45 | define('WEB_PATH', substr($_SERVER["REQUEST_URI"], 0, 1+strrpos($_SERVER["REQUEST_URI"], '/', 0))); | ||
40 | 46 | ||
41 | // Force cookie path (but do not change lifetime) | 47 | // Force cookie path (but do not change lifetime) |
42 | $cookie=session_get_cookie_params(); | 48 | $cookie=session_get_cookie_params(); |
@@ -46,8 +52,8 @@ session_set_cookie_params($cookie['lifetime'],$cookiedir,$_SERVER['HTTP_HOST']); | |||
46 | // Set session parameters on server side. | 52 | // Set session parameters on server side. |
47 | define('INACTIVITY_TIMEOUT',3600); // (in seconds). If the user does not access any page within this time, his/her session is considered expired. | 53 | define('INACTIVITY_TIMEOUT',3600); // (in seconds). If the user does not access any page within this time, his/her session is considered expired. |
48 | ini_set('session.use_cookies', 1); // Use cookies to store session. | 54 | ini_set('session.use_cookies', 1); // Use cookies to store session. |
49 | ini_set('session.use_only_cookies', 1); // Force cookies for session (phpsessionID forbidden in URL) | 55 | ini_set('session.use_only_cookies', 1); // Force cookies for session (phpsessionID forbidden in URL). |
50 | ini_set('session.use_trans_sid', false); // Prevent php to use sessionID in URL if cookies are disabled. | 56 | ini_set('session.use_trans_sid', false); // Prevent PHP form using sessionID in URL if cookies are disabled. |
51 | session_name('shaarli'); | 57 | session_name('shaarli'); |
52 | if (session_id() == '') session_start(); // Start session if needed (Some server auto-start sessions). | 58 | if (session_id() == '') session_start(); // Start session if needed (Some server auto-start sessions). |
53 | 59 | ||
@@ -61,9 +67,8 @@ error_reporting(E_ALL^E_WARNING); // See all error except warnings. | |||
61 | //error_reporting(-1); // See all errors (for debugging only) | 67 | //error_reporting(-1); // See all errors (for debugging only) |
62 | 68 | ||
63 | include "inc/rain.tpl.class.php"; //include Rain TPL | 69 | include "inc/rain.tpl.class.php"; //include Rain TPL |
64 | raintpl::$tpl_dir = "tpl/"; // template directory | 70 | raintpl::$tpl_dir = $GLOBALS['config']['RAINTPL_TPL']; // template directory |
65 | if (!is_dir('tmp')) { mkdir('tmp',0705); chmod('tmp',0705); } | 71 | raintpl::$cache_dir = $GLOBALS['config']['RAINTPL_TMP']; // cache directory |
66 | raintpl::$cache_dir = "tmp/"; // cache directory | ||
67 | 72 | ||
68 | ob_start(); // Output buffering for the page cache. | 73 | ob_start(); // Output buffering for the page cache. |
69 | 74 | ||
@@ -83,18 +88,8 @@ header("Cache-Control: no-store, no-cache, must-revalidate"); | |||
83 | header("Cache-Control: post-check=0, pre-check=0", false); | 88 | header("Cache-Control: post-check=0, pre-check=0", false); |
84 | header("Pragma: no-cache"); | 89 | header("Pragma: no-cache"); |
85 | 90 | ||
86 | // Directories creations (Note that your web host may require differents rights than 705.) | 91 | // Directories creations (Note that your web host may require different rights than 705.) |
87 | if (!is_writable(realpath(dirname(__FILE__)))) die('<pre>ERROR: Shaarli does not have the right to write in its own directory ('.realpath(dirname(__FILE__)).').</pre>'); | 92 | if (!is_writable(realpath(dirname(__FILE__)))) die('<pre>ERROR: Shaarli does not have the right to write in its own directory ('.realpath(dirname(__FILE__)).').</pre>'); |
88 | if (!is_dir($GLOBALS['config']['DATADIR'])) { mkdir($GLOBALS['config']['DATADIR'],0705); chmod($GLOBALS['config']['DATADIR'],0705); } | ||
89 | if (!is_dir('tmp')) { mkdir('tmp',0705); chmod('tmp',0705); } // For RainTPL temporary files. | ||
90 | if (!is_file($GLOBALS['config']['DATADIR'].'/.htaccess')) { file_put_contents($GLOBALS['config']['DATADIR'].'/.htaccess',"Allow from none\nDeny from all\n"); } // Protect data files. | ||
91 | // Second check to see if Shaarli can write in its directory, because on some hosts is_writable() is not reliable. | ||
92 | if (!is_file($GLOBALS['config']['DATADIR'].'/.htaccess')) die('<pre>ERROR: Shaarli does not have the right to write in its data directory ('.realpath($GLOBALS['config']['DATADIR']).').</pre>'); | ||
93 | if ($GLOBALS['config']['ENABLE_LOCALCACHE']) | ||
94 | { | ||
95 | if (!is_dir($GLOBALS['config']['CACHEDIR'])) { mkdir($GLOBALS['config']['CACHEDIR'],0705); chmod($GLOBALS['config']['CACHEDIR'],0705); } | ||
96 | if (!is_file($GLOBALS['config']['CACHEDIR'].'/.htaccess')) { file_put_contents($GLOBALS['config']['CACHEDIR'].'/.htaccess',"Allow from none\nDeny from all\n"); } // Protect data files. | ||
97 | } | ||
98 | 93 | ||
99 | // Handling of old config file which do not have the new parameters. | 94 | // Handling of old config file which do not have the new parameters. |
100 | if (empty($GLOBALS['title'])) $GLOBALS['title']='Shared links on '.htmlspecialchars(indexUrl()); | 95 | if (empty($GLOBALS['title'])) $GLOBALS['title']='Shared links on '.htmlspecialchars(indexUrl()); |
@@ -103,6 +98,7 @@ if (empty($GLOBALS['redirector'])) $GLOBALS['redirector']=''; | |||
103 | if (empty($GLOBALS['disablesessionprotection'])) $GLOBALS['disablesessionprotection']=false; | 98 | if (empty($GLOBALS['disablesessionprotection'])) $GLOBALS['disablesessionprotection']=false; |
104 | if (empty($GLOBALS['disablejquery'])) $GLOBALS['disablejquery']=false; | 99 | if (empty($GLOBALS['disablejquery'])) $GLOBALS['disablejquery']=false; |
105 | if (empty($GLOBALS['privateLinkByDefault'])) $GLOBALS['privateLinkByDefault']=false; | 100 | if (empty($GLOBALS['privateLinkByDefault'])) $GLOBALS['privateLinkByDefault']=false; |
101 | if (empty($GLOBALS['titleLink'])) $GLOBALS['titleLink']='?'; | ||
106 | // I really need to rewrite Shaarli with a proper configuation manager. | 102 | // I really need to rewrite Shaarli with a proper configuation manager. |
107 | 103 | ||
108 | // Run config screen if first run: | 104 | // Run config screen if first run: |
@@ -110,17 +106,19 @@ if (!is_file($GLOBALS['config']['CONFIG_FILE'])) install(); | |||
110 | 106 | ||
111 | require $GLOBALS['config']['CONFIG_FILE']; // Read login/password hash into $GLOBALS. | 107 | require $GLOBALS['config']['CONFIG_FILE']; // Read login/password hash into $GLOBALS. |
112 | 108 | ||
109 | // a token depending of deployment salt, user password, and the current ip | ||
110 | define('STAY_SIGNED_IN_TOKEN', sha1($GLOBALS['hash'].$_SERVER["REMOTE_ADDR"].$GLOBALS['salt'])); | ||
113 | 111 | ||
114 | autoLocale(); // Sniff browser language and set date format accordingly. | 112 | autoLocale(); // Sniff browser language and set date format accordingly. |
115 | header('Content-Type: text/html; charset=utf-8'); // We use UTF-8 for proper international characters handling. | 113 | header('Content-Type: text/html; charset=utf-8'); // We use UTF-8 for proper international characters handling. |
116 | 114 | ||
117 | // Check php version | 115 | // Check PHP version |
118 | function checkphpversion() | 116 | function checkphpversion() |
119 | { | 117 | { |
120 | if (version_compare(PHP_VERSION, '5.1.0') < 0) | 118 | if (version_compare(PHP_VERSION, '5.1.0') < 0) |
121 | { | 119 | { |
122 | header('Content-Type: text/plain; charset=utf-8'); | 120 | header('Content-Type: text/plain; charset=utf-8'); |
123 | echo 'Your server supports php '.PHP_VERSION.'. Shaarli requires at least php 5.1.0, and thus cannot run. Sorry.'; | 121 | echo 'Your server supports PHP '.PHP_VERSION.'. Shaarli requires at least php 5.1.0, and thus cannot run. Sorry.'; |
124 | exit; | 122 | exit; |
125 | } | 123 | } |
126 | } | 124 | } |
@@ -137,9 +135,9 @@ function checkUpdate() | |||
137 | if (!is_file($GLOBALS['config']['UPDATECHECK_FILENAME']) || (filemtime($GLOBALS['config']['UPDATECHECK_FILENAME'])<time()-($GLOBALS['config']['UPDATECHECK_INTERVAL']))) | 135 | if (!is_file($GLOBALS['config']['UPDATECHECK_FILENAME']) || (filemtime($GLOBALS['config']['UPDATECHECK_FILENAME'])<time()-($GLOBALS['config']['UPDATECHECK_INTERVAL']))) |
138 | { | 136 | { |
139 | $version=shaarli_version; | 137 | $version=shaarli_version; |
140 | list($httpstatus,$headers,$data) = getHTTP('http://sebsauvage.net/files/shaarli_version.txt',2); | 138 | list($httpstatus,$headers,$data) = getHTTP('https://raw.githubusercontent.com/shaarli/Shaarli/master/shaarli_version.txt',2); |
141 | if (strpos($httpstatus,'200 OK')!==false) $version=$data; | 139 | if (strpos($httpstatus,'200 OK')!==false) $version=$data; |
142 | // If failed, nevermind. We don't want to bother the user with that. | 140 | // If failed, never mind. We don't want to bother the user with that. |
143 | file_put_contents($GLOBALS['config']['UPDATECHECK_FILENAME'],$version); // touch file date | 141 | file_put_contents($GLOBALS['config']['UPDATECHECK_FILENAME'],$version); // touch file date |
144 | } | 142 | } |
145 | // Compare versions: | 143 | // Compare versions: |
@@ -155,11 +153,11 @@ function checkUpdate() | |||
155 | class pageCache | 153 | class pageCache |
156 | { | 154 | { |
157 | private $url; // Full URL of the page to cache (typically the value returned by pageUrl()) | 155 | private $url; // Full URL of the page to cache (typically the value returned by pageUrl()) |
158 | private $shouldBeCached; // boolean: Should this url be cached ? | 156 | private $shouldBeCached; // boolean: Should this url be cached? |
159 | private $filename; // Name of the cache file for this url | 157 | private $filename; // Name of the cache file for this url. |
160 | 158 | ||
161 | /* | 159 | /* |
162 | $url = url (typically the value returned by pageUrl()) | 160 | $url = URL (typically the value returned by pageUrl()) |
163 | $shouldBeCached = boolean. If false, the cache will be disabled. | 161 | $shouldBeCached = boolean. If false, the cache will be disabled. |
164 | */ | 162 | */ |
165 | public function __construct($url,$shouldBeCached) | 163 | public function __construct($url,$shouldBeCached) |
@@ -182,7 +180,6 @@ class pageCache | |||
182 | public function cache($page) | 180 | public function cache($page) |
183 | { | 181 | { |
184 | if (!$this->shouldBeCached) return; | 182 | if (!$this->shouldBeCached) return; |
185 | if (!is_dir($GLOBALS['config']['PAGECACHE'])) { mkdir($GLOBALS['config']['PAGECACHE'],0705); chmod($GLOBALS['config']['PAGECACHE'],0705); } | ||
186 | file_put_contents($this->filename,$page); | 183 | file_put_contents($this->filename,$page); |
187 | } | 184 | } |
188 | 185 | ||
@@ -221,8 +218,8 @@ function nl2br_escaped($html) | |||
221 | return str_replace('>','>',str_replace('<','<',nl2br($html))); | 218 | return str_replace('>','>',str_replace('<','<',nl2br($html))); |
222 | } | 219 | } |
223 | 220 | ||
224 | /* Returns the small hash of a string | 221 | /* Returns the small hash of a string, using RFC 4648 base64url format |
225 | eg. smallHash('20111006_131924') --> yZH23w | 222 | e.g. smallHash('20111006_131924') --> yZH23w |
226 | Small hashes: | 223 | Small hashes: |
227 | - are unique (well, as unique as crc32, at last) | 224 | - are unique (well, as unique as crc32, at last) |
228 | - are always 6 characters long. | 225 | - are always 6 characters long. |
@@ -233,13 +230,10 @@ function nl2br_escaped($html) | |||
233 | function smallHash($text) | 230 | function smallHash($text) |
234 | { | 231 | { |
235 | $t = rtrim(base64_encode(hash('crc32',$text,true)),'='); | 232 | $t = rtrim(base64_encode(hash('crc32',$text,true)),'='); |
236 | $t = str_replace('+','-',$t); // Get rid of characters which need encoding in URLs. | 233 | return strtr($t, '+/', '-_'); |
237 | $t = str_replace('/','_',$t); | ||
238 | $t = str_replace('=','@',$t); | ||
239 | return $t; | ||
240 | } | 234 | } |
241 | 235 | ||
242 | // In a string, converts urls to clickable links. | 236 | // In a string, converts URLs to clickable links. |
243 | // Function inspired from http://www.php.net/manual/en/function.preg-replace.php#85722 | 237 | // Function inspired from http://www.php.net/manual/en/function.preg-replace.php#85722 |
244 | function text2clickable($url) | 238 | function text2clickable($url) |
245 | { | 239 | { |
@@ -260,8 +254,8 @@ function keepMultipleSpaces($text) | |||
260 | function autoLocale() | 254 | function autoLocale() |
261 | { | 255 | { |
262 | $loc='en_US'; // Default if browser does not send HTTP_ACCEPT_LANGUAGE | 256 | $loc='en_US'; // Default if browser does not send HTTP_ACCEPT_LANGUAGE |
263 | if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) // eg. "fr,fr-fr;q=0.8,en;q=0.5,en-us;q=0.3" | 257 | if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) // e.g. "fr,fr-fr;q=0.8,en;q=0.5,en-us;q=0.3" |
264 | { // (It's a bit crude, but it works very well. Prefered language is always presented first.) | 258 | { // (It's a bit crude, but it works very well. Preferred language is always presented first.) |
265 | if (preg_match('/([a-z]{2}(-[a-z]{2})?)/i',$_SERVER['HTTP_ACCEPT_LANGUAGE'],$matches)) $loc=$matches[1]; | 259 | if (preg_match('/([a-z]{2}(-[a-z]{2})?)/i',$_SERVER['HTTP_ACCEPT_LANGUAGE'],$matches)) $loc=$matches[1]; |
266 | } | 260 | } |
267 | setlocale(LC_TIME,$loc); // LC_TIME = Set local for date/time format only. | 261 | setlocale(LC_TIME,$loc); // LC_TIME = Set local for date/time format only. |
@@ -297,16 +291,20 @@ function allIPs() | |||
297 | return $ip; | 291 | return $ip; |
298 | } | 292 | } |
299 | 293 | ||
294 | function fillSessionInfo() { | ||
295 | $_SESSION['uid'] = sha1(uniqid('',true).'_'.mt_rand()); // Generate unique random number (different than phpsessionid) | ||
296 | $_SESSION['ip']=allIPs(); // We store IP address(es) of the client to make sure session is not hijacked. | ||
297 | $_SESSION['username']=$GLOBALS['login']; | ||
298 | $_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT; // Set session expiration. | ||
299 | } | ||
300 | |||
300 | // Check that user/password is correct. | 301 | // Check that user/password is correct. |
301 | function check_auth($login,$password) | 302 | function check_auth($login,$password) |
302 | { | 303 | { |
303 | $hash = sha1($password.$login.$GLOBALS['salt']); | 304 | $hash = sha1($password.$login.$GLOBALS['salt']); |
304 | if ($login==$GLOBALS['login'] && $hash==$GLOBALS['hash']) | 305 | if ($login==$GLOBALS['login'] && $hash==$GLOBALS['hash']) |
305 | { // Login/password is correct. | 306 | { // Login/password is correct. |
306 | $_SESSION['uid'] = sha1(uniqid('',true).'_'.mt_rand()); // generate unique random number (different than phpsessionid) | 307 | fillSessionInfo(); |
307 | $_SESSION['ip']=allIPs(); // We store IP address(es) of the client to make sure session is not hijacked. | ||
308 | $_SESSION['username']=$login; | ||
309 | $_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT; // Set session expiration. | ||
310 | logm('Login successful'); | 308 | logm('Login successful'); |
311 | return True; | 309 | return True; |
312 | } | 310 | } |
@@ -321,6 +319,11 @@ function isLoggedIn() | |||
321 | 319 | ||
322 | if (!isset($GLOBALS['login'])) return false; // Shaarli is not configured yet. | 320 | if (!isset($GLOBALS['login'])) return false; // Shaarli is not configured yet. |
323 | 321 | ||
322 | if (@$_COOKIE['shaarli_staySignedIn']===STAY_SIGNED_IN_TOKEN) | ||
323 | { | ||
324 | fillSessionInfo(); | ||
325 | return true; | ||
326 | } | ||
324 | // If session does not exist on server side, or IP address has changed, or session has expired, logout. | 327 | // If session does not exist on server side, or IP address has changed, or session has expired, logout. |
325 | if (empty($_SESSION['uid']) || ($GLOBALS['disablesessionprotection']==false && $_SESSION['ip']!=allIPs()) || time()>=$_SESSION['expires_on']) | 328 | if (empty($_SESSION['uid']) || ($GLOBALS['disablesessionprotection']==false && $_SESSION['ip']!=allIPs()) || time()>=$_SESSION['expires_on']) |
326 | { | 329 | { |
@@ -334,7 +337,9 @@ function isLoggedIn() | |||
334 | } | 337 | } |
335 | 338 | ||
336 | // Force logout. | 339 | // Force logout. |
337 | function logout() { if (isset($_SESSION)) { unset($_SESSION['uid']); unset($_SESSION['ip']); unset($_SESSION['username']); unset($_SESSION['privateonly']); } } | 340 | function logout() { if (isset($_SESSION)) { unset($_SESSION['uid']); unset($_SESSION['ip']); unset($_SESSION['username']); unset($_SESSION['privateonly']); } |
341 | setcookie('shaarli_staySignedIn', FALSE, 0, WEB_PATH); | ||
342 | } | ||
338 | 343 | ||
339 | 344 | ||
340 | // ------------------------------------------------------------------------------------------ | 345 | // ------------------------------------------------------------------------------------------ |
@@ -391,17 +396,18 @@ if (isset($_POST['login'])) | |||
391 | { | 396 | { |
392 | if (!ban_canLogin()) die('I said: NO. You are banned for the moment. Go away.'); | 397 | if (!ban_canLogin()) die('I said: NO. You are banned for the moment. Go away.'); |
393 | if (isset($_POST['password']) && tokenOk($_POST['token']) && (check_auth($_POST['login'], $_POST['password']))) | 398 | if (isset($_POST['password']) && tokenOk($_POST['token']) && (check_auth($_POST['login'], $_POST['password']))) |
394 | { // Login/password is ok. | 399 | { // Login/password is OK. |
395 | ban_loginOk(); | 400 | ban_loginOk(); |
396 | // If user wants to keep the session cookie even after the browser closes: | 401 | // If user wants to keep the session cookie even after the browser closes: |
397 | if (!empty($_POST['longlastingsession'])) | 402 | if (!empty($_POST['longlastingsession'])) |
398 | { | 403 | { |
404 | setcookie('shaarli_staySignedIn', STAY_SIGNED_IN_TOKEN, time()+31536000, WEB_PATH); | ||
399 | $_SESSION['longlastingsession']=31536000; // (31536000 seconds = 1 year) | 405 | $_SESSION['longlastingsession']=31536000; // (31536000 seconds = 1 year) |
400 | $_SESSION['expires_on']=time()+$_SESSION['longlastingsession']; // Set session expiration on server-side. | 406 | $_SESSION['expires_on']=time()+$_SESSION['longlastingsession']; // Set session expiration on server-side. |
401 | 407 | ||
402 | $cookiedir = ''; if(dirname($_SERVER['SCRIPT_NAME'])!='/') $cookiedir=dirname($_SERVER["SCRIPT_NAME"]).'/'; | 408 | $cookiedir = ''; if(dirname($_SERVER['SCRIPT_NAME'])!='/') $cookiedir=dirname($_SERVER["SCRIPT_NAME"]).'/'; |
403 | session_set_cookie_params($_SESSION['longlastingsession'],$cookiedir,$_SERVER['HTTP_HOST']); // Set session cookie expiration on client side | 409 | session_set_cookie_params($_SESSION['longlastingsession'],$cookiedir,$_SERVER['HTTP_HOST']); // Set session cookie expiration on client side |
404 | // Note: Never forget the trailing slash on the cookie path ! | 410 | // Note: Never forget the trailing slash on the cookie path! |
405 | session_regenerate_id(true); // Send cookie with new expiration date to browser. | 411 | session_regenerate_id(true); // Send cookie with new expiration date to browser. |
406 | } | 412 | } |
407 | else // Standard session expiration (=when browser closes) | 413 | else // Standard session expiration (=when browser closes) |
@@ -411,7 +417,7 @@ if (isset($_POST['login'])) | |||
411 | session_regenerate_id(true); | 417 | session_regenerate_id(true); |
412 | } | 418 | } |
413 | // Optional redirect after login: | 419 | // Optional redirect after login: |
414 | if (isset($_GET['post'])) { header('Location: ?post='.urlencode($_GET['post']).(!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):'')); exit; } | 420 | if (isset($_GET['post'])) { header('Location: ?post='.urlencode($_GET['post']).(!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').(!empty($_GET['description'])?'&description='.urlencode($_GET['description']):'').(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):'')); exit; } |
415 | if (isset($_POST['returnurl'])) | 421 | if (isset($_POST['returnurl'])) |
416 | { | 422 | { |
417 | if (endsWith($_POST['returnurl'],'?do=login')) { header('Location: ?'); exit; } // Prevent loops over login screen. | 423 | if (endsWith($_POST['returnurl'],'?do=login')) { header('Location: ?'); exit; } // Prevent loops over login screen. |
@@ -423,7 +429,7 @@ if (isset($_POST['login'])) | |||
423 | { | 429 | { |
424 | ban_loginFailed(); | 430 | ban_loginFailed(); |
425 | $redir = ''; | 431 | $redir = ''; |
426 | if (isset($_GET['post'])) { $redir = '&post='.urlencode($_GET['post']).(!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):''); } | 432 | if (isset($_GET['post'])) { $redir = '&post='.urlencode($_GET['post']).(!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').(!empty($_GET['description'])?'&description='.urlencode($_GET['description']):'').(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):''); } |
427 | echo '<script language="JavaScript">alert("Wrong login/password.");document.location=\'?do=login'.$redir.'\';</script>'; // Redirect to login screen. | 433 | echo '<script language="JavaScript">alert("Wrong login/password.");document.location=\'?do=login'.$redir.'\';</script>'; // Redirect to login screen. |
428 | exit; | 434 | exit; |
429 | } | 435 | } |
@@ -433,7 +439,7 @@ if (isset($_POST['login'])) | |||
433 | // Misc utility functions: | 439 | // Misc utility functions: |
434 | 440 | ||
435 | // Returns the server URL (including port and http/https), without path. | 441 | // Returns the server URL (including port and http/https), without path. |
436 | // eg. "http://myserver.com:8080" | 442 | // e.g. "http://myserver.com:8080" |
437 | // You can append $_SERVER['SCRIPT_NAME'] to get the current script URL. | 443 | // You can append $_SERVER['SCRIPT_NAME'] to get the current script URL. |
438 | function serverUrl() | 444 | function serverUrl() |
439 | { | 445 | { |
@@ -443,24 +449,24 @@ function serverUrl() | |||
443 | } | 449 | } |
444 | 450 | ||
445 | // Returns the absolute URL of current script, without the query. | 451 | // Returns the absolute URL of current script, without the query. |
446 | // (eg. http://sebsauvage.net/links/) | 452 | // (e.g. http://sebsauvage.net/links/) |
447 | function indexUrl() | 453 | function indexUrl() |
448 | { | 454 | { |
449 | $scriptname = $_SERVER["SCRIPT_NAME"]; | 455 | $scriptname = $_SERVER["SCRIPT_NAME"]; |
450 | // If the script is named 'index.php', we remove it (for better looking URLs, | 456 | // If the script is named 'index.php', we remove it (for better looking URLs, |
451 | // eg. http://mysite.com/shaarli/?abcde instead of http://mysite.com/shaarli/index.php?abcde) | 457 | // e.g. http://mysite.com/shaarli/?abcde instead of http://mysite.com/shaarli/index.php?abcde) |
452 | if (endswith($scriptname,'index.php')) $scriptname = substr($scriptname,0,strlen($scriptname)-9); | 458 | if (endswith($scriptname,'index.php')) $scriptname = substr($scriptname,0,strlen($scriptname)-9); |
453 | return serverUrl() . $scriptname; | 459 | return serverUrl() . $scriptname; |
454 | } | 460 | } |
455 | 461 | ||
456 | // Returns the absolute URL of current script, WITH the query. | 462 | // Returns the absolute URL of current script, WITH the query. |
457 | // (eg. http://sebsauvage.net/links/?toto=titi&spamspamspam=humbug) | 463 | // (e.g. http://sebsauvage.net/links/?toto=titi&spamspamspam=humbug) |
458 | function pageUrl() | 464 | function pageUrl() |
459 | { | 465 | { |
460 | return indexUrl().(!empty($_SERVER["QUERY_STRING"]) ? '?'.$_SERVER["QUERY_STRING"] : ''); | 466 | return indexUrl().(!empty($_SERVER["QUERY_STRING"]) ? '?'.$_SERVER["QUERY_STRING"] : ''); |
461 | } | 467 | } |
462 | 468 | ||
463 | // Convert post_max_size/upload_max_filesize (eg.'16M') parameters to bytes. | 469 | // Convert post_max_size/upload_max_filesize (e.g. '16M') parameters to bytes. |
464 | function return_bytes($val) | 470 | function return_bytes($val) |
465 | { | 471 | { |
466 | $val = trim($val); $last=strtolower($val[strlen($val)-1]); | 472 | $val = trim($val); $last=strtolower($val[strlen($val)-1]); |
@@ -481,7 +487,7 @@ function getMaxFileSize() | |||
481 | $size2 = return_bytes(ini_get('upload_max_filesize')); | 487 | $size2 = return_bytes(ini_get('upload_max_filesize')); |
482 | // Return the smaller of two: | 488 | // Return the smaller of two: |
483 | $maxsize = min($size1,$size2); | 489 | $maxsize = min($size1,$size2); |
484 | // FIXME: Then convert back to readable notations ? (eg. 2M instead of 2000000) | 490 | // FIXME: Then convert back to readable notations ? (e.g. 2M instead of 2000000) |
485 | return $maxsize; | 491 | return $maxsize; |
486 | } | 492 | } |
487 | 493 | ||
@@ -529,7 +535,7 @@ function linkdate2iso8601($linkdate) | |||
529 | function linkdate2locale($linkdate) | 535 | function linkdate2locale($linkdate) |
530 | { | 536 | { |
531 | return utf8_encode(strftime('%c',linkdate2timestamp($linkdate))); // %c is for automatic date format according to locale. | 537 | return utf8_encode(strftime('%c',linkdate2timestamp($linkdate))); // %c is for automatic date format according to locale. |
532 | // Note that if you use a local which is not installed on your webserver, | 538 | // Note that if you use a locale which is not installed on your webserver, |
533 | // the date will not be displayed in the chosen locale, but probably in US notation. | 539 | // the date will not be displayed in the chosen locale, but probably in US notation. |
534 | } | 540 | } |
535 | 541 | ||
@@ -551,10 +557,10 @@ function http_parse_headers_shaarli( $headers ) | |||
551 | } | 557 | } |
552 | 558 | ||
553 | /* GET an URL. | 559 | /* GET an URL. |
554 | Input: $url : url to get (http://...) | 560 | Input: $url : URL to get (http://...) |
555 | $timeout : Network timeout (will wait this many seconds for an anwser before giving up). | 561 | $timeout : Network timeout (will wait this many seconds for an anwser before giving up). |
556 | Output: An array. [0] = HTTP status message (eg. "HTTP/1.1 200 OK") or error message | 562 | Output: An array. [0] = HTTP status message (e.g. "HTTP/1.1 200 OK") or error message |
557 | [1] = associative array containing HTTP response headers (eg. echo getHTTP($url)[1]['Content-Type']) | 563 | [1] = associative array containing HTTP response headers (e.g. echo getHTTP($url)[1]['Content-Type']) |
558 | [2] = data | 564 | [2] = data |
559 | Example: list($httpstatus,$headers,$data) = getHTTP('http://sebauvage.net/'); | 565 | Example: list($httpstatus,$headers,$data) = getHTTP('http://sebauvage.net/'); |
560 | if (strpos($httpstatus,'200 OK')!==false) | 566 | if (strpos($httpstatus,'200 OK')!==false) |
@@ -570,11 +576,11 @@ function getHTTP($url,$timeout=30) | |||
570 | $context = stream_context_create($options); | 576 | $context = stream_context_create($options); |
571 | $data=file_get_contents($url,false,$context,-1, 4000000); // We download at most 4 Mb from source. | 577 | $data=file_get_contents($url,false,$context,-1, 4000000); // We download at most 4 Mb from source. |
572 | if (!$data) { return array('HTTP Error',array(),''); } | 578 | if (!$data) { return array('HTTP Error',array(),''); } |
573 | $httpStatus=$http_response_header[0]; // eg. "HTTP/1.1 200 OK" | 579 | $httpStatus=$http_response_header[0]; // e.g. "HTTP/1.1 200 OK" |
574 | $responseHeaders=http_parse_headers_shaarli($http_response_header); | 580 | $responseHeaders=http_parse_headers_shaarli($http_response_header); |
575 | return array($httpStatus,$responseHeaders,$data); | 581 | return array($httpStatus,$responseHeaders,$data); |
576 | } | 582 | } |
577 | catch (Exception $e) // getHTTP *can* fail silentely (we don't care if the title cannot be fetched) | 583 | catch (Exception $e) // getHTTP *can* fail silently (we don't care if the title cannot be fetched) |
578 | { | 584 | { |
579 | return array($e->getMessage(),'',''); | 585 | return array($e->getMessage(),'',''); |
580 | } | 586 | } |
@@ -600,14 +606,14 @@ function getToken() | |||
600 | return $rnd; | 606 | return $rnd; |
601 | } | 607 | } |
602 | 608 | ||
603 | // Tells if a token is ok. Using this function will destroy the token. | 609 | // Tells if a token is OK. Using this function will destroy the token. |
604 | // true=token is ok. | 610 | // true=token is OK. |
605 | function tokenOk($token) | 611 | function tokenOk($token) |
606 | { | 612 | { |
607 | if (isset($_SESSION['tokens'][$token])) | 613 | if (isset($_SESSION['tokens'][$token])) |
608 | { | 614 | { |
609 | unset($_SESSION['tokens'][$token]); // Token is used: destroy it. | 615 | unset($_SESSION['tokens'][$token]); // Token is used: destroy it. |
610 | return true; // Token is ok. | 616 | return true; // Token is OK. |
611 | } | 617 | } |
612 | return false; // Wrong token, or already used. | 618 | return false; // Wrong token, or already used. |
613 | } | 619 | } |
@@ -642,8 +648,9 @@ class pageBuilder | |||
642 | $this->tpl->assign('version',shaarli_version); | 648 | $this->tpl->assign('version',shaarli_version); |
643 | $this->tpl->assign('scripturl',indexUrl()); | 649 | $this->tpl->assign('scripturl',indexUrl()); |
644 | $this->tpl->assign('pagetitle','Shaarli'); | 650 | $this->tpl->assign('pagetitle','Shaarli'); |
645 | $this->tpl->assign('privateonly',!empty($_SESSION['privateonly'])); // Show only private links ? | 651 | $this->tpl->assign('privateonly',!empty($_SESSION['privateonly'])); // Show only private links? |
646 | if (!empty($GLOBALS['title'])) $this->tpl->assign('pagetitle',$GLOBALS['title']); | 652 | if (!empty($GLOBALS['title'])) $this->tpl->assign('pagetitle',$GLOBALS['title']); |
653 | if (!empty($GLOBALS['titleLink'])) $this->tpl->assign('titleLink',$GLOBALS['titleLink']); | ||
647 | if (!empty($GLOBALS['pagetitle'])) $this->tpl->assign('pagetitle',$GLOBALS['pagetitle']); | 654 | if (!empty($GLOBALS['pagetitle'])) $this->tpl->assign('pagetitle',$GLOBALS['pagetitle']); |
648 | $this->tpl->assign('shaarlititle',empty($GLOBALS['title']) ? 'Shaarli': $GLOBALS['title'] ); | 655 | $this->tpl->assign('shaarlititle',empty($GLOBALS['title']) ? 'Shaarli': $GLOBALS['title'] ); |
649 | return; | 656 | return; |
@@ -657,7 +664,7 @@ class pageBuilder | |||
657 | } | 664 | } |
658 | 665 | ||
659 | // Render a specific page (using a template). | 666 | // Render a specific page (using a template). |
660 | // eg. pb.renderPage('picwall') | 667 | // e.g. pb.renderPage('picwall') |
661 | public function renderPage($page) | 668 | public function renderPage($page) |
662 | { | 669 | { |
663 | if ($this->tpl===false) $this->initialize(); // Lazy initialization | 670 | if ($this->tpl===false) $this->initialize(); // Lazy initialization |
@@ -676,10 +683,10 @@ class pageBuilder | |||
676 | 683 | ||
677 | Available keys: | 684 | Available keys: |
678 | title : Title of the link | 685 | title : Title of the link |
679 | url : URL of the link. Can be absolute or relative. Relative URLs are permalinks (eg.'?m-ukcw') | 686 | url : URL of the link. Can be absolute or relative. Relative URLs are permalinks (e.g.'?m-ukcw') |
680 | description : description of the entry | 687 | description : description of the entry |
681 | private : Is this link private ? 0=no, other value=yes | 688 | private : Is this link private? 0=no, other value=yes |
682 | linkdate : date of the creation of this entry, in the form YYYYMMDD_HHMMSS (eg.'20110914_192317') | 689 | linkdate : date of the creation of this entry, in the form YYYYMMDD_HHMMSS (e.g.'20110914_192317') |
683 | tags : tags attached to this entry (separated by spaces) | 690 | tags : tags attached to this entry (separated by spaces) |
684 | 691 | ||
685 | We implement 3 interfaces: | 692 | We implement 3 interfaces: |
@@ -689,15 +696,15 @@ class pageBuilder | |||
689 | */ | 696 | */ |
690 | class linkdb implements Iterator, Countable, ArrayAccess | 697 | class linkdb implements Iterator, Countable, ArrayAccess |
691 | { | 698 | { |
692 | private $links; // List of links (associative array. Key=linkdate (eg. "20110823_124546"), value= associative array (keys:title,description...) | 699 | private $links; // List of links (associative array. Key=linkdate (e.g. "20110823_124546"), value= associative array (keys:title,description...) |
693 | private $urls; // List of all recorded URLs (key=url, value=linkdate) for fast reserve search (url-->linkdate) | 700 | private $urls; // List of all recorded URLs (key=url, value=linkdate) for fast reserve search (url-->linkdate) |
694 | private $keys; // List of linkdate keys (for the Iterator interface implementation) | 701 | private $keys; // List of linkdate keys (for the Iterator interface implementation) |
695 | private $position; // Position in the $this->keys array. (for the Iterator interface implementation.) | 702 | private $position; // Position in the $this->keys array. (for the Iterator interface implementation.) |
696 | private $loggedin; // Is the used logged in ? (used to filter private links) | 703 | private $loggedin; // Is the user logged in? (used to filter private links) |
697 | 704 | ||
698 | // Constructor: | 705 | // Constructor: |
699 | function __construct($isLoggedIn) | 706 | function __construct($isLoggedIn) |
700 | // Input : $isLoggedIn : is the used logged in ? | 707 | // Input : $isLoggedIn : is the user logged in? |
701 | { | 708 | { |
702 | $this->loggedin = $isLoggedIn; | 709 | $this->loggedin = $isLoggedIn; |
703 | $this->checkdb(); // Make sure data file exists. | 710 | $this->checkdb(); // Make sure data file exists. |
@@ -711,7 +718,7 @@ class linkdb implements Iterator, Countable, ArrayAccess | |||
711 | public function offsetSet($offset, $value) | 718 | public function offsetSet($offset, $value) |
712 | { | 719 | { |
713 | if (!$this->loggedin) die('You are not authorized to add a link.'); | 720 | if (!$this->loggedin) die('You are not authorized to add a link.'); |
714 | if (empty($value['linkdate']) || empty($value['url'])) die('Internal Error: A link should always have a linkdate and url.'); | 721 | if (empty($value['linkdate']) || empty($value['url'])) die('Internal Error: A link should always have a linkdate and URL.'); |
715 | if (empty($offset)) die('You must specify a key.'); | 722 | if (empty($offset)) die('You must specify a key.'); |
716 | $this->links[$offset] = $value; | 723 | $this->links[$offset] = $value; |
717 | $this->urls[$value['url']]=$offset; | 724 | $this->urls[$value['url']]=$offset; |
@@ -774,19 +781,19 @@ class linkdb implements Iterator, Countable, ArrayAccess | |||
774 | invalidateCaches(); | 781 | invalidateCaches(); |
775 | } | 782 | } |
776 | 783 | ||
777 | // Returns the link for a given URL (if it exists). false it does not exist. | 784 | // Returns the link for a given URL (if it exists). False if it does not exist. |
778 | public function getLinkFromUrl($url) | 785 | public function getLinkFromUrl($url) |
779 | { | 786 | { |
780 | if (isset($this->urls[$url])) return $this->links[$this->urls[$url]]; | 787 | if (isset($this->urls[$url])) return $this->links[$this->urls[$url]]; |
781 | return false; | 788 | return false; |
782 | } | 789 | } |
783 | 790 | ||
784 | // Case insentitive search among links (in url, title and description). Returns filtered list of links. | 791 | // Case insensitive search among links (in the URLs, title and description). Returns filtered list of links. |
785 | // eg. print_r($mydb->filterFulltext('hollandais')); | 792 | // e.g. print_r($mydb->filterFulltext('hollandais')); |
786 | public function filterFulltext($searchterms) | 793 | public function filterFulltext($searchterms) |
787 | { | 794 | { |
788 | // FIXME: explode(' ',$searchterms) and perform a AND search. | 795 | // FIXME: explode(' ',$searchterms) and perform a AND search. |
789 | // FIXME: accept double-quotes to search for a string "as is" ? | 796 | // FIXME: accept double-quotes to search for a string "as is"? |
790 | $filtered=array(); | 797 | $filtered=array(); |
791 | $s = strtolower($searchterms); | 798 | $s = strtolower($searchterms); |
792 | foreach($this->links as $l) | 799 | foreach($this->links as $l) |
@@ -803,7 +810,7 @@ class linkdb implements Iterator, Countable, ArrayAccess | |||
803 | 810 | ||
804 | // Filter by tag. | 811 | // Filter by tag. |
805 | // You can specify one or more tags (tags can be separated by space or comma). | 812 | // You can specify one or more tags (tags can be separated by space or comma). |
806 | // eg. print_r($mydb->filterTags('linux programming')); | 813 | // e.g. print_r($mydb->filterTags('linux programming')); |
807 | public function filterTags($tags,$casesensitive=false) | 814 | public function filterTags($tags,$casesensitive=false) |
808 | { | 815 | { |
809 | $t = str_replace(',',' ',($casesensitive?$tags:strtolower($tags))); | 816 | $t = str_replace(',',' ',($casesensitive?$tags:strtolower($tags))); |
@@ -819,9 +826,9 @@ class linkdb implements Iterator, Countable, ArrayAccess | |||
819 | return $filtered; | 826 | return $filtered; |
820 | } | 827 | } |
821 | 828 | ||
822 | // Filter by day. Day must be in the form 'YYYYMMDD' (eg. '20120125') | 829 | // Filter by day. Day must be in the form 'YYYYMMDD' (e.g. '20120125') |
823 | // Sort order is: older articles first. | 830 | // Sort order is: older articles first. |
824 | // eg. print_r($mydb->filterDay('20120125')); | 831 | // e.g. print_r($mydb->filterDay('20120125')); |
825 | public function filterDay($day) | 832 | public function filterDay($day) |
826 | { | 833 | { |
827 | $filtered=array(); | 834 | $filtered=array(); |
@@ -876,13 +883,13 @@ class linkdb implements Iterator, Countable, ArrayAccess | |||
876 | } | 883 | } |
877 | 884 | ||
878 | // ------------------------------------------------------------------------------------------ | 885 | // ------------------------------------------------------------------------------------------ |
879 | // Ouput the last N links in RSS 2.0 format. | 886 | // Output the last N links in RSS 2.0 format. |
880 | function showRSS() | 887 | function showRSS() |
881 | { | 888 | { |
882 | header('Content-Type: application/rss+xml; charset=utf-8'); | 889 | header('Content-Type: application/rss+xml; charset=utf-8'); |
883 | 890 | ||
884 | // $usepermalink : If true, use permalink instead of final link. | 891 | // $usepermalink : If true, use permalink instead of final link. |
885 | // User just has to add 'permalink' in URL parameters. eg. http://mysite.com/shaarli/?do=rss&permalinks | 892 | // User just has to add 'permalink' in URL parameters. e.g. http://mysite.com/shaarli/?do=rss&permalinks |
886 | $usepermalinks = isset($_GET['permalinks']); | 893 | $usepermalinks = isset($_GET['permalinks']); |
887 | 894 | ||
888 | // Cache system | 895 | // Cache system |
@@ -891,9 +898,9 @@ function showRSS() | |||
891 | $cached = $cache->cachedVersion(); if (!empty($cached)) { echo $cached; exit; } | 898 | $cached = $cache->cachedVersion(); if (!empty($cached)) { echo $cached; exit; } |
892 | 899 | ||
893 | // If cached was not found (or not usable), then read the database and build the response: | 900 | // If cached was not found (or not usable), then read the database and build the response: |
894 | $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in). | 901 | $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if user it not logged in). |
895 | 902 | ||
896 | // Optionnaly filter the results: | 903 | // Optionally filter the results: |
897 | $linksToDisplay=array(); | 904 | $linksToDisplay=array(); |
898 | if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']); | 905 | if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']); |
899 | elseif (!empty($_GET['searchtags'])) $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags'])); | 906 | elseif (!empty($_GET['searchtags'])) $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags'])); |
@@ -942,7 +949,7 @@ function showRSS() | |||
942 | echo '<description><![CDATA['.nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description'])))).$descriptionlink.']]></description>'."\n</item>\n"; | 949 | echo '<description><![CDATA['.nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description'])))).$descriptionlink.']]></description>'."\n</item>\n"; |
943 | $i++; | 950 | $i++; |
944 | } | 951 | } |
945 | echo '</channel></rss><!-- Cached version of '.pageUrl().' -->'; | 952 | echo '</channel></rss><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->'; |
946 | 953 | ||
947 | $cache->cache(ob_get_contents()); | 954 | $cache->cache(ob_get_contents()); |
948 | ob_end_flush(); | 955 | ob_end_flush(); |
@@ -950,13 +957,13 @@ function showRSS() | |||
950 | } | 957 | } |
951 | 958 | ||
952 | // ------------------------------------------------------------------------------------------ | 959 | // ------------------------------------------------------------------------------------------ |
953 | // Ouput the last N links in ATOM format. | 960 | // Output the last N links in ATOM format. |
954 | function showATOM() | 961 | function showATOM() |
955 | { | 962 | { |
956 | header('Content-Type: application/atom+xml; charset=utf-8'); | 963 | header('Content-Type: application/atom+xml; charset=utf-8'); |
957 | 964 | ||
958 | // $usepermalink : If true, use permalink instead of final link. | 965 | // $usepermalink : If true, use permalink instead of final link. |
959 | // User just has to add 'permalink' in URL parameters. eg. http://mysite.com/shaarli/?do=atom&permalinks | 966 | // User just has to add 'permalink' in URL parameters. e.g. http://mysite.com/shaarli/?do=atom&permalinks |
960 | $usepermalinks = isset($_GET['permalinks']); | 967 | $usepermalinks = isset($_GET['permalinks']); |
961 | 968 | ||
962 | // Cache system | 969 | // Cache system |
@@ -968,7 +975,7 @@ function showATOM() | |||
968 | $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in). | 975 | $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in). |
969 | 976 | ||
970 | 977 | ||
971 | // Optionnaly filter the results: | 978 | // Optionally filter the results: |
972 | $linksToDisplay=array(); | 979 | $linksToDisplay=array(); |
973 | if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']); | 980 | if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']); |
974 | elseif (!empty($_GET['searchtags'])) $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags'])); | 981 | elseif (!empty($_GET['searchtags'])) $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags'])); |
@@ -1027,7 +1034,7 @@ function showATOM() | |||
1027 | $feed.='<author><name>'.htmlspecialchars($pageaddr).'</name><uri>'.htmlspecialchars($pageaddr).'</uri></author>'; | 1034 | $feed.='<author><name>'.htmlspecialchars($pageaddr).'</name><uri>'.htmlspecialchars($pageaddr).'</uri></author>'; |
1028 | $feed.='<id>'.htmlspecialchars($pageaddr).'</id>'."\n\n"; // Yes, I know I should use a real IRI (RFC3987), but the site URL will do. | 1035 | $feed.='<id>'.htmlspecialchars($pageaddr).'</id>'."\n\n"; // Yes, I know I should use a real IRI (RFC3987), but the site URL will do. |
1029 | $feed.=$entries; | 1036 | $feed.=$entries; |
1030 | $feed.='</feed><!-- Cached version of '.pageUrl().' -->'; | 1037 | $feed.='</feed><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->'; |
1031 | echo $feed; | 1038 | echo $feed; |
1032 | 1039 | ||
1033 | $cache->cache(ob_get_contents()); | 1040 | $cache->cache(ob_get_contents()); |
@@ -1064,7 +1071,7 @@ function showDailyRSS() | |||
1064 | if (empty($days[$day])) $days[$day]=array(); | 1071 | if (empty($days[$day])) $days[$day]=array(); |
1065 | $days[$day][]=$linkdate; | 1072 | $days[$day][]=$linkdate; |
1066 | } | 1073 | } |
1067 | if (count($days)>$nb_of_days) break; // Have we collected enough days ? | 1074 | if (count($days)>$nb_of_days) break; // Have we collected enough days? |
1068 | } | 1075 | } |
1069 | 1076 | ||
1070 | // Build the RSS feed. | 1077 | // Build the RSS feed. |
@@ -1104,7 +1111,7 @@ function showDailyRSS() | |||
1104 | echo '<description><![CDATA['.$html.']]></description>'."\n</item>\n\n"; | 1111 | echo '<description><![CDATA['.$html.']]></description>'."\n</item>\n\n"; |
1105 | 1112 | ||
1106 | } | 1113 | } |
1107 | echo '</channel></rss><!-- Cached version of '.pageUrl().' -->'; | 1114 | echo '</channel></rss><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->'; |
1108 | 1115 | ||
1109 | $cache->cache(ob_get_contents()); | 1116 | $cache->cache(ob_get_contents()); |
1110 | ob_end_flush(); | 1117 | ob_end_flush(); |
@@ -1143,7 +1150,7 @@ function showDaily() | |||
1143 | } | 1150 | } |
1144 | 1151 | ||
1145 | /* We need to spread the articles on 3 columns. | 1152 | /* We need to spread the articles on 3 columns. |
1146 | I did not want to use a javascript lib like http://masonry.desandro.com/ | 1153 | I did not want to use a JavaScript lib like http://masonry.desandro.com/ |
1147 | so I manually spread entries with a simple method: I roughly evaluate the | 1154 | so I manually spread entries with a simple method: I roughly evaluate the |
1148 | height of a div according to title and description length. | 1155 | height of a div according to title and description length. |
1149 | */ | 1156 | */ |
@@ -1154,7 +1161,7 @@ function showDaily() | |||
1154 | // Roughly estimate length of entry (by counting characters) | 1161 | // Roughly estimate length of entry (by counting characters) |
1155 | // Title: 30 chars = 1 line. 1 line is 30 pixels height. | 1162 | // Title: 30 chars = 1 line. 1 line is 30 pixels height. |
1156 | // Description: 836 characters gives roughly 342 pixel height. | 1163 | // Description: 836 characters gives roughly 342 pixel height. |
1157 | // This is not perfect, but it's usually ok. | 1164 | // This is not perfect, but it's usually OK. |
1158 | $length=strlen($link['title'])+(342*strlen($link['description']))/836; | 1165 | $length=strlen($link['title'])+(342*strlen($link['description']))/836; |
1159 | if ($link['thumbnail']) $length +=100; // 1 thumbnails roughly takes 100 pixels height. | 1166 | if ($link['thumbnail']) $length +=100; // 1 thumbnails roughly takes 100 pixels height. |
1160 | // Then put in column which is the less filled: | 1167 | // Then put in column which is the less filled: |
@@ -1207,7 +1214,7 @@ function renderPage() | |||
1207 | // -------- Picture wall | 1214 | // -------- Picture wall |
1208 | if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=picwall')) | 1215 | if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=picwall')) |
1209 | { | 1216 | { |
1210 | // Optionnaly filter the results: | 1217 | // Optionally filter the results: |
1211 | $links=array(); | 1218 | $links=array(); |
1212 | if (!empty($_GET['searchterm'])) $links = $LINKSDB->filterFulltext($_GET['searchterm']); | 1219 | if (!empty($_GET['searchterm'])) $links = $LINKSDB->filterFulltext($_GET['searchterm']); |
1213 | elseif (!empty($_GET['searchtags'])) $links = $LINKSDB->filterTags(trim($_GET['searchtags'])); | 1220 | elseif (!empty($_GET['searchtags'])) $links = $LINKSDB->filterTags(trim($_GET['searchtags'])); |
@@ -1287,7 +1294,7 @@ function renderPage() | |||
1287 | if (isset($_GET['linksperpage'])) | 1294 | if (isset($_GET['linksperpage'])) |
1288 | { | 1295 | { |
1289 | if (is_numeric($_GET['linksperpage'])) { $_SESSION['LINKS_PER_PAGE']=abs(intval($_GET['linksperpage'])); } | 1296 | if (is_numeric($_GET['linksperpage'])) { $_SESSION['LINKS_PER_PAGE']=abs(intval($_GET['linksperpage'])); } |
1290 | // Make sure the referer is from Shaarli itself. | 1297 | // Make sure the referrer is Shaarli itself. |
1291 | $referer = '?'; | 1298 | $referer = '?'; |
1292 | if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['HTTP_HOST'])==0) | 1299 | if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['HTTP_HOST'])==0) |
1293 | $referer = $_SERVER['HTTP_REFERER']; | 1300 | $referer = $_SERVER['HTTP_REFERER']; |
@@ -1306,7 +1313,7 @@ function renderPage() | |||
1306 | { | 1313 | { |
1307 | unset($_SESSION['privateonly']); // See all links | 1314 | unset($_SESSION['privateonly']); // See all links |
1308 | } | 1315 | } |
1309 | // Make sure the referer is from Shaarli itself. | 1316 | // Make sure the referrer is Shaarli itself. |
1310 | $referer = '?'; | 1317 | $referer = '?'; |
1311 | if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['HTTP_HOST'])==0) | 1318 | if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['HTTP_HOST'])==0) |
1312 | $referer = $_SERVER['HTTP_REFERER']; | 1319 | $referer = $_SERVER['HTTP_REFERER']; |
@@ -1317,17 +1324,17 @@ function renderPage() | |||
1317 | // -------- Handle other actions allowed for non-logged in users: | 1324 | // -------- Handle other actions allowed for non-logged in users: |
1318 | if (!isLoggedIn()) | 1325 | if (!isLoggedIn()) |
1319 | { | 1326 | { |
1320 | // User tries to post new link but is not loggedin: | 1327 | // User tries to post new link but is not logged in: |
1321 | // Show login screen, then redirect to ?post=... | 1328 | // Show login screen, then redirect to ?post=... |
1322 | if (isset($_GET['post'])) | 1329 | if (isset($_GET['post'])) |
1323 | { | 1330 | { |
1324 | header('Location: ?do=login&post='.urlencode($_GET['post']).(!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):'')); // Redirect to login page, then back to post link. | 1331 | header('Location: ?do=login&post='.urlencode($_GET['post']).(!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').(!empty($_GET['description'])?'&description='.urlencode($_GET['description']):'').(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):'')); // Redirect to login page, then back to post link. |
1325 | exit; | 1332 | exit; |
1326 | } | 1333 | } |
1327 | $PAGE = new pageBuilder; | 1334 | $PAGE = new pageBuilder; |
1328 | buildLinkList($PAGE,$LINKSDB); // Compute list of links to display | 1335 | buildLinkList($PAGE,$LINKSDB); // Compute list of links to display |
1329 | $PAGE->renderPage('linklist'); | 1336 | $PAGE->renderPage('linklist'); |
1330 | exit; // Never remove this one ! All operations below are reserved for logged in user. | 1337 | exit; // Never remove this one! All operations below are reserved for logged in user. |
1331 | } | 1338 | } |
1332 | 1339 | ||
1333 | // -------- All other functions are reserved for the registered user: | 1340 | // -------- All other functions are reserved for the registered user: |
@@ -1348,7 +1355,7 @@ function renderPage() | |||
1348 | if ($GLOBALS['config']['OPEN_SHAARLI']) die('You are not supposed to change a password on an Open Shaarli.'); | 1355 | if ($GLOBALS['config']['OPEN_SHAARLI']) die('You are not supposed to change a password on an Open Shaarli.'); |
1349 | if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword'])) | 1356 | if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword'])) |
1350 | { | 1357 | { |
1351 | if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away ! | 1358 | if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away! |
1352 | 1359 | ||
1353 | // Make sure old password is correct. | 1360 | // Make sure old password is correct. |
1354 | $oldhash = sha1($_POST['oldpassword'].$GLOBALS['login'].$GLOBALS['salt']); | 1361 | $oldhash = sha1($_POST['oldpassword'].$GLOBALS['login'].$GLOBALS['salt']); |
@@ -1375,13 +1382,14 @@ function renderPage() | |||
1375 | { | 1382 | { |
1376 | if (!empty($_POST['title']) ) | 1383 | if (!empty($_POST['title']) ) |
1377 | { | 1384 | { |
1378 | if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away ! | 1385 | if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away! |
1379 | $tz = 'UTC'; | 1386 | $tz = 'UTC'; |
1380 | if (!empty($_POST['continent']) && !empty($_POST['city'])) | 1387 | if (!empty($_POST['continent']) && !empty($_POST['city'])) |
1381 | if (isTZvalid($_POST['continent'],$_POST['city'])) | 1388 | if (isTZvalid($_POST['continent'],$_POST['city'])) |
1382 | $tz = $_POST['continent'].'/'.$_POST['city']; | 1389 | $tz = $_POST['continent'].'/'.$_POST['city']; |
1383 | $GLOBALS['timezone'] = $tz; | 1390 | $GLOBALS['timezone'] = $tz; |
1384 | $GLOBALS['title']=$_POST['title']; | 1391 | $GLOBALS['title']=$_POST['title']; |
1392 | $GLOBALS['titleLink']=$_POST['titleLink']; | ||
1385 | $GLOBALS['redirector']=$_POST['redirector']; | 1393 | $GLOBALS['redirector']=$_POST['redirector']; |
1386 | $GLOBALS['disablesessionprotection']=!empty($_POST['disablesessionprotection']); | 1394 | $GLOBALS['disablesessionprotection']=!empty($_POST['disablesessionprotection']); |
1387 | $GLOBALS['disablejquery']=!empty($_POST['disablejquery']); | 1395 | $GLOBALS['disablejquery']=!empty($_POST['disablejquery']); |
@@ -1398,7 +1406,7 @@ function renderPage() | |||
1398 | $PAGE->assign('title',htmlspecialchars( empty($GLOBALS['title']) ? '' : $GLOBALS['title'] , ENT_QUOTES)); | 1406 | $PAGE->assign('title',htmlspecialchars( empty($GLOBALS['title']) ? '' : $GLOBALS['title'] , ENT_QUOTES)); |
1399 | $PAGE->assign('redirector',htmlspecialchars( empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'] , ENT_QUOTES)); | 1407 | $PAGE->assign('redirector',htmlspecialchars( empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'] , ENT_QUOTES)); |
1400 | list($timezone_form,$timezone_js) = templateTZform($GLOBALS['timezone']); | 1408 | list($timezone_form,$timezone_js) = templateTZform($GLOBALS['timezone']); |
1401 | $PAGE->assign('timezone_form',$timezone_form); // FIXME: put entire tz form generation in template ? | 1409 | $PAGE->assign('timezone_form',$timezone_form); // FIXME: Put entire tz form generation in template? |
1402 | $PAGE->assign('timezone_js',$timezone_js); | 1410 | $PAGE->assign('timezone_js',$timezone_js); |
1403 | $PAGE->renderPage('configure'); | 1411 | $PAGE->renderPage('configure'); |
1404 | exit; | 1412 | exit; |
@@ -1422,7 +1430,7 @@ function renderPage() | |||
1422 | if (!empty($_POST['deletetag']) && !empty($_POST['fromtag'])) | 1430 | if (!empty($_POST['deletetag']) && !empty($_POST['fromtag'])) |
1423 | { | 1431 | { |
1424 | $needle=trim($_POST['fromtag']); | 1432 | $needle=trim($_POST['fromtag']); |
1425 | $linksToAlter = $LINKSDB->filterTags($needle,true); // true for case-sensitive tag search. | 1433 | $linksToAlter = $LINKSDB->filterTags($needle,true); // True for case-sensitive tag search. |
1426 | foreach($linksToAlter as $key=>$value) | 1434 | foreach($linksToAlter as $key=>$value) |
1427 | { | 1435 | { |
1428 | $tags = explode(' ',trim($value['tags'])); | 1436 | $tags = explode(' ',trim($value['tags'])); |
@@ -1430,7 +1438,7 @@ function renderPage() | |||
1430 | $value['tags']=trim(implode(' ',$tags)); | 1438 | $value['tags']=trim(implode(' ',$tags)); |
1431 | $LINKSDB[$key]=$value; | 1439 | $LINKSDB[$key]=$value; |
1432 | } | 1440 | } |
1433 | $LINKSDB->savedb(); // save to disk | 1441 | $LINKSDB->savedb(); // Save to disk. |
1434 | echo '<script language="JavaScript">alert("Tag was removed from '.count($linksToAlter).' links.");document.location=\'?\';</script>'; | 1442 | echo '<script language="JavaScript">alert("Tag was removed from '.count($linksToAlter).' links.");document.location=\'?\';</script>'; |
1435 | exit; | 1443 | exit; |
1436 | } | 1444 | } |
@@ -1443,17 +1451,17 @@ function renderPage() | |||
1443 | foreach($linksToAlter as $key=>$value) | 1451 | foreach($linksToAlter as $key=>$value) |
1444 | { | 1452 | { |
1445 | $tags = explode(' ',trim($value['tags'])); | 1453 | $tags = explode(' ',trim($value['tags'])); |
1446 | $tags[array_search($needle,$tags)] = trim($_POST['totag']); // Remplace tags value. | 1454 | $tags[array_search($needle,$tags)] = trim($_POST['totag']); // Replace tags value. |
1447 | $value['tags']=trim(implode(' ',$tags)); | 1455 | $value['tags']=trim(implode(' ',$tags)); |
1448 | $LINKSDB[$key]=$value; | 1456 | $LINKSDB[$key]=$value; |
1449 | } | 1457 | } |
1450 | $LINKSDB->savedb(); // save to disk | 1458 | $LINKSDB->savedb(); // Save to disk. |
1451 | echo '<script language="JavaScript">alert("Tag was renamed in '.count($linksToAlter).' links.");document.location=\'?searchtags='.urlencode($_POST['totag']).'\';</script>'; | 1459 | echo '<script language="JavaScript">alert("Tag was renamed in '.count($linksToAlter).' links.");document.location=\'?searchtags='.urlencode($_POST['totag']).'\';</script>'; |
1452 | exit; | 1460 | exit; |
1453 | } | 1461 | } |
1454 | } | 1462 | } |
1455 | 1463 | ||
1456 | // -------- User wants to add a link without using the bookmarklet: show form. | 1464 | // -------- User wants to add a link without using the bookmarklet: Show form. |
1457 | if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=addlink')) | 1465 | if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=addlink')) |
1458 | { | 1466 | { |
1459 | $PAGE = new pageBuilder; | 1467 | $PAGE = new pageBuilder; |
@@ -1465,7 +1473,7 @@ function renderPage() | |||
1465 | // -------- User clicked the "Save" button when editing a link: Save link to database. | 1473 | // -------- User clicked the "Save" button when editing a link: Save link to database. |
1466 | if (isset($_POST['save_edit'])) | 1474 | if (isset($_POST['save_edit'])) |
1467 | { | 1475 | { |
1468 | if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away ! | 1476 | if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away! |
1469 | $tags = trim(preg_replace('/\s\s+/',' ', $_POST['lf_tags'])); // Remove multiple spaces. | 1477 | $tags = trim(preg_replace('/\s\s+/',' ', $_POST['lf_tags'])); // Remove multiple spaces. |
1470 | $linkdate=$_POST['lf_linkdate']; | 1478 | $linkdate=$_POST['lf_linkdate']; |
1471 | $url = trim($_POST['lf_url']); | 1479 | $url = trim($_POST['lf_url']); |
@@ -1475,7 +1483,7 @@ function renderPage() | |||
1475 | 'linkdate'=>$linkdate,'tags'=>str_replace(',',' ',$tags)); | 1483 | 'linkdate'=>$linkdate,'tags'=>str_replace(',',' ',$tags)); |
1476 | if ($link['title']=='') $link['title']=$link['url']; // If title is empty, use the URL as title. | 1484 | if ($link['title']=='') $link['title']=$link['url']; // If title is empty, use the URL as title. |
1477 | $LINKSDB[$linkdate] = $link; | 1485 | $LINKSDB[$linkdate] = $link; |
1478 | $LINKSDB->savedb(); // save to disk | 1486 | $LINKSDB->savedb(); // Save to disk. |
1479 | pubsubhub(); | 1487 | pubsubhub(); |
1480 | 1488 | ||
1481 | // If we are called from the bookmarklet, we must close the popup: | 1489 | // If we are called from the bookmarklet, we must close the popup: |
@@ -1489,7 +1497,7 @@ function renderPage() | |||
1489 | // -------- User clicked the "Cancel" button when editing a link. | 1497 | // -------- User clicked the "Cancel" button when editing a link. |
1490 | if (isset($_POST['cancel_edit'])) | 1498 | if (isset($_POST['cancel_edit'])) |
1491 | { | 1499 | { |
1492 | // If we are called from the bookmarklet, we must close the popup; | 1500 | // If we are called from the bookmarklet, we must close the popup: |
1493 | if (isset($_GET['source']) && $_GET['source']=='bookmarklet') { echo '<script language="JavaScript">self.close();</script>'; exit; } | 1501 | if (isset($_GET['source']) && $_GET['source']=='bookmarklet') { echo '<script language="JavaScript">self.close();</script>'; exit; } |
1494 | $returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' ); | 1502 | $returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' ); |
1495 | $returnurl .= '#'.smallHash($_POST['lf_linkdate']); // Scroll to the link which has been edited. | 1503 | $returnurl .= '#'.smallHash($_POST['lf_linkdate']); // Scroll to the link which has been edited. |
@@ -1497,12 +1505,12 @@ function renderPage() | |||
1497 | exit; | 1505 | exit; |
1498 | } | 1506 | } |
1499 | 1507 | ||
1500 | // -------- User clicked the "Delete" button when editing a link : Delete link from database. | 1508 | // -------- User clicked the "Delete" button when editing a link: Delete link from database. |
1501 | if (isset($_POST['delete_link'])) | 1509 | if (isset($_POST['delete_link'])) |
1502 | { | 1510 | { |
1503 | if (!tokenOk($_POST['token'])) die('Wrong token.'); | 1511 | if (!tokenOk($_POST['token'])) die('Wrong token.'); |
1504 | // We do not need to ask for confirmation: | 1512 | // We do not need to ask for confirmation: |
1505 | // - confirmation is handled by javascript | 1513 | // - confirmation is handled by JavaScript |
1506 | // - we are protected from XSRF by the token. | 1514 | // - we are protected from XSRF by the token. |
1507 | $linkdate=$_POST['lf_linkdate']; | 1515 | $linkdate=$_POST['lf_linkdate']; |
1508 | unset($LINKSDB[$linkdate]); | 1516 | unset($LINKSDB[$linkdate]); |
@@ -1552,7 +1560,7 @@ function renderPage() | |||
1552 | $tags = (empty($_GET['tags']) ? '' : $_GET['tags'] ); // Get tags if it was provided in URL | 1560 | $tags = (empty($_GET['tags']) ? '' : $_GET['tags'] ); // Get tags if it was provided in URL |
1553 | $private = (!empty($_GET['private']) && $_GET['private'] === "1" ? 1 : 0); // Get private if it was provided in URL | 1561 | $private = (!empty($_GET['private']) && $_GET['private'] === "1" ? 1 : 0); // Get private if it was provided in URL |
1554 | if (($url!='') && parse_url($url,PHP_URL_SCHEME)=='') $url = 'http://'.$url; | 1562 | if (($url!='') && parse_url($url,PHP_URL_SCHEME)=='') $url = 'http://'.$url; |
1555 | // If this is an HTTP link, we try go get the page to extact the title (otherwise we will to straight to the edit form.) | 1563 | // If this is an HTTP link, we try go get the page to extract the title (otherwise we will to straight to the edit form.) |
1556 | if (empty($title) && parse_url($url,PHP_URL_SCHEME)=='http') | 1564 | if (empty($title) && parse_url($url,PHP_URL_SCHEME)=='http') |
1557 | { | 1565 | { |
1558 | list($status,$headers,$data) = getHTTP($url,4); // Short timeout to keep the application responsive. | 1566 | list($status,$headers,$data) = getHTTP($url,4); // Short timeout to keep the application responsive. |
@@ -1581,7 +1589,11 @@ function renderPage() | |||
1581 | } | 1589 | } |
1582 | } | 1590 | } |
1583 | } | 1591 | } |
1584 | if ($url=='') $url='?'.smallHash($linkdate); // In case of empty URL, this is just a text (with a link that point to itself) | 1592 | if ($url=='') // In case of empty URL, this is just a text (with a link that points to itself) |
1593 | { | ||
1594 | $url='?'.smallHash($linkdate); | ||
1595 | $title='Note: '; | ||
1596 | } | ||
1585 | $link = array('linkdate'=>$linkdate,'title'=>$title,'url'=>$url,'description'=>$description,'tags'=>$tags,'private'=>$private); | 1597 | $link = array('linkdate'=>$linkdate,'title'=>$title,'url'=>$url,'description'=>$description,'tags'=>$tags,'private'=>$private); |
1586 | } | 1598 | } |
1587 | 1599 | ||
@@ -1606,7 +1618,7 @@ function renderPage() | |||
1606 | exit; | 1618 | exit; |
1607 | } | 1619 | } |
1608 | $exportWhat=$_GET['what']; | 1620 | $exportWhat=$_GET['what']; |
1609 | if (!array_intersect(array('all','public','private'),array($exportWhat))) die('What are you trying to export ???'); | 1621 | if (!array_intersect(array('all','public','private'),array($exportWhat))) die('What are you trying to export???'); |
1610 | 1622 | ||
1611 | header('Content-Type: text/html; charset=utf-8'); | 1623 | header('Content-Type: text/html; charset=utf-8'); |
1612 | header('Content-disposition: attachment; filename=bookmarks_'.$exportWhat.'_'.strval(date('Ymd_His')).'.html'); | 1624 | header('Content-disposition: attachment; filename=bookmarks_'.$exportWhat.'_'.strval(date('Ymd_His')).'.html'); |
@@ -1679,8 +1691,8 @@ function importFile() | |||
1679 | $filename=$_FILES['filetoupload']['name']; | 1691 | $filename=$_FILES['filetoupload']['name']; |
1680 | $filesize=$_FILES['filetoupload']['size']; | 1692 | $filesize=$_FILES['filetoupload']['size']; |
1681 | $data=file_get_contents($_FILES['filetoupload']['tmp_name']); | 1693 | $data=file_get_contents($_FILES['filetoupload']['tmp_name']); |
1682 | $private = (empty($_POST['private']) ? 0 : 1); // Should the links be imported as private ? | 1694 | $private = (empty($_POST['private']) ? 0 : 1); // Should the links be imported as private? |
1683 | $overwrite = !empty($_POST['overwrite']) ; // Should the imported links overwrite existing ones ? | 1695 | $overwrite = !empty($_POST['overwrite']) ; // Should the imported links overwrite existing ones? |
1684 | $import_count=0; | 1696 | $import_count=0; |
1685 | 1697 | ||
1686 | // Sniff file type: | 1698 | // Sniff file type: |
@@ -1691,7 +1703,7 @@ function importFile() | |||
1691 | if ($type=='netscape') | 1703 | if ($type=='netscape') |
1692 | { | 1704 | { |
1693 | // This is a standard Netscape-style bookmark file. | 1705 | // This is a standard Netscape-style bookmark file. |
1694 | // This format is supported by all browsers (except IE, of course), also delicious, diigo and others. | 1706 | // This format is supported by all browsers (except IE, of course), also Delicious, Diigo and others. |
1695 | foreach(explode('<DT>',$data) as $html) // explode is very fast | 1707 | foreach(explode('<DT>',$data) as $html) // explode is very fast |
1696 | { | 1708 | { |
1697 | $link = array('linkdate'=>'','title'=>'','url'=>'','description'=>'','tags'=>'','private'=>0); | 1709 | $link = array('linkdate'=>'','title'=>'','url'=>'','description'=>'','tags'=>'','private'=>0); |
@@ -1725,14 +1737,14 @@ function importFile() | |||
1725 | 1737 | ||
1726 | // Make sure date/time is not already used by another link. | 1738 | // Make sure date/time is not already used by another link. |
1727 | // (Some bookmark files have several different links with the same ADD_DATE) | 1739 | // (Some bookmark files have several different links with the same ADD_DATE) |
1728 | // We increment date by 1 second until we find a date which is not used in db. | 1740 | // We increment date by 1 second until we find a date which is not used in DB. |
1729 | // (so that links that have the same date/time are more or less kept grouped by date, but do not conflict.) | 1741 | // (so that links that have the same date/time are more or less kept grouped by date, but do not conflict.) |
1730 | while (!empty($LINKSDB[date('Ymd_His',$raw_add_date)])) { $raw_add_date++; }// Yes, I know it's ugly. | 1742 | while (!empty($LINKSDB[date('Ymd_His',$raw_add_date)])) { $raw_add_date++; }// Yes, I know it's ugly. |
1731 | $link['linkdate']=date('Ymd_His',$raw_add_date); | 1743 | $link['linkdate']=date('Ymd_His',$raw_add_date); |
1732 | $LINKSDB[$link['linkdate']] = $link; | 1744 | $LINKSDB[$link['linkdate']] = $link; |
1733 | $import_count++; | 1745 | $import_count++; |
1734 | } | 1746 | } |
1735 | else // link already present in database. | 1747 | else // Link already present in database. |
1736 | { | 1748 | { |
1737 | if ($overwrite) | 1749 | if ($overwrite) |
1738 | { // If overwrite is required, we import link data, except date/time. | 1750 | { // If overwrite is required, we import link data, except date/time. |
@@ -1747,11 +1759,11 @@ function importFile() | |||
1747 | } | 1759 | } |
1748 | $LINKSDB->savedb(); | 1760 | $LINKSDB->savedb(); |
1749 | 1761 | ||
1750 | echo '<script language="JavaScript">alert("File '.$filename.' ('.$filesize.' bytes) was successfully processed: '.$import_count.' links imported.");document.location=\'?\';</script>'; | 1762 | echo '<script language="JavaScript">alert("File '.json_encode($filename).' ('.$filesize.' bytes) was successfully processed: '.$import_count.' links imported.");document.location=\'?\';</script>'; |
1751 | } | 1763 | } |
1752 | else | 1764 | else |
1753 | { | 1765 | { |
1754 | echo '<script language="JavaScript">alert("File '.$filename.' ('.$filesize.' bytes) has an unknown file format. Nothing was imported.");document.location=\'?\';</script>'; | 1766 | echo '<script language="JavaScript">alert("File '.json_encode($filename).' ('.$filesize.' bytes) has an unknown file format. Nothing was imported.");document.location=\'?\';</script>'; |
1755 | } | 1767 | } |
1756 | } | 1768 | } |
1757 | 1769 | ||
@@ -1783,13 +1795,13 @@ function buildLinkList($PAGE,$LINKSDB) | |||
1783 | { | 1795 | { |
1784 | header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found"); | 1796 | header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found"); |
1785 | echo '<h1>404 Not found.</h1>Oh crap. The link you are trying to reach does not exist or has been deleted.'; | 1797 | echo '<h1>404 Not found.</h1>Oh crap. The link you are trying to reach does not exist or has been deleted.'; |
1786 | echo '<br>You would mind <a href="?">clicking here</a> ?'; | 1798 | echo '<br>You would mind <a href="?">clicking here</a>?'; |
1787 | exit; | 1799 | exit; |
1788 | } | 1800 | } |
1789 | $search_type='permalink'; | 1801 | $search_type='permalink'; |
1790 | } | 1802 | } |
1791 | else | 1803 | else |
1792 | $linksToDisplay = $LINKSDB; // otherwise, display without filtering. | 1804 | $linksToDisplay = $LINKSDB; // Otherwise, display without filtering. |
1793 | 1805 | ||
1794 | // Option: Show only private links | 1806 | // Option: Show only private links |
1795 | if (!empty($_SESSION['privateonly'])) | 1807 | if (!empty($_SESSION['privateonly'])) |
@@ -1803,11 +1815,11 @@ function buildLinkList($PAGE,$LINKSDB) | |||
1803 | } | 1815 | } |
1804 | 1816 | ||
1805 | // ---- Handle paging. | 1817 | // ---- Handle paging. |
1806 | /* Can someone explain to me why you get the following error when using array_keys() on an object which implements the interface ArrayAccess ??? | 1818 | /* Can someone explain to me why you get the following error when using array_keys() on an object which implements the interface ArrayAccess??? |
1807 | "Warning: array_keys() expects parameter 1 to be array, object given in ... " | 1819 | "Warning: array_keys() expects parameter 1 to be array, object given in ... " |
1808 | If my class implements ArrayAccess, why won't array_keys() accept it ? ( $keys=array_keys($linksToDisplay); ) | 1820 | If my class implements ArrayAccess, why won't array_keys() accept it ? ( $keys=array_keys($linksToDisplay); ) |
1809 | */ | 1821 | */ |
1810 | $keys=array(); foreach($linksToDisplay as $key=>$value) { $keys[]=$key; } // Stupid and ugly. Thanks php. | 1822 | $keys=array(); foreach($linksToDisplay as $key=>$value) { $keys[]=$key; } // Stupid and ugly. Thanks PHP. |
1811 | 1823 | ||
1812 | // If there is only a single link, we change on-the-fly the title of the page. | 1824 | // If there is only a single link, we change on-the-fly the title of the page. |
1813 | if (count($linksToDisplay)==1) $GLOBALS['pagetitle'] = $linksToDisplay[$keys[0]]['title'].' - '.$GLOBALS['title']; | 1825 | if (count($linksToDisplay)==1) $GLOBALS['pagetitle'] = $linksToDisplay[$keys[0]]['title'].' - '.$GLOBALS['title']; |
@@ -1854,7 +1866,7 @@ function buildLinkList($PAGE,$LINKSDB) | |||
1854 | $PAGE->assign('result_count',count($linksToDisplay)); | 1866 | $PAGE->assign('result_count',count($linksToDisplay)); |
1855 | $PAGE->assign('search_type',$search_type); | 1867 | $PAGE->assign('search_type',$search_type); |
1856 | $PAGE->assign('search_crits',$search_crits); | 1868 | $PAGE->assign('search_crits',$search_crits); |
1857 | $PAGE->assign('redirector',empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector']); // optional redirector URL | 1869 | $PAGE->assign('redirector',empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector']); // Optional redirector URL. |
1858 | $PAGE->assign('token',$token); | 1870 | $PAGE->assign('token',$token); |
1859 | $PAGE->assign('links',$linkDisp); | 1871 | $PAGE->assign('links',$linkDisp); |
1860 | return; | 1872 | return; |
@@ -1862,9 +1874,9 @@ function buildLinkList($PAGE,$LINKSDB) | |||
1862 | 1874 | ||
1863 | // Compute the thumbnail for a link. | 1875 | // Compute the thumbnail for a link. |
1864 | // | 1876 | // |
1865 | // with a link to the original URL. | 1877 | // With a link to the original URL. |
1866 | // Understands various services (youtube.com...) | 1878 | // Understands various services (youtube.com...) |
1867 | // Input: $url = url for which the thumbnail must be found. | 1879 | // Input: $url = URL for which the thumbnail must be found. |
1868 | // $href = if provided, this URL will be followed instead of $url | 1880 | // $href = if provided, this URL will be followed instead of $url |
1869 | // Returns an associative array with thumbnail attributes (src,href,width,height,style,alt) | 1881 | // Returns an associative array with thumbnail attributes (src,href,width,height,style,alt) |
1870 | // Some of them may be missing. | 1882 | // Some of them may be missing. |
@@ -1875,19 +1887,19 @@ function computeThumbnail($url,$href=false) | |||
1875 | if ($href==false) $href=$url; | 1887 | if ($href==false) $href=$url; |
1876 | 1888 | ||
1877 | // For most hosts, the URL of the thumbnail can be easily deduced from the URL of the link. | 1889 | // For most hosts, the URL of the thumbnail can be easily deduced from the URL of the link. |
1878 | // (eg. http://www.youtube.com/watch?v=spVypYk4kto ---> http://img.youtube.com/vi/spVypYk4kto/default.jpg ) | 1890 | // (e.g. http://www.youtube.com/watch?v=spVypYk4kto ---> http://img.youtube.com/vi/spVypYk4kto/default.jpg ) |
1879 | // ^^^^^^^^^^^ ^^^^^^^^^^^ | 1891 | // ^^^^^^^^^^^ ^^^^^^^^^^^ |
1880 | $domain = parse_url($url,PHP_URL_HOST); | 1892 | $domain = parse_url($url,PHP_URL_HOST); |
1881 | if ($domain=='youtube.com' || $domain=='www.youtube.com') | 1893 | if ($domain=='youtube.com' || $domain=='www.youtube.com') |
1882 | { | 1894 | { |
1883 | parse_str(parse_url($url,PHP_URL_QUERY), $params); // Extract video ID and get thumbnail | 1895 | parse_str(parse_url($url,PHP_URL_QUERY), $params); // Extract video ID and get thumbnail |
1884 | if (!empty($params['v'])) return array('src'=>'http://img.youtube.com/vi/'.$params['v'].'/default.jpg', | 1896 | if (!empty($params['v'])) return array('src'=>'https://img.youtube.com/vi/'.$params['v'].'/default.jpg', |
1885 | 'href'=>$href,'width'=>'120','height'=>'90','alt'=>'YouTube thumbnail'); | 1897 | 'href'=>$href,'width'=>'120','height'=>'90','alt'=>'YouTube thumbnail'); |
1886 | } | 1898 | } |
1887 | if ($domain=='youtu.be') // Youtube short links | 1899 | if ($domain=='youtu.be') // Youtube short links |
1888 | { | 1900 | { |
1889 | $path = parse_url($url,PHP_URL_PATH); | 1901 | $path = parse_url($url,PHP_URL_PATH); |
1890 | return array('src'=>'http://img.youtube.com/vi'.$path.'/default.jpg', | 1902 | return array('src'=>'https://img.youtube.com/vi'.$path.'/default.jpg', |
1891 | 'href'=>$href,'width'=>'120','height'=>'90','alt'=>'YouTube thumbnail'); | 1903 | 'href'=>$href,'width'=>'120','height'=>'90','alt'=>'YouTube thumbnail'); |
1892 | } | 1904 | } |
1893 | if ($domain=='pix.toile-libre.org') // pix.toile-libre.org image hosting | 1905 | if ($domain=='pix.toile-libre.org') // pix.toile-libre.org image hosting |
@@ -1901,18 +1913,18 @@ function computeThumbnail($url,$href=false) | |||
1901 | { | 1913 | { |
1902 | $path = parse_url($url,PHP_URL_PATH); | 1914 | $path = parse_url($url,PHP_URL_PATH); |
1903 | if (startsWith($path,'/a/')) return array(); // Thumbnails for albums are not available. | 1915 | if (startsWith($path,'/a/')) return array(); // Thumbnails for albums are not available. |
1904 | if (startsWith($path,'/r/')) return array('src'=>'http://i.imgur.com/'.basename($path).'s.jpg', | 1916 | if (startsWith($path,'/r/')) return array('src'=>'https://i.imgur.com/'.basename($path).'s.jpg', |
1905 | 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); | 1917 | 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); |
1906 | if (startsWith($path,'/gallery/')) return array('src'=>'http://i.imgur.com'.substr($path,8).'s.jpg', | 1918 | if (startsWith($path,'/gallery/')) return array('src'=>'https://i.imgur.com'.substr($path,8).'s.jpg', |
1907 | 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); | 1919 | 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); |
1908 | 1920 | ||
1909 | if (substr_count($path,'/')==1) return array('src'=>'http://i.imgur.com/'.substr($path,1).'s.jpg', | 1921 | if (substr_count($path,'/')==1) return array('src'=>'https://i.imgur.com/'.substr($path,1).'s.jpg', |
1910 | 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); | 1922 | 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); |
1911 | } | 1923 | } |
1912 | if ($domain=='i.imgur.com') | 1924 | if ($domain=='i.imgur.com') |
1913 | { | 1925 | { |
1914 | $pi = pathinfo(parse_url($url,PHP_URL_PATH)); | 1926 | $pi = pathinfo(parse_url($url,PHP_URL_PATH)); |
1915 | if (!empty($pi['filename'])) return array('src'=>'http://i.imgur.com/'.$pi['filename'].'s.jpg', | 1927 | if (!empty($pi['filename'])) return array('src'=>'https://i.imgur.com/'.$pi['filename'].'s.jpg', |
1916 | 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); | 1928 | 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); |
1917 | } | 1929 | } |
1918 | if ($domain=='dailymotion.com' || $domain=='www.dailymotion.com') | 1930 | if ($domain=='dailymotion.com' || $domain=='www.dailymotion.com') |
@@ -1948,17 +1960,17 @@ function computeThumbnail($url,$href=false) | |||
1948 | ) | 1960 | ) |
1949 | { | 1961 | { |
1950 | if ($domain=='vimeo.com') | 1962 | if ($domain=='vimeo.com') |
1951 | { // Make sure this vimeo url points to a video (/xxx... where xxx is numeric) | 1963 | { // Make sure this vimeo URL points to a video (/xxx... where xxx is numeric) |
1952 | $path = parse_url($url,PHP_URL_PATH); | 1964 | $path = parse_url($url,PHP_URL_PATH); |
1953 | if (!preg_match('!/\d+.+?!',$path)) return array(); // This is not a single video URL. | 1965 | if (!preg_match('!/\d+.+?!',$path)) return array(); // This is not a single video URL. |
1954 | } | 1966 | } |
1955 | if ($domain=='xkcd.com' || endsWith($domain,'.xkcd.com')) | 1967 | if ($domain=='xkcd.com' || endsWith($domain,'.xkcd.com')) |
1956 | { // Make sure this url points to a single comic (/xxx... where xxx is numeric) | 1968 | { // Make sure this URL points to a single comic (/xxx... where xxx is numeric) |
1957 | $path = parse_url($url,PHP_URL_PATH); | 1969 | $path = parse_url($url,PHP_URL_PATH); |
1958 | if (!preg_match('!/\d+.+?!',$path)) return array(); | 1970 | if (!preg_match('!/\d+.+?!',$path)) return array(); |
1959 | } | 1971 | } |
1960 | if ($domain=='ted.com' || endsWith($domain,'.ted.com')) | 1972 | if ($domain=='ted.com' || endsWith($domain,'.ted.com')) |
1961 | { // Make sure this TED url points to a video (/talks/...) | 1973 | { // Make sure this TED URL points to a video (/talks/...) |
1962 | $path = parse_url($url,PHP_URL_PATH); | 1974 | $path = parse_url($url,PHP_URL_PATH); |
1963 | if ("/talks/" !== substr($path,0,7)) return array(); // This is not a single video URL. | 1975 | if ("/talks/" !== substr($path,0,7)) return array(); // This is not a single video URL. |
1964 | } | 1976 | } |
@@ -1985,7 +1997,7 @@ function computeThumbnail($url,$href=false) | |||
1985 | // Returns the HTML code to display a thumbnail for a link | 1997 | // Returns the HTML code to display a thumbnail for a link |
1986 | // with a link to the original URL. | 1998 | // with a link to the original URL. |
1987 | // Understands various services (youtube.com...) | 1999 | // Understands various services (youtube.com...) |
1988 | // Input: $url = url for which the thumbnail must be found. | 2000 | // Input: $url = URL for which the thumbnail must be found. |
1989 | // $href = if provided, this URL will be followed instead of $url | 2001 | // $href = if provided, this URL will be followed instead of $url |
1990 | // Returns '' if no thumbnail available. | 2002 | // Returns '' if no thumbnail available. |
1991 | function thumbnail($url,$href=false) | 2003 | function thumbnail($url,$href=false) |
@@ -2006,7 +2018,7 @@ function thumbnail($url,$href=false) | |||
2006 | // Returns the HTML code to display a thumbnail for a link | 2018 | // Returns the HTML code to display a thumbnail for a link |
2007 | // for the picture wall (using lazy image loading) | 2019 | // for the picture wall (using lazy image loading) |
2008 | // Understands various services (youtube.com...) | 2020 | // Understands various services (youtube.com...) |
2009 | // Input: $url = url for which the thumbnail must be found. | 2021 | // Input: $url = URL for which the thumbnail must be found. |
2010 | // $href = if provided, this URL will be followed instead of $url | 2022 | // $href = if provided, this URL will be followed instead of $url |
2011 | // Returns '' if no thumbnail available. | 2023 | // Returns '' if no thumbnail available. |
2012 | function lazyThumbnail($url,$href=false) | 2024 | function lazyThumbnail($url,$href=false) |
@@ -2016,7 +2028,7 @@ function lazyThumbnail($url,$href=false) | |||
2016 | 2028 | ||
2017 | $html='<a href="'.htmlspecialchars($t['href']).'">'; | 2029 | $html='<a href="'.htmlspecialchars($t['href']).'">'; |
2018 | 2030 | ||
2019 | // Lazy image (only loaded by javascript when in the viewport). | 2031 | // Lazy image (only loaded by JavaScript when in the viewport). |
2020 | if (!empty($GLOBALS['disablejquery'])) // (except if jQuery is disabled) | 2032 | if (!empty($GLOBALS['disablejquery'])) // (except if jQuery is disabled) |
2021 | $html.='<img class="lazyimage" src="'.htmlspecialchars($t['src']).'"'; | 2033 | $html.='<img class="lazyimage" src="'.htmlspecialchars($t['src']).'"'; |
2022 | else | 2034 | else |
@@ -2028,7 +2040,7 @@ function lazyThumbnail($url,$href=false) | |||
2028 | if (!empty($t['alt'])) $html.=' alt="'.htmlspecialchars($t['alt']).'"'; | 2040 | if (!empty($t['alt'])) $html.=' alt="'.htmlspecialchars($t['alt']).'"'; |
2029 | $html.='>'; | 2041 | $html.='>'; |
2030 | 2042 | ||
2031 | // No-javascript fallback. | 2043 | // No-JavaScript fallback. |
2032 | $html.='<noscript><img src="'.htmlspecialchars($t['src']).'"'; | 2044 | $html.='<noscript><img src="'.htmlspecialchars($t['src']).'"'; |
2033 | if (!empty($t['width'])) $html.=' width="'.htmlspecialchars($t['width']).'"'; | 2045 | if (!empty($t['width'])) $html.=' width="'.htmlspecialchars($t['width']).'"'; |
2034 | if (!empty($t['height'])) $html.=' height="'.htmlspecialchars($t['height']).'"'; | 2046 | if (!empty($t['height'])) $html.=' height="'.htmlspecialchars($t['height']).'"'; |
@@ -2056,7 +2068,9 @@ function install() | |||
2056 | { // Step 2: Check if data in session is correct. | 2068 | { // Step 2: Check if data in session is correct. |
2057 | echo '<pre>Sessions do not seem to work correctly on your server.<br>'; | 2069 | echo '<pre>Sessions do not seem to work correctly on your server.<br>'; |
2058 | echo 'Make sure the variable session.save_path is set correctly in your php config, and that you have write access to it.<br>'; | 2070 | echo 'Make sure the variable session.save_path is set correctly in your php config, and that you have write access to it.<br>'; |
2059 | echo 'It currently points to '.session_save_path().'<br><br><a href="?">Click to try again.</a></pre>'; | 2071 | echo 'It currently points to '.session_save_path().'<br>'; |
2072 | echo 'Check that the hostname used to access Shaarli contains a dot. On some browsers, accessing your server via a hostname like \'localhost\' or any custom hostname without a dot causes cookie storage to fail. We recommend accessing your server via it\'s IP address or Fully Qualified Domain Name.<br>'; | ||
2073 | echo '<br><a href="?">Click to try again.</a></pre>'; | ||
2060 | die; | 2074 | die; |
2061 | } | 2075 | } |
2062 | if (!isset($_SESSION['session_tested'])) | 2076 | if (!isset($_SESSION['session_tested'])) |
@@ -2065,7 +2079,7 @@ function install() | |||
2065 | header('Location: '.indexUrl().'?test_session'); // Redirect to check stored data. | 2079 | header('Location: '.indexUrl().'?test_session'); // Redirect to check stored data. |
2066 | } | 2080 | } |
2067 | if (isset($_GET['test_session'])) | 2081 | if (isset($_GET['test_session'])) |
2068 | { // Step 3: Sessions are ok. Remove test parameter from URL. | 2082 | { // Step 3: Sessions are OK. Remove test parameter from URL. |
2069 | header('Location: '.indexUrl()); | 2083 | header('Location: '.indexUrl()); |
2070 | } | 2084 | } |
2071 | 2085 | ||
@@ -2083,7 +2097,7 @@ function install() | |||
2083 | $GLOBALS['hash'] = sha1($_POST['setpassword'].$GLOBALS['login'].$GLOBALS['salt']); | 2097 | $GLOBALS['hash'] = sha1($_POST['setpassword'].$GLOBALS['login'].$GLOBALS['salt']); |
2084 | $GLOBALS['title'] = (empty($_POST['title']) ? 'Shared links on '.htmlspecialchars(indexUrl()) : $_POST['title'] ); | 2098 | $GLOBALS['title'] = (empty($_POST['title']) ? 'Shared links on '.htmlspecialchars(indexUrl()) : $_POST['title'] ); |
2085 | writeConfig(); | 2099 | writeConfig(); |
2086 | echo '<script language="JavaScript">alert("Shaarli is now configured. Please enter your login/password and start shaaring your links !");document.location=\'?do=login\';</script>'; | 2100 | echo '<script language="JavaScript">alert("Shaarli is now configured. Please enter your login/password and start shaaring your links!");document.location=\'?do=login\';</script>'; |
2087 | exit; | 2101 | exit; |
2088 | } | 2102 | } |
2089 | 2103 | ||
@@ -2098,14 +2112,14 @@ function install() | |||
2098 | exit; | 2112 | exit; |
2099 | } | 2113 | } |
2100 | 2114 | ||
2101 | // Generates the timezone selection form and javascript. | 2115 | // Generates the timezone selection form and JavaScript. |
2102 | // Input: (optional) current timezone (can be 'UTC/UTC'). It will be pre-selected. | 2116 | // Input: (optional) current timezone (can be 'UTC/UTC'). It will be pre-selected. |
2103 | // Output: array(html,js) | 2117 | // Output: array(html,js) |
2104 | // Example: list($htmlform,$js) = templateTZform('Europe/Paris'); // Europe/Paris pre-selected. | 2118 | // Example: list($htmlform,$js) = templateTZform('Europe/Paris'); // Europe/Paris pre-selected. |
2105 | // Returns array('','') if server does not support timezones list. (eg. php 5.1 on free.fr) | 2119 | // Returns array('','') if server does not support timezones list. (e.g. PHP 5.1 on free.fr) |
2106 | function templateTZform($ptz=false) | 2120 | function templateTZform($ptz=false) |
2107 | { | 2121 | { |
2108 | if (function_exists('timezone_identifiers_list')) // because of old php version (5.1) which can be found on free.fr | 2122 | if (function_exists('timezone_identifiers_list')) // because of old PHP version (5.1) which can be found on free.fr |
2109 | { | 2123 | { |
2110 | // Try to split the provided timezone. | 2124 | // Try to split the provided timezone. |
2111 | if ($ptz==false) { $l=timezone_identifiers_list(); $ptz=$l[0]; } | 2125 | if ($ptz==false) { $l=timezone_identifiers_list(); $ptz=$l[0]; } |
@@ -2114,7 +2128,7 @@ function templateTZform($ptz=false) | |||
2114 | // Display config form: | 2128 | // Display config form: |
2115 | $timezone_form = ''; | 2129 | $timezone_form = ''; |
2116 | $timezone_js = ''; | 2130 | $timezone_js = ''; |
2117 | // The list is in the forme "Europe/Paris", "America/Argentina/Buenos_Aires"... | 2131 | // The list is in the form "Europe/Paris", "America/Argentina/Buenos_Aires"... |
2118 | // We split the list in continents/cities. | 2132 | // We split the list in continents/cities. |
2119 | $continents = array(); | 2133 | $continents = array(); |
2120 | $cities = array(); | 2134 | $cities = array(); |
@@ -2152,9 +2166,9 @@ function templateTZform($ptz=false) | |||
2152 | function isTZvalid($continent,$city) | 2166 | function isTZvalid($continent,$city) |
2153 | { | 2167 | { |
2154 | $tz = $continent.'/'.$city; | 2168 | $tz = $continent.'/'.$city; |
2155 | if (function_exists('timezone_identifiers_list')) // because of old php version (5.1) which can be found on free.fr | 2169 | if (function_exists('timezone_identifiers_list')) // because of old PHP version (5.1) which can be found on free.fr |
2156 | { | 2170 | { |
2157 | if (in_array($tz, timezone_identifiers_list())) // it's a valid timezone ? | 2171 | if (in_array($tz, timezone_identifiers_list())) // it's a valid timezone? |
2158 | return true; | 2172 | return true; |
2159 | } | 2173 | } |
2160 | return false; | 2174 | return false; |
@@ -2197,7 +2211,7 @@ if (!function_exists('json_encode')) { | |||
2197 | } | 2211 | } |
2198 | 2212 | ||
2199 | // Webservices (for use with jQuery/jQueryUI) | 2213 | // Webservices (for use with jQuery/jQueryUI) |
2200 | // eg. index.php?ws=tags&term=minecr | 2214 | // e.g. index.php?ws=tags&term=minecr |
2201 | function processWS() | 2215 | function processWS() |
2202 | { | 2216 | { |
2203 | if (empty($_GET['ws']) || empty($_GET['term'])) return; | 2217 | if (empty($_GET['ws']) || empty($_GET['term'])) return; |
@@ -2205,7 +2219,7 @@ function processWS() | |||
2205 | $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in). | 2219 | $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in). |
2206 | header('Content-Type: application/json; charset=utf-8'); | 2220 | header('Content-Type: application/json; charset=utf-8'); |
2207 | 2221 | ||
2208 | // Search in tags (case insentitive, cumulative search) | 2222 | // Search in tags (case insensitive, cumulative search) |
2209 | if ($_GET['ws']=='tags') | 2223 | if ($_GET['ws']=='tags') |
2210 | { | 2224 | { |
2211 | $tags=explode(' ',str_replace(',',' ',$term)); $last = array_pop($tags); // Get the last term ("a b c d" ==> "a b c", "d") | 2225 | $tags=explode(' ',str_replace(',',' ',$term)); $last = array_pop($tags); // Get the last term ("a b c d" ==> "a b c", "d") |
@@ -2221,7 +2235,7 @@ function processWS() | |||
2221 | exit; | 2235 | exit; |
2222 | } | 2236 | } |
2223 | 2237 | ||
2224 | // Search a single tag (case sentitive, single tag search) | 2238 | // Search a single tag (case sensitive, single tag search) |
2225 | if ($_GET['ws']=='singletag') | 2239 | if ($_GET['ws']=='singletag') |
2226 | { | 2240 | { |
2227 | /* To speed up things, we store list of tags in session */ | 2241 | /* To speed up things, we store list of tags in session */ |
@@ -2237,13 +2251,14 @@ function processWS() | |||
2237 | 2251 | ||
2238 | // Re-write configuration file according to globals. | 2252 | // Re-write configuration file according to globals. |
2239 | // Requires some $GLOBALS to be set (login,hash,salt,title). | 2253 | // Requires some $GLOBALS to be set (login,hash,salt,title). |
2240 | // If the config file cannot be saved, an error message is dislayed and the user is redirected to "Tools" menu. | 2254 | // If the config file cannot be saved, an error message is displayed and the user is redirected to "Tools" menu. |
2241 | // (otherwise, the function simply returns.) | 2255 | // (otherwise, the function simply returns.) |
2242 | function writeConfig() | 2256 | function writeConfig() |
2243 | { | 2257 | { |
2244 | if (is_file($GLOBALS['config']['CONFIG_FILE']) && !isLoggedIn()) die('You are not authorized to alter config.'); // Only logged in user can alter config. | 2258 | if (is_file($GLOBALS['config']['CONFIG_FILE']) && !isLoggedIn()) die('You are not authorized to alter config.'); // Only logged in user can alter config. |
2245 | $config='<?php $GLOBALS[\'login\']='.var_export($GLOBALS['login'],true).'; $GLOBALS[\'hash\']='.var_export($GLOBALS['hash'],true).'; $GLOBALS[\'salt\']='.var_export($GLOBALS['salt'],true).'; '; | 2259 | $config='<?php $GLOBALS[\'login\']='.var_export($GLOBALS['login'],true).'; $GLOBALS[\'hash\']='.var_export($GLOBALS['hash'],true).'; $GLOBALS[\'salt\']='.var_export($GLOBALS['salt'],true).'; '; |
2246 | $config .='$GLOBALS[\'timezone\']='.var_export($GLOBALS['timezone'],true).'; date_default_timezone_set('.var_export($GLOBALS['timezone'],true).'); $GLOBALS[\'title\']='.var_export($GLOBALS['title'],true).';'; | 2260 | $config .='$GLOBALS[\'timezone\']='.var_export($GLOBALS['timezone'],true).'; date_default_timezone_set('.var_export($GLOBALS['timezone'],true).'); $GLOBALS[\'title\']='.var_export($GLOBALS['title'],true).';'; |
2261 | $config .= '$GLOBALS[\'titleLink\']='.var_export($GLOBALS['titleLink'],true).'; '; | ||
2247 | $config .= '$GLOBALS[\'redirector\']='.var_export($GLOBALS['redirector'],true).'; '; | 2262 | $config .= '$GLOBALS[\'redirector\']='.var_export($GLOBALS['redirector'],true).'; '; |
2248 | $config .= '$GLOBALS[\'disablesessionprotection\']='.var_export($GLOBALS['disablesessionprotection'],true).'; '; | 2263 | $config .= '$GLOBALS[\'disablesessionprotection\']='.var_export($GLOBALS['disablesessionprotection'],true).'; '; |
2249 | $config .= '$GLOBALS[\'disablejquery\']='.var_export($GLOBALS['disablejquery'],true).'; '; | 2264 | $config .= '$GLOBALS[\'disablejquery\']='.var_export($GLOBALS['disablejquery'],true).'; '; |
@@ -2256,12 +2271,12 @@ function writeConfig() | |||
2256 | } | 2271 | } |
2257 | } | 2272 | } |
2258 | 2273 | ||
2259 | /* Because some f*cking services like Flickr require an extra HTTP request to get the thumbnail URL, | 2274 | /* Because some f*cking services like flickr require an extra HTTP request to get the thumbnail URL, |
2260 | I have deported the thumbnail URL code generation here, otherwise this would slow down page generation. | 2275 | I have deported the thumbnail URL code generation here, otherwise this would slow down page generation. |
2261 | The following function takes the URL a link (eg. a flickr page) and return the proper thumbnail. | 2276 | The following function takes the URL a link (e.g. a flickr page) and return the proper thumbnail. |
2262 | This function is called by passing the url: | 2277 | This function is called by passing the URL: |
2263 | http://mywebsite.com/shaarli/?do=genthumbnail&hmac=[HMAC]&url=[URL] | 2278 | http://mywebsite.com/shaarli/?do=genthumbnail&hmac=[HMAC]&url=[URL] |
2264 | [URL] is the URL of the link (eg. a flickr page) | 2279 | [URL] is the URL of the link (e.g. a flickr page) |
2265 | [HMAC] is the signature for the [URL] (so that these URL cannot be forged). | 2280 | [HMAC] is the signature for the [URL] (so that these URL cannot be forged). |
2266 | The function below will fetch the image from the webservice and store it in the cache. | 2281 | The function below will fetch the image from the webservice and store it in the cache. |
2267 | */ | 2282 | */ |
@@ -2269,7 +2284,7 @@ function genThumbnail() | |||
2269 | { | 2284 | { |
2270 | // Make sure the parameters in the URL were generated by us. | 2285 | // Make sure the parameters in the URL were generated by us. |
2271 | $sign = hash_hmac('sha256', $_GET['url'], $GLOBALS['salt']); | 2286 | $sign = hash_hmac('sha256', $_GET['url'], $GLOBALS['salt']); |
2272 | if ($sign!=$_GET['hmac']) die('Naughty boy !'); | 2287 | if ($sign!=$_GET['hmac']) die('Naughty boy!'); |
2273 | 2288 | ||
2274 | // Let's see if we don't already have the image for this URL in the cache. | 2289 | // Let's see if we don't already have the image for this URL in the cache. |
2275 | $thumbname=hash('sha1',$_GET['url']).'.jpg'; | 2290 | $thumbname=hash('sha1',$_GET['url']).'.jpg'; |
@@ -2294,22 +2309,22 @@ function genThumbnail() | |||
2294 | 2309 | ||
2295 | if ($domain=='flickr.com' || endsWith($domain,'.flickr.com')) | 2310 | if ($domain=='flickr.com' || endsWith($domain,'.flickr.com')) |
2296 | { | 2311 | { |
2297 | // Crude replacement to handle new Flickr domain policy (They prefer www. now) | 2312 | // Crude replacement to handle new flickr domain policy (They prefer www. now) |
2298 | $url = str_replace('http://flickr.com/','http://www.flickr.com/',$url); | 2313 | $url = str_replace('http://flickr.com/','http://www.flickr.com/',$url); |
2299 | 2314 | ||
2300 | // Is this a link to an image, or to a flickr page ? | 2315 | // Is this a link to an image, or to a flickr page ? |
2301 | $imageurl=''; | 2316 | $imageurl=''; |
2302 | if (endswith(parse_url($url,PHP_URL_PATH),'.jpg')) | 2317 | if (endswith(parse_url($url,PHP_URL_PATH),'.jpg')) |
2303 | { // This is a direct link to an image. eg. http://farm1.staticflickr.com/5/5921913_ac83ed27bd_o.jpg | 2318 | { // This is a direct link to an image. e.g. http://farm1.staticflickr.com/5/5921913_ac83ed27bd_o.jpg |
2304 | preg_match('!(http://farm\d+\.staticflickr\.com/\d+/\d+_\w+_)\w.jpg!',$url,$matches); | 2319 | preg_match('!(http://farm\d+\.staticflickr\.com/\d+/\d+_\w+_)\w.jpg!',$url,$matches); |
2305 | if (!empty($matches[1])) $imageurl=$matches[1].'m.jpg'; | 2320 | if (!empty($matches[1])) $imageurl=$matches[1].'m.jpg'; |
2306 | } | 2321 | } |
2307 | else // this is a flickr page (html) | 2322 | else // This is a flickr page (html) |
2308 | { | 2323 | { |
2309 | list($httpstatus,$headers,$data) = getHTTP($url,20); // Get the flickr html page. | 2324 | list($httpstatus,$headers,$data) = getHTTP($url,20); // Get the flickr html page. |
2310 | if (strpos($httpstatus,'200 OK')!==false) | 2325 | if (strpos($httpstatus,'200 OK')!==false) |
2311 | { | 2326 | { |
2312 | // Flickr now nicely provides the URL of the thumbnail in each flickr page. | 2327 | // flickr now nicely provides the URL of the thumbnail in each flickr page. |
2313 | preg_match('!<link rel=\"image_src\" href=\"(.+?)\"!',$data,$matches); | 2328 | preg_match('!<link rel=\"image_src\" href=\"(.+?)\"!',$data,$matches); |
2314 | if (!empty($matches[1])) $imageurl=$matches[1]; | 2329 | if (!empty($matches[1])) $imageurl=$matches[1]; |
2315 | 2330 | ||
@@ -2340,9 +2355,9 @@ function genThumbnail() | |||
2340 | elseif ($domain=='vimeo.com' ) | 2355 | elseif ($domain=='vimeo.com' ) |
2341 | { | 2356 | { |
2342 | // This is more complex: we have to perform a HTTP request, then parse the result. | 2357 | // This is more complex: we have to perform a HTTP request, then parse the result. |
2343 | // Maybe we should deport this to javascript ? Example: http://stackoverflow.com/questions/1361149/get-img-thumbnails-from-vimeo/4285098#4285098 | 2358 | // Maybe we should deport this to JavaScript ? Example: http://stackoverflow.com/questions/1361149/get-img-thumbnails-from-vimeo/4285098#4285098 |
2344 | $vid = substr(parse_url($url,PHP_URL_PATH),1); | 2359 | $vid = substr(parse_url($url,PHP_URL_PATH),1); |
2345 | list($httpstatus,$headers,$data) = getHTTP('http://vimeo.com/api/v2/video/'.htmlspecialchars($vid).'.php',5); | 2360 | list($httpstatus,$headers,$data) = getHTTP('https://vimeo.com/api/v2/video/'.htmlspecialchars($vid).'.php',5); |
2346 | if (strpos($httpstatus,'200 OK')!==false) | 2361 | if (strpos($httpstatus,'200 OK')!==false) |
2347 | { | 2362 | { |
2348 | $t = unserialize($data); | 2363 | $t = unserialize($data); |
@@ -2476,7 +2491,7 @@ function resizeImage($filepath) | |||
2476 | } | 2491 | } |
2477 | 2492 | ||
2478 | // Invalidate caches when the database is changed or the user logs out. | 2493 | // Invalidate caches when the database is changed or the user logs out. |
2479 | // (eg. tags cache). | 2494 | // (e.g. tags cache). |
2480 | function invalidateCaches() | 2495 | function invalidateCaches() |
2481 | { | 2496 | { |
2482 | unset($_SESSION['tags']); // Purge cache attached to session. | 2497 | unset($_SESSION['tags']); // Purge cache attached to session. |