aboutsummaryrefslogtreecommitdiffhomepage
path: root/index.php
diff options
context:
space:
mode:
Diffstat (limited to 'index.php')
-rw-r--r--index.php355
1 files changed, 185 insertions, 170 deletions
diff --git a/index.php b/index.php
index 0465a4e5..1d523a33 100644
--- a/index.php
+++ b/index.php
@@ -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.
35if (is_file($GLOBALS['config']['DATADIR'].'/options.php')) require($GLOBALS['config']['DATADIR'].'/options.php'); 39if (is_file($GLOBALS['config']['DATADIR'].'/options.php')) require($GLOBALS['config']['DATADIR'].'/options.php');
36 40
37define('shaarli_version','0.0.41 beta'); 41define('shaarli_version','0.0.42 beta');
38define('PHPPREFIX','<?php /* '); // Prefix to encapsulate data in php code. 42define('PHPPREFIX','<?php /* '); // Prefix to encapsulate data in PHP code.
39define('PHPSUFFIX',' */ ?>'); // Suffix to encapsulate data in php code. 43define('PHPSUFFIX',' */ ?>'); // Suffix to encapsulate data in PHP code.
44// http://server.com/x/shaarli --> /shaarli/
45define('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.
47define('INACTIVITY_TIMEOUT',3600); // (in seconds). If the user does not access any page within this time, his/her session is considered expired. 53define('INACTIVITY_TIMEOUT',3600); // (in seconds). If the user does not access any page within this time, his/her session is considered expired.
48ini_set('session.use_cookies', 1); // Use cookies to store session. 54ini_set('session.use_cookies', 1); // Use cookies to store session.
49ini_set('session.use_only_cookies', 1); // Force cookies for session (phpsessionID forbidden in URL) 55ini_set('session.use_only_cookies', 1); // Force cookies for session (phpsessionID forbidden in URL).
50ini_set('session.use_trans_sid', false); // Prevent php to use sessionID in URL if cookies are disabled. 56ini_set('session.use_trans_sid', false); // Prevent PHP form using sessionID in URL if cookies are disabled.
51session_name('shaarli'); 57session_name('shaarli');
52if (session_id() == '') session_start(); // Start session if needed (Some server auto-start sessions). 58if (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
63include "inc/rain.tpl.class.php"; //include Rain TPL 69include "inc/rain.tpl.class.php"; //include Rain TPL
64raintpl::$tpl_dir = "tpl/"; // template directory 70raintpl::$tpl_dir = $GLOBALS['config']['RAINTPL_TPL']; // template directory
65if (!is_dir('tmp')) { mkdir('tmp',0705); chmod('tmp',0705); } 71raintpl::$cache_dir = $GLOBALS['config']['RAINTPL_TMP']; // cache directory
66raintpl::$cache_dir = "tmp/"; // cache directory
67 72
68ob_start(); // Output buffering for the page cache. 73ob_start(); // Output buffering for the page cache.
69 74
@@ -83,18 +88,8 @@ header("Cache-Control: no-store, no-cache, must-revalidate");
83header("Cache-Control: post-check=0, pre-check=0", false); 88header("Cache-Control: post-check=0, pre-check=0", false);
84header("Pragma: no-cache"); 89header("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.)
87if (!is_writable(realpath(dirname(__FILE__)))) die('<pre>ERROR: Shaarli does not have the right to write in its own directory ('.realpath(dirname(__FILE__)).').</pre>'); 92if (!is_writable(realpath(dirname(__FILE__)))) die('<pre>ERROR: Shaarli does not have the right to write in its own directory ('.realpath(dirname(__FILE__)).').</pre>');
88if (!is_dir($GLOBALS['config']['DATADIR'])) { mkdir($GLOBALS['config']['DATADIR'],0705); chmod($GLOBALS['config']['DATADIR'],0705); }
89if (!is_dir('tmp')) { mkdir('tmp',0705); chmod('tmp',0705); } // For RainTPL temporary files.
90if (!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.
92if (!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>');
93if ($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.
100if (empty($GLOBALS['title'])) $GLOBALS['title']='Shared links on '.htmlspecialchars(indexUrl()); 95if (empty($GLOBALS['title'])) $GLOBALS['title']='Shared links on '.htmlspecialchars(indexUrl());
@@ -103,6 +98,7 @@ if (empty($GLOBALS['redirector'])) $GLOBALS['redirector']='';
103if (empty($GLOBALS['disablesessionprotection'])) $GLOBALS['disablesessionprotection']=false; 98if (empty($GLOBALS['disablesessionprotection'])) $GLOBALS['disablesessionprotection']=false;
104if (empty($GLOBALS['disablejquery'])) $GLOBALS['disablejquery']=false; 99if (empty($GLOBALS['disablejquery'])) $GLOBALS['disablejquery']=false;
105if (empty($GLOBALS['privateLinkByDefault'])) $GLOBALS['privateLinkByDefault']=false; 100if (empty($GLOBALS['privateLinkByDefault'])) $GLOBALS['privateLinkByDefault']=false;
101if (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
111require $GLOBALS['config']['CONFIG_FILE']; // Read login/password hash into $GLOBALS. 107require $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
110define('STAY_SIGNED_IN_TOKEN', sha1($GLOBALS['hash'].$_SERVER["REMOTE_ADDR"].$GLOBALS['salt']));
113 111
114autoLocale(); // Sniff browser language and set date format accordingly. 112autoLocale(); // Sniff browser language and set date format accordingly.
115header('Content-Type: text/html; charset=utf-8'); // We use UTF-8 for proper international characters handling. 113header('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
118function checkphpversion() 116function 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()
155class pageCache 153class 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('>','&gt;',str_replace('<','&lt;',nl2br($html))); 218 return str_replace('>','&gt;',str_replace('<','&lt;',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)
233function smallHash($text) 230function 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
244function text2clickable($url) 238function text2clickable($url)
245{ 239{
@@ -260,8 +254,8 @@ function keepMultipleSpaces($text)
260function autoLocale() 254function 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
294function 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.
301function check_auth($login,$password) 302function 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.
337function logout() { if (isset($_SESSION)) { unset($_SESSION['uid']); unset($_SESSION['ip']); unset($_SESSION['username']); unset($_SESSION['privateonly']); } } 340function logout() { if (isset($_SESSION)) { unset($_SESSION['uid']); unset($_SESSION['ip']); unset($_SESSION['username']); unset($_SESSION['privateonly']); }
341setcookie('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.
438function serverUrl() 444function 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/)
447function indexUrl() 453function 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)
458function pageUrl() 464function 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.
464function return_bytes($val) 470function 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)
529function linkdate2locale($linkdate) 535function 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.
605function tokenOk($token) 611function 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*/
690class linkdb implements Iterator, Countable, ArrayAccess 697class 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.
880function showRSS() 887function 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.
954function showATOM() 961function 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.
1991function thumbnail($url,$href=false) 2003function 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.
2012function lazyThumbnail($url,$href=false) 2024function 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)
2106function templateTZform($ptz=false) 2120function 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)
2152function isTZvalid($continent,$city) 2166function 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
2201function processWS() 2215function 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.)
2242function writeConfig() 2256function 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).
2480function invalidateCaches() 2495function invalidateCaches()
2481{ 2496{
2482 unset($_SESSION['tags']); // Purge cache attached to session. 2497 unset($_SESSION['tags']); // Purge cache attached to session.