X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=index.php;h=e50c0181ae3d9146cefe62718008b1421d83940f;hb=f7762cf803f03f5caf4b8078359a63783d0090c1;hp=35e83eb54498613497c9f926939e38dc2eab6edd;hpb=aedc912d36153b131b3e3bc8a9813c104440aa4b;p=github%2Fshaarli%2FShaarli.git
diff --git a/index.php b/index.php
index 35e83eb5..1439ec2f 100644
--- a/index.php
+++ b/index.php
@@ -1,9 +1,9 @@
'); // Suffix to encapsulate data in PHP code.
+define('shaarli_version','0.5.0');
// http://server.com/x/shaarli --> /shaarli/
define('WEB_PATH', substr($_SERVER["REQUEST_URI"], 0, 1+strrpos($_SERVER["REQUEST_URI"], '/', 0)));
// Force cookie path (but do not change lifetime)
$cookie=session_get_cookie_params();
$cookiedir = ''; if(dirname($_SERVER['SCRIPT_NAME'])!='/') $cookiedir=dirname($_SERVER["SCRIPT_NAME"]).'/';
-session_set_cookie_params($cookie['lifetime'],$cookiedir,$_SERVER['HTTP_HOST']); // Set default cookie expiration and path.
+session_set_cookie_params($cookie['lifetime'],$cookiedir,$_SERVER['SERVER_NAME']); // Set default cookie expiration and path.
// Set session parameters on server side.
define('INACTIVITY_TIMEOUT',3600); // (in seconds). If the user does not access any page within this time, his/her session is considered expired.
@@ -62,10 +59,29 @@ ini_set('max_input_time','60'); // High execution time in case of problematic i
ini_set('memory_limit', '128M'); // Try to set max upload file size and read (May not work on some hosts).
ini_set('post_max_size', '16M');
ini_set('upload_max_filesize', '16M');
-checkphpversion();
error_reporting(E_ALL^E_WARNING); // See all error except warnings.
//error_reporting(-1); // See all errors (for debugging only)
+// User configuration
+if (is_file($GLOBALS['config']['CONFIG_FILE'])) {
+ require_once $GLOBALS['config']['CONFIG_FILE'];
+}
+
+// Shaarli library
+require_once 'application/LinkDB.php';
+require_once 'application/TimeZone.php';
+require_once 'application/Utils.php';
+require_once 'application/Config.php';
+
+// Ensure the PHP version is supported
+try {
+ checkPHPVersion('5.3', PHP_VERSION);
+} catch(Exception $e) {
+ header('Content-Type: text/plain; charset=utf-8');
+ echo $e->getMessage();
+ exit;
+}
+
include "inc/rain.tpl.class.php"; //include Rain TPL
raintpl::$tpl_dir = $GLOBALS['config']['RAINTPL_TPL']; // template directory
raintpl::$cache_dir = $GLOBALS['config']['RAINTPL_TMP']; // cache directory
@@ -89,22 +105,25 @@ header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
// Directories creations (Note that your web host may require different rights than 705.)
-if (!is_writable(realpath(dirname(__FILE__)))) die('
ERROR: Shaarli does not have the right to write in its own directory ('.realpath(dirname(__FILE__)).'). ');
+if (!is_writable(realpath(dirname(__FILE__)))) die('ERROR: Shaarli does not have the right to write in its own directory. ');
// Handling of old config file which do not have the new parameters.
-if (empty($GLOBALS['title'])) $GLOBALS['title']='Shared links on '.htmlspecialchars(indexUrl());
+if (empty($GLOBALS['title'])) $GLOBALS['title']='Shared links on '.escape(indexUrl());
if (empty($GLOBALS['timezone'])) $GLOBALS['timezone']=date_default_timezone_get();
if (empty($GLOBALS['redirector'])) $GLOBALS['redirector']='';
if (empty($GLOBALS['disablesessionprotection'])) $GLOBALS['disablesessionprotection']=false;
-if (empty($GLOBALS['disablejquery'])) $GLOBALS['disablejquery']=false;
if (empty($GLOBALS['privateLinkByDefault'])) $GLOBALS['privateLinkByDefault']=false;
if (empty($GLOBALS['titleLink'])) $GLOBALS['titleLink']='?';
// I really need to rewrite Shaarli with a proper configuation manager.
// Run config screen if first run:
-if (!is_file($GLOBALS['config']['CONFIG_FILE'])) install();
+if (! is_file($GLOBALS['config']['CONFIG_FILE'])) {
+ install();
+}
-require $GLOBALS['config']['CONFIG_FILE']; // Read login/password hash into $GLOBALS.
+$GLOBALS['title'] = !empty($GLOBALS['title']) ? escape($GLOBALS['title']) : '';
+$GLOBALS['titleLink'] = !empty($GLOBALS['titleLink']) ? escape($GLOBALS['titleLink']) : '';
+$GLOBALS['redirector'] = !empty($GLOBALS['redirector']) ? escape($GLOBALS['redirector']) : '';
// a token depending of deployment salt, user password, and the current ip
define('STAY_SIGNED_IN_TOKEN', sha1($GLOBALS['hash'].$_SERVER["REMOTE_ADDR"].$GLOBALS['salt']));
@@ -112,16 +131,49 @@ define('STAY_SIGNED_IN_TOKEN', sha1($GLOBALS['hash'].$_SERVER["REMOTE_ADDR"].$GL
autoLocale(); // Sniff browser language and set date format accordingly.
header('Content-Type: text/html; charset=utf-8'); // We use UTF-8 for proper international characters handling.
-// Check PHP version
-function checkphpversion()
-{
- if (version_compare(PHP_VERSION, '5.1.0') < 0)
- {
- header('Content-Type: text/plain; charset=utf-8');
- echo 'Your server supports PHP '.PHP_VERSION.'. Shaarli requires at least php 5.1.0, and thus cannot run. Sorry.';
- exit;
- }
+//==================================================================================================
+// Checking session state (i.e. is the user still logged in)
+//==================================================================================================
+
+function setup_login_state() {
+ $userIsLoggedIn = false; // By default, we do not consider the user as logged in;
+ $loginFailure = false; // If set to true, every attempt to authenticate the user will fail. This indicates that an important condition isn't met.
+ if ($GLOBALS['config']['OPEN_SHAARLI']) {
+ $userIsLoggedIn = true;
+ }
+ if (!isset($GLOBALS['login'])) {
+ $userIsLoggedIn = false; // Shaarli is not configured yet.
+ $loginFailure = true;
+ }
+ if (isset($_COOKIE['shaarli_staySignedIn']) &&
+ $_COOKIE['shaarli_staySignedIn']===STAY_SIGNED_IN_TOKEN &&
+ !$loginFailure)
+ {
+ fillSessionInfo();
+ $userIsLoggedIn = true;
+ }
+ // If session does not exist on server side, or IP address has changed, or session has expired, logout.
+ if (empty($_SESSION['uid']) ||
+ ($GLOBALS['disablesessionprotection']==false && $_SESSION['ip']!=allIPs()) ||
+ time() >= $_SESSION['expires_on'])
+ {
+ logout();
+ $userIsLoggedIn = false;
+ $loginFailure = true;
+ }
+ if (!empty($_SESSION['longlastingsession'])) {
+ $_SESSION['expires_on']=time()+$_SESSION['longlastingsession']; // In case of "Stay signed in" checked.
+ }
+ else {
+ $_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT; // Standard session expiration date.
+ }
+ if (!$loginFailure) {
+ $userIsLoggedIn = true;
+ }
+
+ return $userIsLoggedIn;
}
+$userIsLoggedIn = setup_login_state();
// Checks if an update is available for Shaarli.
// (at most once a day, and only for registered user.)
@@ -130,13 +182,14 @@ function checkphpversion()
function checkUpdate()
{
if (!isLoggedIn()) return ''; // Do not check versions for visitors.
+ if (empty($GLOBALS['config']['ENABLE_UPDATECHECK'])) return ''; // Do not check if the user doesn't want to.
// Get latest version number at most once a day.
if (!is_file($GLOBALS['config']['UPDATECHECK_FILENAME']) || (filemtime($GLOBALS['config']['UPDATECHECK_FILENAME'])','',str_replace('
-function nl2br_escaped($html)
-{
- return str_replace('>','>',str_replace('<','<',nl2br($html)));
-}
-
-/* Returns the small hash of a string, using RFC 4648 base64url format
- e.g. smallHash('20111006_131924') --> yZH23w
- Small hashes:
- - are unique (well, as unique as crc32, at last)
- - are always 6 characters long.
- - only use the following characters: a-z A-Z 0-9 - _ @
- - are NOT cryptographically secure (they CAN be forged)
- In Shaarli, they are used as a tinyurl-like link to individual entries.
-*/
-function smallHash($text)
-{
- $t = rtrim(base64_encode(hash('crc32',$text,true)),'=');
- return strtr($t, '+/', '-_');
-}
-
// In a string, converts URLs to clickable links.
// Function inspired from http://www.php.net/manual/en/function.preg-replace.php#85722
function text2clickable($url)
@@ -253,12 +285,18 @@ function keepMultipleSpaces($text)
// (Note that is may not work on your server if the corresponding local is not installed.)
function autoLocale()
{
- $loc='en_US'; // Default if browser does not send HTTP_ACCEPT_LANGUAGE
+ $attempts = array('en_US'); // Default if browser does not send HTTP_ACCEPT_LANGUAGE
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) // e.g. "fr,fr-fr;q=0.8,en;q=0.5,en-us;q=0.3"
{ // (It's a bit crude, but it works very well. Preferred language is always presented first.)
- if (preg_match('/([a-z]{2}(-[a-z]{2})?)/i',$_SERVER['HTTP_ACCEPT_LANGUAGE'],$matches)) $loc=$matches[1];
+ if (preg_match('/([a-z]{2})-?([a-z]{2})?/i',$_SERVER['HTTP_ACCEPT_LANGUAGE'],$matches)) {
+ $loc = $matches[1] . (!empty($matches[2]) ? '_' . strtoupper($matches[2]) : '');
+ $attempts = array($loc.'.UTF-8', $loc, str_replace('_', '-', $loc).'.UTF-8', str_replace('_', '-', $loc),
+ $loc . '_' . strtoupper($loc).'.UTF-8', $loc . '_' . strtoupper($loc),
+ $loc . '_' . $loc.'.UTF-8', $loc . '_' . $loc, $loc . '-' . strtoupper($loc).'.UTF-8',
+ $loc . '-' . strtoupper($loc), $loc . '-' . $loc.'.UTF-8', $loc . '-' . $loc);
+ }
}
- setlocale(LC_TIME,$loc); // LC_TIME = Set local for date/time format only.
+ setlocale(LC_TIME, $attempts); // LC_TIME = Set local for date/time format only.
}
// ------------------------------------------------------------------------------------------
@@ -315,30 +353,19 @@ function check_auth($login,$password)
// Returns true if the user is logged in.
function isLoggedIn()
{
- if ($GLOBALS['config']['OPEN_SHAARLI']) return true;
-
- if (!isset($GLOBALS['login'])) return false; // Shaarli is not configured yet.
-
- if (@$_COOKIE['shaarli_staySignedIn']===STAY_SIGNED_IN_TOKEN)
- {
- fillSessionInfo();
- return true;
- }
- // If session does not exist on server side, or IP address has changed, or session has expired, logout.
- if (empty($_SESSION['uid']) || ($GLOBALS['disablesessionprotection']==false && $_SESSION['ip']!=allIPs()) || time()>=$_SESSION['expires_on'])
- {
- logout();
- return false;
- }
- if (!empty($_SESSION['longlastingsession'])) $_SESSION['expires_on']=time()+$_SESSION['longlastingsession']; // In case of "Stay signed in" checked.
- else $_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT; // Standard session expiration date.
-
- return true;
+ global $userIsLoggedIn;
+ return $userIsLoggedIn;
}
// Force logout.
-function logout() { if (isset($_SESSION)) { unset($_SESSION['uid']); unset($_SESSION['ip']); unset($_SESSION['username']); unset($_SESSION['privateonly']); }
-setcookie('shaarli_staySignedIn', FALSE, 0, WEB_PATH);
+function logout() {
+ if (isset($_SESSION)) {
+ unset($_SESSION['uid']);
+ unset($_SESSION['ip']);
+ unset($_SESSION['username']);
+ unset($_SESSION['privateonly']);
+ }
+ setcookie('shaarli_staySignedIn', FALSE, 0, WEB_PATH);
}
@@ -406,14 +433,14 @@ if (isset($_POST['login']))
$_SESSION['expires_on']=time()+$_SESSION['longlastingsession']; // Set session expiration on server-side.
$cookiedir = ''; if(dirname($_SERVER['SCRIPT_NAME'])!='/') $cookiedir=dirname($_SERVER["SCRIPT_NAME"]).'/';
- session_set_cookie_params($_SESSION['longlastingsession'],$cookiedir,$_SERVER['HTTP_HOST']); // Set session cookie expiration on client side
+ session_set_cookie_params($_SESSION['longlastingsession'],$cookiedir,$_SERVER['SERVER_NAME']); // Set session cookie expiration on client side
// Note: Never forget the trailing slash on the cookie path!
session_regenerate_id(true); // Send cookie with new expiration date to browser.
}
else // Standard session expiration (=when browser closes)
{
$cookiedir = ''; if(dirname($_SERVER['SCRIPT_NAME'])!='/') $cookiedir=dirname($_SERVER["SCRIPT_NAME"]).'/';
- session_set_cookie_params(0,$cookiedir,$_SERVER['HTTP_HOST']); // 0 means "When browser closes"
+ session_set_cookie_params(0,$cookiedir,$_SERVER['SERVER_NAME']); // 0 means "When browser closes"
session_regenerate_id(true);
}
// Optional redirect after login:
@@ -430,7 +457,7 @@ if (isset($_POST['login']))
ban_loginFailed();
$redir = '';
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']):''); }
- echo ''; // Redirect to login screen.
+ echo ''; // Redirect to login screen.
exit;
}
}
@@ -445,7 +472,7 @@ function serverUrl()
{
$https = (!empty($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS'])=='on')) || $_SERVER["SERVER_PORT"]=='443'; // HTTPS detection.
$serverport = ($_SERVER["SERVER_PORT"]=='80' || ($https && $_SERVER["SERVER_PORT"]=='443') ? '' : ':'.$_SERVER["SERVER_PORT"]);
- return 'http'.($https?'s':'').'://'.$_SERVER['HTTP_HOST'].$serverport;
+ return 'http'.($https?'s':'').'://'.$_SERVER['SERVER_NAME'].$serverport;
}
// Returns the absolute URL of current script, without the query.
@@ -491,28 +518,17 @@ function getMaxFileSize()
return $maxsize;
}
-// Tells if a string start with a substring or not.
-function startsWith($haystack,$needle,$case=true)
-{
- if($case){return (strcmp(substr($haystack, 0, strlen($needle)),$needle)===0);}
- return (strcasecmp(substr($haystack, 0, strlen($needle)),$needle)===0);
-}
-
-// Tells if a string ends with a substring or not.
-function endsWith($haystack,$needle,$case=true)
-{
- if($case){return (strcmp(substr($haystack, strlen($haystack) - strlen($needle)),$needle)===0);}
- return (strcasecmp(substr($haystack, strlen($haystack) - strlen($needle)),$needle)===0);
-}
-
/* Converts a linkdate time (YYYYMMDD_HHMMSS) of an article to a timestamp (Unix epoch)
(used to build the ADD_DATE attribute in Netscape-bookmarks file)
PS: I could have used strptime(), but it does not exist on Windows. I'm too kind. */
function linkdate2timestamp($linkdate)
{
- $Y=$M=$D=$h=$m=$s=0;
- $r = sscanf($linkdate,'%4d%2d%2d_%2d%2d%2d',$Y,$M,$D,$h,$m,$s);
- return mktime($h,$m,$s,$M,$D,$Y);
+ if(strcmp($linkdate, '_000000') !== 0 || !$linkdate){
+ $Y=$M=$D=$h=$m=$s=0;
+ $r = sscanf($linkdate,'%4d%2d%2d_%2d%2d%2d',$Y,$M,$D,$h,$m,$s);
+ return mktime($h,$m,$s,$M,$D,$Y);
+ }
+ return time();
}
/* Converts a linkdate time (YYYYMMDD_HHMMSS) of an article to a RFC822 date.
@@ -529,16 +545,6 @@ function linkdate2iso8601($linkdate)
return date('c',linkdate2timestamp($linkdate)); // 'c' is for ISO 8601 date format.
}
-/* Converts a linkdate time (YYYYMMDD_HHMMSS) of an article to a localized date format.
- (used to display link date on screen)
- The date format is automatically chosen according to locale/languages sniffed from browser headers (see autoLocale()). */
-function linkdate2locale($linkdate)
-{
- return utf8_encode(strftime('%c',linkdate2timestamp($linkdate))); // %c is for automatic date format according to locale.
- // Note that if you use a locale which is not installed on your webserver,
- // the date will not be displayed in the chosen locale, but probably in US notation.
-}
-
// Parse HTTP response headers and return an associative array.
function http_parse_headers_shaarli( $headers )
{
@@ -638,8 +644,8 @@ class pageBuilder
private function initialize()
{
$this->tpl = new RainTPL;
- $this->tpl->assign('newversion',checkUpdate());
- $this->tpl->assign('feedurl',htmlspecialchars(indexUrl()));
+ $this->tpl->assign('newversion',escape(checkUpdate()));
+ $this->tpl->assign('feedurl',escape(indexUrl()));
$searchcrits=''; // Search criteria
if (!empty($_GET['searchtags'])) $searchcrits.='&searchtags='.urlencode($_GET['searchtags']);
elseif (!empty($_GET['searchterm'])) $searchcrits.='&searchterm='.urlencode($_GET['searchterm']);
@@ -672,216 +678,6 @@ class pageBuilder
}
}
-// ------------------------------------------------------------------------------------------
-/* Data storage for links.
- This object behaves like an associative array.
- Example:
- $mylinks = new linkdb();
- echo $mylinks['20110826_161819']['title'];
- foreach($mylinks as $link)
- echo $link['title'].' at url '.$link['url'].' ; description:'.$link['description'];
-
- Available keys:
- title : Title of the link
- url : URL of the link. Can be absolute or relative. Relative URLs are permalinks (e.g.'?m-ukcw')
- description : description of the entry
- private : Is this link private? 0=no, other value=yes
- linkdate : date of the creation of this entry, in the form YYYYMMDD_HHMMSS (e.g.'20110914_192317')
- tags : tags attached to this entry (separated by spaces)
-
- We implement 3 interfaces:
- - ArrayAccess so that this object behaves like an associative array.
- - Iterator so that this object can be used in foreach() loops.
- - Countable interface so that we can do a count() on this object.
-*/
-class linkdb implements Iterator, Countable, ArrayAccess
-{
- private $links; // List of links (associative array. Key=linkdate (e.g. "20110823_124546"), value= associative array (keys:title,description...)
- private $urls; // List of all recorded URLs (key=url, value=linkdate) for fast reserve search (url-->linkdate)
- private $keys; // List of linkdate keys (for the Iterator interface implementation)
- private $position; // Position in the $this->keys array. (for the Iterator interface implementation.)
- private $loggedin; // Is the user logged in? (used to filter private links)
-
- // Constructor:
- function __construct($isLoggedIn)
- // Input : $isLoggedIn : is the user logged in?
- {
- $this->loggedin = $isLoggedIn;
- $this->checkdb(); // Make sure data file exists.
- $this->readdb(); // Then read it.
- }
-
- // ---- Countable interface implementation
- public function count() { return count($this->links); }
-
- // ---- ArrayAccess interface implementation
- public function offsetSet($offset, $value)
- {
- if (!$this->loggedin) die('You are not authorized to add a link.');
- if (empty($value['linkdate']) || empty($value['url'])) die('Internal Error: A link should always have a linkdate and URL.');
- if (empty($offset)) die('You must specify a key.');
- $this->links[$offset] = $value;
- $this->urls[$value['url']]=$offset;
- }
- public function offsetExists($offset) { return array_key_exists($offset,$this->links); }
- public function offsetUnset($offset)
- {
- if (!$this->loggedin) die('You are not authorized to delete a link.');
- $url = $this->links[$offset]['url']; unset($this->urls[$url]);
- unset($this->links[$offset]);
- }
- public function offsetGet($offset) { return isset($this->links[$offset]) ? $this->links[$offset] : null; }
-
- // ---- Iterator interface implementation
- function rewind() { $this->keys=array_keys($this->links); rsort($this->keys); $this->position=0; } // Start over for iteration, ordered by date (latest first).
- function key() { return $this->keys[$this->position]; } // current key
- function current() { return $this->links[$this->keys[$this->position]]; } // current value
- function next() { ++$this->position; } // go to next item
- function valid() { return isset($this->keys[$this->position]); } // Check if current position is valid.
-
- // ---- Misc methods
- private function checkdb() // Check if db directory and file exists.
- {
- if (!file_exists($GLOBALS['config']['DATASTORE'])) // Create a dummy database for example.
- {
- $this->links = array();
- $link = array('title'=>'Shaarli - sebsauvage.net','url'=>'http://sebsauvage.net/wiki/doku.php?id=php:shaarli','description'=>'Welcome to Shaarli ! This is a bookmark. To edit or delete me, you must first login.','private'=>0,'linkdate'=>'20110914_190000','tags'=>'opensource software');
- $this->links[$link['linkdate']] = $link;
- $link = array('title'=>'My secret stuff... - Pastebin.com','url'=>'http://sebsauvage.net/paste/?8434b27936c09649#bR7XsXhoTiLcqCpQbmOpBi3rq2zzQUC5hBI7ZT1O3x8=','description'=>'SShhhh!! I\'m a private link only YOU can see. You can delete me too.','private'=>1,'linkdate'=>'20110914_074522','tags'=>'secretstuff');
- $this->links[$link['linkdate']] = $link;
- file_put_contents($GLOBALS['config']['DATASTORE'], PHPPREFIX.base64_encode(gzdeflate(serialize($this->links))).PHPSUFFIX); // Write database to disk
- }
- }
-
- // Read database from disk to memory
- private function readdb()
- {
- // Read data
- $this->links=(file_exists($GLOBALS['config']['DATASTORE']) ? unserialize(gzinflate(base64_decode(substr(file_get_contents($GLOBALS['config']['DATASTORE']),strlen(PHPPREFIX),-strlen(PHPSUFFIX))))) : array() );
- // Note that gzinflate is faster than gzuncompress. See: http://www.php.net/manual/en/function.gzdeflate.php#96439
-
- // If user is not logged in, filter private links.
- if (!$this->loggedin)
- {
- $toremove=array();
- foreach($this->links as $link) { if ($link['private']!=0) $toremove[]=$link['linkdate']; }
- foreach($toremove as $linkdate) { unset($this->links[$linkdate]); }
- }
-
- // Keep the list of the mapping URLs-->linkdate up-to-date.
- $this->urls=array();
- foreach($this->links as $link) { $this->urls[$link['url']]=$link['linkdate']; }
- }
-
- // Save database from memory to disk.
- public function savedb()
- {
- if (!$this->loggedin) die('You are not authorized to change the database.');
- file_put_contents($GLOBALS['config']['DATASTORE'], PHPPREFIX.base64_encode(gzdeflate(serialize($this->links))).PHPSUFFIX);
- invalidateCaches();
- }
-
- // Returns the link for a given URL (if it exists). False if it does not exist.
- public function getLinkFromUrl($url)
- {
- if (isset($this->urls[$url])) return $this->links[$this->urls[$url]];
- return false;
- }
-
- // Case insensitive search among links (in the URLs, title and description). Returns filtered list of links.
- // e.g. print_r($mydb->filterFulltext('hollandais'));
- public function filterFulltext($searchterms)
- {
- // FIXME: explode(' ',$searchterms) and perform a AND search.
- // FIXME: accept double-quotes to search for a string "as is"?
- $filtered=array();
- $s = strtolower($searchterms);
- foreach($this->links as $l)
- {
- $found= (strpos(strtolower($l['title']),$s)!==false)
- || (strpos(strtolower($l['description']),$s)!==false)
- || (strpos(strtolower($l['url']),$s)!==false)
- || (strpos(strtolower($l['tags']),$s)!==false);
- if ($found) $filtered[$l['linkdate']] = $l;
- }
- krsort($filtered);
- return $filtered;
- }
-
- // Filter by tag.
- // You can specify one or more tags (tags can be separated by space or comma).
- // e.g. print_r($mydb->filterTags('linux programming'));
- public function filterTags($tags,$casesensitive=false)
- {
- $t = str_replace(',',' ',($casesensitive?$tags:strtolower($tags)));
- $searchtags=explode(' ',$t);
- $filtered=array();
- foreach($this->links as $l)
- {
- $linktags = explode(' ',($casesensitive?$l['tags']:strtolower($l['tags'])));
- if (count(array_intersect($linktags,$searchtags)) == count($searchtags))
- $filtered[$l['linkdate']] = $l;
- }
- krsort($filtered);
- return $filtered;
- }
-
- // Filter by day. Day must be in the form 'YYYYMMDD' (e.g. '20120125')
- // Sort order is: older articles first.
- // e.g. print_r($mydb->filterDay('20120125'));
- public function filterDay($day)
- {
- $filtered=array();
- foreach($this->links as $l)
- {
- if (startsWith($l['linkdate'],$day)) $filtered[$l['linkdate']] = $l;
- }
- ksort($filtered);
- return $filtered;
- }
- // Filter by smallHash.
- // Only 1 article is returned.
- public function filterSmallHash($smallHash)
- {
- $filtered=array();
- foreach($this->links as $l)
- {
- if ($smallHash==smallHash($l['linkdate'])) // Yes, this is ugly and slow
- {
- $filtered[$l['linkdate']] = $l;
- return $filtered;
- }
- }
- return $filtered;
- }
-
- // Returns the list of all tags
- // Output: associative array key=tags, value=0
- public function allTags()
- {
- $tags=array();
- foreach($this->links as $link)
- foreach(explode(' ',$link['tags']) as $tag)
- if (!empty($tag)) $tags[$tag]=(empty($tags[$tag]) ? 1 : $tags[$tag]+1);
- arsort($tags); // Sort tags by usage (most used tag first)
- return $tags;
- }
-
- // Returns the list of days containing articles (oldest first)
- // Output: An array containing days (in format YYYYMMDD).
- public function days()
- {
- $linkdays=array();
- foreach(array_keys($this->links) as $day)
- {
- $linkdays[substr($day,0,8)]=0;
- }
- $linkdays=array_keys($linkdays);
- sort($linkdays);
- return $linkdays;
- }
-}
-
// ------------------------------------------------------------------------------------------
// Output the last N links in RSS 2.0 format.
function showRSS()
@@ -890,7 +686,8 @@ function showRSS()
// $usepermalink : If true, use permalink instead of final link.
// User just has to add 'permalink' in URL parameters. e.g. http://mysite.com/shaarli/?do=rss&permalinks
- $usepermalinks = isset($_GET['permalinks']);
+ // Also enabled through a config option
+ $usepermalinks = isset($_GET['permalinks']) || !$GLOBALS['config']['ENABLE_RSS_PERMALINKS'];
// Cache system
$query = $_SERVER["QUERY_STRING"];
@@ -898,28 +695,34 @@ function showRSS()
$cached = $cache->cachedVersion(); if (!empty($cached)) { echo $cached; exit; }
// If cached was not found (or not usable), then read the database and build the response:
- $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if user it not logged in).
+ $LINKSDB = new LinkDB(
+ $GLOBALS['config']['DATASTORE'],
+ isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI'],
+ $GLOBALS['config']['HIDE_PUBLIC_LINKS']
+ );
+ // Read links from database (and filter private links if user it not logged in).
// Optionally filter the results:
$linksToDisplay=array();
if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']);
- elseif (!empty($_GET['searchtags'])) $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags']));
+ else if (!empty($_GET['searchtags'])) $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags']));
else $linksToDisplay = $LINKSDB;
+
$nblinksToDisplay = 50; // Number of links to display.
if (!empty($_GET['nb'])) // In URL, you can specificy the number of links. Example: nb=200 or nb=all for all links.
{
$nblinksToDisplay = $_GET['nb']=='all' ? count($linksToDisplay) : max($_GET['nb']+0,1) ;
}
- $pageaddr=htmlspecialchars(indexUrl());
+ $pageaddr=escape(indexUrl());
echo '';
- echo ''.htmlspecialchars($GLOBALS['title']).' '.$pageaddr.'';
+ echo ''.$GLOBALS['title'].' '.$pageaddr.'';
echo 'Shared links en-en '.$pageaddr.' '."\n\n";
if (!empty($GLOBALS['config']['PUBSUBHUB_URL']))
{
echo '';
- echo ' ';
- echo ' ';
+ echo ' ';
+ echo ' ';
echo '';
}
$i=0;
@@ -929,16 +732,16 @@ function showRSS()
$link = $linksToDisplay[$keys[$i]];
$guid = $pageaddr.'?'.smallHash($link['linkdate']);
$rfc822date = linkdate2rfc822($link['linkdate']);
- $absurl = htmlspecialchars($link['url']);
+ $absurl = $link['url'];
if (startsWith($absurl,'?')) $absurl=$pageaddr.$absurl; // make permalink URL absolute
if ($usepermalinks===true)
- echo ''.htmlspecialchars($link['title']).' '.$guid.' '.$guid.'';
+ echo ''.$link['title'].' '.$guid.' '.$guid.'';
else
- echo ''.htmlspecialchars($link['title']).' '.$guid.' '.$absurl.'';
- if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) echo ''.htmlspecialchars($rfc822date)." \n";
+ echo ''.$link['title'].' '.$guid.' '.$absurl.'';
+ if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) echo ''.escape($rfc822date)." \n";
if ($link['tags']!='') // Adding tags to each RSS entry (as mentioned in RSS specification)
{
- foreach(explode(' ',$link['tags']) as $tag) { echo ''.htmlspecialchars($tag).' '."\n"; }
+ foreach(explode(' ',$link['tags']) as $tag) { echo ''.$tag.' '."\n"; }
}
// Add permalink in description
@@ -946,10 +749,10 @@ function showRSS()
// If user wants permalinks first, put the final link in description
if ($usepermalinks===true) $descriptionlink = '(Link )';
if (strlen($link['description'])>0) $descriptionlink = ' '.$descriptionlink;
- echo ' '."\n \n";
+ echo ' '."\n \n";
$i++;
}
- echo ' ';
+ echo '';
$cache->cache(ob_get_contents());
ob_end_flush();
@@ -964,7 +767,7 @@ function showATOM()
// $usepermalink : If true, use permalink instead of final link.
// User just has to add 'permalink' in URL parameters. e.g. http://mysite.com/shaarli/?do=atom&permalinks
- $usepermalinks = isset($_GET['permalinks']);
+ $usepermalinks = isset($_GET['permalinks']) || !$GLOBALS['config']['ENABLE_RSS_PERMALINKS'];
// Cache system
$query = $_SERVER["QUERY_STRING"];
@@ -972,21 +775,26 @@ function showATOM()
$cached = $cache->cachedVersion(); if (!empty($cached)) { echo $cached; exit; }
// If cached was not found (or not usable), then read the database and build the response:
- $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in).
-
+// Read links from database (and filter private links if used it not logged in).
+ $LINKSDB = new LinkDB(
+ $GLOBALS['config']['DATASTORE'],
+ isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI'],
+ $GLOBALS['config']['HIDE_PUBLIC_LINKS']
+ );
// Optionally filter the results:
$linksToDisplay=array();
if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']);
- elseif (!empty($_GET['searchtags'])) $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags']));
+ else if (!empty($_GET['searchtags'])) $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags']));
else $linksToDisplay = $LINKSDB;
+
$nblinksToDisplay = 50; // Number of links to display.
if (!empty($_GET['nb'])) // In URL, you can specificy the number of links. Example: nb=200 or nb=all for all links.
{
$nblinksToDisplay = $_GET['nb']=='all' ? count($linksToDisplay) : max($_GET['nb']+0,1) ;
}
- $pageaddr=htmlspecialchars(indexUrl());
+ $pageaddr=escape(indexUrl());
$latestDate = '';
$entries='';
$i=0;
@@ -997,44 +805,44 @@ function showATOM()
$guid = $pageaddr.'?'.smallHash($link['linkdate']);
$iso8601date = linkdate2iso8601($link['linkdate']);
$latestDate = max($latestDate,$iso8601date);
- $absurl = htmlspecialchars($link['url']);
+ $absurl = $link['url'];
if (startsWith($absurl,'?')) $absurl=$pageaddr.$absurl; // make permalink URL absolute
- $entries.=''.htmlspecialchars($link['title']).' ';
+ $entries.=''.$link['title'].' ';
if ($usepermalinks===true)
$entries.=''.$guid.' ';
else
$entries.=''.$guid.' ';
- if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) $entries.=''.htmlspecialchars($iso8601date).' ';
+ if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) $entries.=''.escape($iso8601date).' ';
// Add permalink in description
- $descriptionlink = htmlspecialchars('(Permalink )');
+ $descriptionlink = '(Permalink )';
// If user wants permalinks first, put the final link in description
- if ($usepermalinks===true) $descriptionlink = htmlspecialchars('(Link )');
- if (strlen($link['description'])>0) $descriptionlink = '<br>'.$descriptionlink;
+ if ($usepermalinks===true) $descriptionlink = '(Link )';
+ if (strlen($link['description'])>0) $descriptionlink = ' '.$descriptionlink;
- $entries.=''.htmlspecialchars(nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description']))))).$descriptionlink." \n";
+ $entries.=' \n";
if ($link['tags']!='') // Adding tags to each ATOM entry (as mentioned in ATOM specification)
{
foreach(explode(' ',$link['tags']) as $tag)
- { $entries.=' '."\n"; }
+ { $entries.=' '."\n"; }
}
$entries.=" \n";
$i++;
}
$feed='';
- $feed.=''.htmlspecialchars($GLOBALS['title']).' ';
- if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) $feed.=''.htmlspecialchars($latestDate).' ';
- $feed.=' ';
+ $feed.=''.$GLOBALS['title'].' ';
+ if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) $feed.=''.escape($latestDate).' ';
+ $feed.=' ';
if (!empty($GLOBALS['config']['PUBSUBHUB_URL']))
{
$feed.='';
- $feed.=' ';
+ $feed.=' ';
$feed.='';
}
- $feed.=''.htmlspecialchars($pageaddr).' '.htmlspecialchars($pageaddr).' ';
- $feed.=''.htmlspecialchars($pageaddr).' '."\n\n"; // Yes, I know I should use a real IRI (RFC3987), but the site URL will do.
+ $feed.=''.$pageaddr.' '.$pageaddr.' ';
+ $feed.=''.$pageaddr.' '."\n\n"; // Yes, I know I should use a real IRI (RFC3987), but the site URL will do.
$feed.=$entries;
- $feed.=' ';
+ $feed.='';
echo $feed;
$cache->cache(ob_get_contents());
@@ -1046,72 +854,96 @@ function showATOM()
// Daily RSS feed: 1 RSS entry per day giving all the links on that day.
// Gives the last 7 days (which have links).
// This RSS feed cannot be filtered.
-function showDailyRSS()
-{
+function showDailyRSS() {
// Cache system
$query = $_SERVER["QUERY_STRING"];
- $cache = new pageCache(pageUrl(),startsWith($query,'do=dailyrss') && !isLoggedIn());
- $cached = $cache->cachedVersion(); if (!empty($cached)) { echo $cached; exit; }
+ $cache = new pageCache(pageUrl(), startsWith($query, 'do=dailyrss') && !isLoggedIn());
+ $cached = $cache->cachedVersion();
+ if (!empty($cached)) {
+ echo $cached;
+ exit;
+ }
+
// If cached was not found (or not usable), then read the database and build the response:
- $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in).
+ // Read links from database (and filter private links if used it not logged in).
+ $LINKSDB = new LinkDB(
+ $GLOBALS['config']['DATASTORE'],
+ isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI'],
+ $GLOBALS['config']['HIDE_PUBLIC_LINKS']
+ );
/* Some Shaarlies may have very few links, so we need to look
back in time (rsort()) until we have enough days ($nb_of_days).
*/
- $linkdates=array(); foreach($LINKSDB as $linkdate=>$value) { $linkdates[]=$linkdate; }
+ $linkdates = array();
+ foreach ($LINKSDB as $linkdate => $value) {
+ $linkdates[] = $linkdate;
+ }
rsort($linkdates);
- $nb_of_days=7; // We take 7 days.
- $today=Date('Ymd');
- $days=array();
- foreach($linkdates as $linkdate)
- {
- $day=substr($linkdate,0,8); // Extract day (without time)
- if (strcmp($day,$today)<0)
- {
- if (empty($days[$day])) $days[$day]=array();
- $days[$day][]=$linkdate;
+ $nb_of_days = 7; // We take 7 days.
+ $today = Date('Ymd');
+ $days = array();
+
+ foreach ($linkdates as $linkdate) {
+ $day = substr($linkdate, 0, 8); // Extract day (without time)
+ if (strcmp($day,$today) < 0) {
+ if (empty($days[$day])) {
+ $days[$day] = array();
+ }
+ $days[$day][] = $linkdate;
+ }
+
+ if (count($days) > $nb_of_days) {
+ break; // Have we collected enough days?
}
- if (count($days)>$nb_of_days) break; // Have we collected enough days?
}
// Build the RSS feed.
header('Content-Type: application/rss+xml; charset=utf-8');
- $pageaddr=htmlspecialchars(indexUrl());
+ $pageaddr = escape(indexUrl());
echo '';
- echo 'Daily - '.htmlspecialchars($GLOBALS['title']).' '.$pageaddr.'';
- echo 'Daily shared links en-en '.$pageaddr.' '."\n";
-
- foreach($days as $day=>$linkdates) // For each day.
- {
- $daydate = utf8_encode(strftime('%A %d, %B %Y',linkdate2timestamp($day.'_000000'))); // Full text date
+ echo '';
+ echo 'Daily - '. $GLOBALS['title'] . ' ';
+ echo ' '. $pageaddr .'';
+ echo 'Daily shared links ';
+ echo 'en-en ';
+ echo ''. $pageaddr .' '. PHP_EOL;
+
+ // For each day.
+ foreach ($days as $day => $linkdates) {
+ $daydate = linkdate2timestamp($day.'_000000'); // Full text date
$rfc822date = linkdate2rfc822($day.'_000000');
- $absurl=htmlspecialchars(indexUrl().'?do=daily&day='.$day); // Absolute URL of the corresponding "Daily" page.
- echo ''.htmlspecialchars($GLOBALS['title'].' - '.$daydate).' '.$absurl.' '.$absurl.'';
- echo ''.htmlspecialchars($rfc822date)." ";
+ $absurl = escape(indexUrl().'?do=daily&day='.$day); // Absolute URL of the corresponding "Daily" page.
// Build the HTML body of this RSS entry.
- $html='';
- $href='';
- $links=array();
+ $html = '';
+ $href = '';
+ $links = array();
+
// We pre-format some fields for proper output.
- foreach($linkdates as $linkdate)
- {
+ foreach ($linkdates as $linkdate) {
$l = $LINKSDB[$linkdate];
- $l['formatedDescription']=nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($l['description']))));
+ $l['formatedDescription'] = nl2br(keepMultipleSpaces(text2clickable($l['description'])));
$l['thumbnail'] = thumbnail($l['url']);
- $l['localdate']=linkdate2locale($l['linkdate']);
- if (startsWith($l['url'],'?')) $l['url']=indexUrl().$l['url']; // make permalink URL absolute
- $links[$linkdate]=$l;
+ $l['timestamp'] = linkdate2timestamp($l['linkdate']);
+ if (startsWith($l['url'], '?')) {
+ $l['url'] = indexUrl() . $l['url']; // make permalink URL absolute
+ }
+ $links[$linkdate] = $l;
}
+
// Then build the HTML for this day:
$tpl = new RainTPL;
- $tpl->assign('links',$links);
- $html = $tpl->draw('dailyrss',$return_string=true);
- echo "\n";
- echo ' '."\n \n\n";
+ $tpl->assign('title', $GLOBALS['title']);
+ $tpl->assign('daydate', $daydate);
+ $tpl->assign('absurl', $absurl);
+ $tpl->assign('links', $links);
+ $tpl->assign('rfc822date', escape($rfc822date));
+ $html = $tpl->draw('dailyrss', $return_string=true);
+ echo $html . PHP_EOL;
}
- echo ' ';
+ echo '';
$cache->cache(ob_get_contents());
ob_end_flush();
@@ -1121,32 +953,43 @@ function showDailyRSS()
// "Daily" page.
function showDaily()
{
- $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in).
-
+ $LINKSDB = new LinkDB(
+ $GLOBALS['config']['DATASTORE'],
+ isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI'],
+ $GLOBALS['config']['HIDE_PUBLIC_LINKS']
+ );
$day=Date('Ymd',strtotime('-1 day')); // Yesterday, in format YYYYMMDD.
if (isset($_GET['day'])) $day=$_GET['day'];
$days = $LINKSDB->days();
$i = array_search($day,$days);
- if ($i==false) { $i=count($days)-1; $day=$days[$i]; }
+ if ($i===false) { $i=count($days)-1; $day=$days[$i]; }
$previousday='';
$nextday='';
if ($i!==false)
{
- if ($i>1) $previousday=$days[$i-1];
+ if ($i>=1) $previousday=$days[$i-1];
if ($ifilterDay($day);
+ try {
+ $linksToDisplay = $LINKSDB->filterDay($day);
+ } catch (Exception $exc) {
+ error_log($exc);
+ $linksToDisplay = array();
+ }
+
// We pre-format some fields for proper output.
foreach($linksToDisplay as $key=>$link)
{
+
$taglist = explode(' ',$link['tags']);
uasort($taglist, 'strcasecmp');
$linksToDisplay[$key]['taglist']=$taglist;
- $linksToDisplay[$key]['formatedDescription']=nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description']))));
+ $linksToDisplay[$key]['formatedDescription']=nl2br(keepMultipleSpaces(text2clickable($link['description'])));
$linksToDisplay[$key]['thumbnail'] = thumbnail($link['url']);
+ $linksToDisplay[$key]['timestamp'] = linkdate2timestamp($link['linkdate']);
}
/* We need to spread the articles on 3 columns.
@@ -1173,11 +1016,8 @@ function showDaily()
$PAGE = new pageBuilder;
$PAGE->assign('linksToDisplay',$linksToDisplay);
$PAGE->assign('linkcount',count($LINKSDB));
- $PAGE->assign('col1',$columns[0]);
- $PAGE->assign('col1',$columns[0]);
- $PAGE->assign('col2',$columns[1]);
- $PAGE->assign('col3',$columns[2]);
- $PAGE->assign('day',utf8_encode(strftime('%A %d, %B %Y',linkdate2timestamp($day.'_000000'))));
+ $PAGE->assign('cols', $columns);
+ $PAGE->assign('day',linkdate2timestamp($day.'_000000'));
$PAGE->assign('previousday',$previousday);
$PAGE->assign('nextday',$nextday);
$PAGE->renderPage('daily');
@@ -1189,7 +1029,11 @@ function showDaily()
// Render HTML page (according to URL parameters and user rights)
function renderPage()
{
- $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in).
+ $LINKSDB = new LinkDB(
+ $GLOBALS['config']['DATASTORE'],
+ isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI'],
+ $GLOBALS['config']['HIDE_PUBLIC_LINKS']
+ );
// -------- Display login form.
if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=login'))
@@ -1198,7 +1042,7 @@ function renderPage()
$token=''; if (ban_canLogin()) $token=getToken(); // Do not waste token generation if not useful.
$PAGE = new pageBuilder;
$PAGE->assign('token',$token);
- $PAGE->assign('returnurl',(isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER']:''));
+ $PAGE->assign('returnurl',(isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']):''));
$PAGE->renderPage('loginform');
exit;
}
@@ -1219,21 +1063,22 @@ function renderPage()
if (!empty($_GET['searchterm'])) $links = $LINKSDB->filterFulltext($_GET['searchterm']);
elseif (!empty($_GET['searchtags'])) $links = $LINKSDB->filterTags(trim($_GET['searchtags']));
else $links = $LINKSDB;
+
$body='';
$linksToDisplay=array();
// Get only links which have a thumbnail.
foreach($links as $link)
{
- $permalink='?'.htmlspecialchars(smallhash($link['linkdate']),ENT_QUOTES);
+ $permalink='?'.escape(smallhash($link['linkdate']));
$thumb=lazyThumbnail($link['url'],$permalink);
if ($thumb!='') // Only output links which have a thumbnail.
{
$link['thumbnail']=$thumb; // Thumbnail HTML code.
- $link['permalink']=$permalink;
$linksToDisplay[]=$link; // Add to array.
}
}
+
$PAGE = new pageBuilder;
$PAGE->assign('linkcount',count($LINKSDB));
$PAGE->assign('linksToDisplay',$linksToDisplay);
@@ -1245,14 +1090,16 @@ function renderPage()
if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=tagcloud'))
{
$tags= $LINKSDB->allTags();
+
// We sort tags alphabetically, then choose a font size according to count.
// First, find max value.
$maxcount=0; foreach($tags as $key=>$value) $maxcount=max($maxcount,$value);
ksort($tags);
$tagList=array();
foreach($tags as $key=>$value)
+ // Tag font size scaling: default 15 and 30 logarithm bases affect scaling, 22 and 6 are arbitrary font sizes for max and min sizes.
{
- $tagList[$key] = array('count'=>$value,'size'=>max(40*$value/$maxcount,8));
+ $tagList[$key] = array('count'=>$value,'size'=>log($value, 15) / log($maxcount, 30) * (22-6) + 6);
}
$PAGE = new pageBuilder;
$PAGE->assign('linkcount',count($LINKSDB));
@@ -1268,9 +1115,18 @@ function renderPage()
if (empty($_SERVER['HTTP_REFERER'])) { header('Location: ?searchtags='.urlencode($_GET['addtag'])); exit; } // In case browser does not send HTTP_REFERER
parse_str(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_QUERY), $params);
+ // Prevent redirection loop
+ if (isset($params['addtag'])) {
+ unset($params['addtag']);
+ }
+
// Check if this tag is already in the search query and ignore it if it is.
// Each tag is always separated by a space
- $current_tags = explode(' ', $params['searchtags']);
+ if (isset($params['searchtags'])) {
+ $current_tags = explode(' ', $params['searchtags']);
+ } else {
+ $current_tags = array();
+ }
$addtag = true;
foreach ($current_tags as $value) {
if ($value === $_GET['addtag']) {
@@ -1292,16 +1148,29 @@ function renderPage()
}
// -------- User clicks on a tag in result count: Remove the tag from the list of searched tags (searchtags=...)
- if (isset($_GET['removetag']))
- {
+ if (isset($_GET['removetag'])) {
// Get previous URL (http_referer) and remove the tag from the searchtags parameters in query.
- if (empty($_SERVER['HTTP_REFERER'])) { header('Location: ?'); exit; } // In case browser does not send HTTP_REFERER
- parse_str(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_QUERY), $params);
- if (isset($params['searchtags']))
- {
+ if (empty($_SERVER['HTTP_REFERER'])) {
+ header('Location: ?');
+ exit;
+ }
+
+ // In case browser does not send HTTP_REFERER
+ parse_str(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_QUERY), $params);
+
+ // Prevent redirection loop
+ if (isset($params['removetag'])) {
+ unset($params['removetag']);
+ }
+
+ if (isset($params['searchtags'])) {
$tags = explode(' ',$params['searchtags']);
$tags=array_diff($tags, array($_GET['removetag'])); // Remove value from array $tags.
- if (count($tags)==0) unset($params['searchtags']); else $params['searchtags'] = implode(' ',$tags);
+ if (count($tags)==0) {
+ unset($params['searchtags']);
+ } else {
+ $params['searchtags'] = implode(' ',$tags);
+ }
unset($params['page']); // We also remove page (keeping the same page has no sense, since the results are different)
}
header('Location: ?'.http_build_query($params));
@@ -1309,33 +1178,24 @@ function renderPage()
}
// -------- User wants to change the number of links per page (linksperpage=...)
- if (isset($_GET['linksperpage']))
- {
- if (is_numeric($_GET['linksperpage'])) { $_SESSION['LINKS_PER_PAGE']=abs(intval($_GET['linksperpage'])); }
- // Make sure the referrer is Shaarli itself.
- $referer = '?';
- if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['HTTP_HOST'])==0)
- $referer = $_SERVER['HTTP_REFERER'];
- header('Location: '.$referer);
+ if (isset($_GET['linksperpage'])) {
+ if (is_numeric($_GET['linksperpage'])) {
+ $_SESSION['LINKS_PER_PAGE']=abs(intval($_GET['linksperpage']));
+ }
+
+ header('Location: '. generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('linksperpage')));
exit;
}
// -------- User wants to see only private links (toggle)
- if (isset($_GET['privateonly']))
- {
- if (empty($_SESSION['privateonly']))
- {
- $_SESSION['privateonly']=1; // See only private links
- }
- else
- {
+ if (isset($_GET['privateonly'])) {
+ if (empty($_SESSION['privateonly'])) {
+ $_SESSION['privateonly'] = 1; // See only private links
+ } else {
unset($_SESSION['privateonly']); // See all links
}
- // Make sure the referrer is Shaarli itself.
- $referer = '?';
- if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['HTTP_HOST'])==0)
- $referer = $_SERVER['HTTP_REFERER'];
- header('Location: '.$referer);
+
+ header('Location: '. generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('privateonly')));
exit;
}
@@ -1385,12 +1245,24 @@ function renderPage()
// Make sure old password is correct.
$oldhash = sha1($_POST['oldpassword'].$GLOBALS['login'].$GLOBALS['salt']);
- if ($oldhash!=$GLOBALS['hash']) { echo ''; exit; }
+ if ($oldhash!=$GLOBALS['hash']) { echo ''; exit; }
// Save new password
$GLOBALS['salt'] = sha1(uniqid('',true).'_'.mt_rand()); // Salt renders rainbow-tables attacks useless.
$GLOBALS['hash'] = sha1($_POST['setpassword'].$GLOBALS['login'].$GLOBALS['salt']);
- writeConfig();
- echo '';
+ try {
+ writeConfig($GLOBALS, isLoggedIn());
+ }
+ catch(Exception $e) {
+ error_log(
+ 'ERROR while writing config file after changing password.' . PHP_EOL .
+ $e->getMessage()
+ );
+
+ // TODO: do not handle exceptions/errors in JS.
+ echo '';
+ exit;
+ }
+ echo '';
exit;
}
else // show the change password form.
@@ -1411,17 +1283,31 @@ function renderPage()
if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away!
$tz = 'UTC';
if (!empty($_POST['continent']) && !empty($_POST['city']))
- if (isTZvalid($_POST['continent'],$_POST['city']))
+ if (isTimeZoneValid($_POST['continent'],$_POST['city']))
$tz = $_POST['continent'].'/'.$_POST['city'];
$GLOBALS['timezone'] = $tz;
$GLOBALS['title']=$_POST['title'];
$GLOBALS['titleLink']=$_POST['titleLink'];
$GLOBALS['redirector']=$_POST['redirector'];
$GLOBALS['disablesessionprotection']=!empty($_POST['disablesessionprotection']);
- $GLOBALS['disablejquery']=!empty($_POST['disablejquery']);
$GLOBALS['privateLinkByDefault']=!empty($_POST['privateLinkByDefault']);
- writeConfig();
- echo '';
+ $GLOBALS['config']['ENABLE_RSS_PERMALINKS']= !empty($_POST['enableRssPermalinks']);
+ $GLOBALS['config']['ENABLE_UPDATECHECK'] = !empty($_POST['updateCheck']);
+ $GLOBALS['config']['HIDE_PUBLIC_LINKS'] = !empty($_POST['hidePublicLinks']);
+ try {
+ writeConfig($GLOBALS, isLoggedIn());
+ }
+ catch(Exception $e) {
+ error_log(
+ 'ERROR while writing config file after configuration update.' . PHP_EOL .
+ $e->getMessage()
+ );
+
+ // TODO: do not handle exceptions/errors in JS.
+ echo '';
+ exit;
+ }
+ echo '';
exit;
}
else // Show the configuration form.
@@ -1429,10 +1315,10 @@ function renderPage()
$PAGE = new pageBuilder;
$PAGE->assign('linkcount',count($LINKSDB));
$PAGE->assign('token',getToken());
- $PAGE->assign('title',htmlspecialchars( empty($GLOBALS['title']) ? '' : $GLOBALS['title'] , ENT_QUOTES));
- $PAGE->assign('redirector',htmlspecialchars( empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'] , ENT_QUOTES));
- list($timezone_form,$timezone_js) = templateTZform($GLOBALS['timezone']);
- $PAGE->assign('timezone_form',$timezone_form); // FIXME: Put entire tz form generation in template?
+ $PAGE->assign('title', empty($GLOBALS['title']) ? '' : $GLOBALS['title'] );
+ $PAGE->assign('redirector', empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'] );
+ list($timezone_form, $timezone_js) = generateTimeZoneForm($GLOBALS['timezone']);
+ $PAGE->assign('timezone_form', $timezone_form);
$PAGE->assign('timezone_js',$timezone_js);
$PAGE->renderPage('configure');
exit;
@@ -1447,6 +1333,7 @@ function renderPage()
$PAGE = new pageBuilder;
$PAGE->assign('linkcount',count($LINKSDB));
$PAGE->assign('token',getToken());
+ $PAGE->assign('tags', $LINKSDB->allTags());
$PAGE->renderPage('changetag');
exit;
}
@@ -1465,7 +1352,7 @@ function renderPage()
$LINKSDB[$key]=$value;
}
$LINKSDB->savedb(); // Save to disk.
- echo '';
+ echo '';
exit;
}
@@ -1482,7 +1369,7 @@ function renderPage()
$LINKSDB[$key]=$value;
}
$LINKSDB->savedb(); // Save to disk.
- echo '';
+ echo '';
exit;
}
}
@@ -1501,9 +1388,10 @@ function renderPage()
{
if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away!
$tags = trim(preg_replace('/\s\s+/',' ', $_POST['lf_tags'])); // Remove multiple spaces.
+ $tags = implode(' ', array_unique(explode(' ', $tags))); // Remove duplicates.
$linkdate=$_POST['lf_linkdate'];
$url = trim($_POST['lf_url']);
- if (!startsWith($url,'http:') && !startsWith($url,'https:') && !startsWith($url,'ftp:') && !startsWith($url,'magnet:') && !startsWith($url,'?'))
+ if (!startsWith($url,'http:') && !startsWith($url,'https:') && !startsWith($url,'ftp:') && !startsWith($url,'magnet:') && !startsWith($url,'?') && !startsWith($url,'javascript:'))
$url = 'http://'.$url;
$link = array('title'=>trim($_POST['lf_title']),'url'=>$url,'description'=>trim($_POST['lf_description']),'private'=>(isset($_POST['lf_private']) ? 1 : 0),
'linkdate'=>$linkdate,'tags'=>str_replace(',',' ',$tags));
@@ -1513,10 +1401,11 @@ function renderPage()
pubsubhub();
// If we are called from the bookmarklet, we must close the popup:
- if (isset($_GET['source']) && $_GET['source']=='bookmarklet') { echo ''; exit; }
- $returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' );
- $returnurl .= '#'.smallHash($linkdate); // Scroll to the link which has been edited.
- header('Location: '.$returnurl); // After saving the link, redirect to the page the user was on.
+ if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo ''; exit; }
+ $returnurl = ( !empty($_POST['returnurl']) ? escape($_POST['returnurl']) : '?' );
+ $returnurl .= '#'.smallHash($_POST['lf_linkdate']); // Scroll to the link which has been edited.
+ $location = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link'));
+ header('Location: '. $location); // After saving the link, redirect to the page the user was on.
exit;
}
@@ -1524,9 +1413,10 @@ function renderPage()
if (isset($_POST['cancel_edit']))
{
// If we are called from the bookmarklet, we must close the popup:
- if (isset($_GET['source']) && $_GET['source']=='bookmarklet') { echo ''; exit; }
+ if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo ''; exit; }
$returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' );
$returnurl .= '#'.smallHash($_POST['lf_linkdate']); // Scroll to the link which has been edited.
+ $returnurl = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link'));
header('Location: '.$returnurl); // After canceling, redirect to the page the user was on.
exit;
}
@@ -1543,10 +1433,35 @@ function renderPage()
$LINKSDB->savedb(); // save to disk
// If we are called from the bookmarklet, we must close the popup:
- if (isset($_GET['source']) && $_GET['source']=='bookmarklet') { echo ''; exit; }
- $returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' );
- if ($returnurl=='?') { $returnurl = (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '?'); }
- header('Location: '.$returnurl); // After deleting the link, redirect to the page the user was on.
+ if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo ''; exit; }
+ // Pick where we're going to redirect
+ // =============================================================
+ // Basically, we can't redirect to where we were previously if it was a permalink
+ // or an edit_link, because it would 404.
+ // Cases:
+ // - / : nothing in $_GET, redirect to self
+ // - /?page : redirect to self
+ // - /?searchterm : redirect to self (there might be other links)
+ // - /?searchtags : redirect to self
+ // - /permalink : redirect to / (the link does not exist anymore)
+ // - /?edit_link : redirect to / (the link does not exist anymore)
+ // PHP treats the permalink as a $_GET variable, so we need to check if every condition for self
+ // redirect is not satisfied, and only then redirect to /
+ $location = "?";
+ // Self redirection
+ if (count($_GET) == 0
+ || isset($_GET['page'])
+ || isset($_GET['searchterm'])
+ || isset($_GET['searchtags'])
+ ) {
+ if (isset($_POST['returnurl'])) {
+ $location = $_POST['returnurl']; // Handle redirects given by the form
+ } else {
+ $location = generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('delete_link'));
+ }
+ }
+
+ header('Location: ' . $location); // After deleting the link, redirect to appropriate location
exit;
}
@@ -1560,7 +1475,8 @@ function renderPage()
$PAGE->assign('link',$link);
$PAGE->assign('link_is_new',false);
$PAGE->assign('token',getToken()); // XSRF protection.
- $PAGE->assign('http_referer',(isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''));
+ $PAGE->assign('http_referer',(isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : ''));
+ $PAGE->assign('tags', $LINKSDB->allTags());
$PAGE->renderPage('editlink');
exit;
}
@@ -1570,10 +1486,26 @@ function renderPage()
{
$url=$_GET['post'];
- // We remove the annoying parameters added by FeedBurner and GoogleFeedProxy (?utm_source=...)
- $i=strpos($url,'&utm_source='); if ($i!==false) $url=substr($url,0,$i);
- $i=strpos($url,'?utm_source='); if ($i!==false) $url=substr($url,0,$i);
- $i=strpos($url,'#xtor=RSS-'); if ($i!==false) $url=substr($url,0,$i);
+
+ // We remove the annoying parameters added by FeedBurner, GoogleFeedProxy, Facebook...
+ $annoyingpatterns = array('/[\?&]utm_source=[^&]*/',
+ '/[\?&]utm_campaign=[^&]*/',
+ '/[\?&]utm_medium=[^&]*/',
+ '/#xtor=RSS-[^&]*/',
+ '/[\?&]fb_[^&]*/',
+ '/[\?&]__scoop[^&]*/',
+ '/#tk\.rss_all\?/',
+ '/[\?&]action_ref_map=[^&]*/',
+ '/[\?&]action_type_map=[^&]*/',
+ '/[\?&]action_object_map=[^&]*/',
+ '/[\?&]utm_content=[^&]*/',
+ '/[\?&]fb=[^&]*/',
+ '/[\?&]xtor=[^&]*/'
+ );
+ foreach($annoyingpatterns as $pattern)
+ {
+ $url = preg_replace($pattern, "", $url);
+ }
$link_is_new = false;
$link = $LINKSDB->getLinkFromUrl($url); // Check if URL is not already in database (in this case, we will edit the existing link)
@@ -1629,6 +1561,8 @@ function renderPage()
$PAGE->assign('link_is_new',$link_is_new);
$PAGE->assign('token',getToken()); // XSRF protection.
$PAGE->assign('http_referer',(isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''));
+ $PAGE->assign('source',(isset($_GET['source']) ? $_GET['source'] : ''));
+ $PAGE->assign('tags', $LINKSDB->allTags());
$PAGE->renderPage('editlink');
exit;
}
@@ -1665,10 +1599,10 @@ HTML;
($exportWhat=='private' && $link['private']!=0) ||
($exportWhat=='public' && $link['private']==0))
{
- echo ''.htmlspecialchars($link['title'])." \n";
- if ($link['description']!='') echo ''.htmlspecialchars($link['description'])."\n";
+ echo ' '.$link['title']." \n";
+ if ($link['description']!='') echo ''.$link['description']."\n";
}
}
exit;
@@ -1681,7 +1615,7 @@ HTML;
if (!isset($_POST['token']) || (!isset($_FILES)) || (isset($_FILES['filetoupload']['size']) && $_FILES['filetoupload']['size']==0))
{
$returnurl = ( empty($_SERVER['HTTP_REFERER']) ? '?' : $_SERVER['HTTP_REFERER'] );
- echo '';
+ echo '';
exit;
}
if (!tokenOk($_POST['token'])) die('Wrong token.');
@@ -1702,7 +1636,6 @@ HTML;
// -------- Otherwise, simply display search form and links:
$PAGE = new pageBuilder;
- $PAGE->assign('linkcount',count($LINKSDB));
buildLinkList($PAGE,$LINKSDB); // Compute list of links to display
$PAGE->renderPage('linklist');
exit;
@@ -1713,7 +1646,11 @@ HTML;
function importFile()
{
if (!(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI'])) { die('Not allowed.'); }
- $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in).
+ $LINKSDB = new LinkDB(
+ $GLOBALS['config']['DATASTORE'],
+ isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI'],
+ $GLOBALS['config']['HIDE_PUBLIC_LINKS']
+ );
$filename=$_FILES['filetoupload']['name'];
$filesize=$_FILES['filetoupload']['size'];
$data=file_get_contents($_FILES['filetoupload']['tmp_name']);
@@ -1785,11 +1722,11 @@ function importFile()
}
$LINKSDB->savedb();
- echo '';
+ echo '';
}
else
{
- echo '';
+ echo '';
}
}
@@ -1805,13 +1742,13 @@ function buildLinkList($PAGE,$LINKSDB)
if (isset($_GET['searchterm'])) // Fulltext search
{
$linksToDisplay = $LINKSDB->filterFulltext(trim($_GET['searchterm']));
- $search_crits=htmlspecialchars(trim($_GET['searchterm']));
+ $search_crits=escape(trim($_GET['searchterm']));
$search_type='fulltext';
}
elseif (isset($_GET['searchtags'])) // Search by tag
{
$linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags']));
- $search_crits=explode(' ',trim($_GET['searchtags']));
+ $search_crits=explode(' ',escape(trim($_GET['searchtags'])));
$search_type='tags';
}
elseif (isset($_SERVER['QUERY_STRING']) && preg_match('/[a-zA-Z0-9-_@]{6}(&.+?)?/',$_SERVER['QUERY_STRING'])) // Detect smallHashes in URL
@@ -1821,7 +1758,7 @@ function buildLinkList($PAGE,$LINKSDB)
{
header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found");
echo '404 Not found. Oh crap. The link you are trying to reach does not exist or has been deleted.';
- echo ' You would mind clicking here ?';
+ echo ' Would you mind clicking here ?';
exit;
}
$search_type='permalink';
@@ -1829,6 +1766,7 @@ function buildLinkList($PAGE,$LINKSDB)
else
$linksToDisplay = $LINKSDB; // Otherwise, display without filtering.
+
// Option: Show only private links
if (!empty($_SESSION['privateonly']))
{
@@ -1862,14 +1800,20 @@ function buildLinkList($PAGE,$LINKSDB)
while ($i<$end && $iassign('redirector',empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector']); // Optional redirector URL.
$PAGE->assign('token',$token);
$PAGE->assign('links',$linkDisp);
+ $PAGE->assign('tags', $LINKSDB->allTags());
return;
}
@@ -2001,7 +1946,7 @@ function computeThumbnail($url,$href=false)
if ("/talks/" !== substr($path,0,7)) return array(); // This is not a single video URL.
}
$sign = hash_hmac('sha256', $url, $GLOBALS['salt']); // We use the salt to sign data (it's random, secret, and specific to each installation)
- return array('src'=>indexUrl().'?do=genthumbnail&hmac='.htmlspecialchars($sign).'&url='.urlencode($url),
+ return array('src'=>indexUrl().'?do=genthumbnail&hmac='.$sign.'&url='.urlencode($url),
'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'thumbnail');
}
@@ -2012,7 +1957,7 @@ function computeThumbnail($url,$href=false)
if ($ext=='jpg' || $ext=='jpeg' || $ext=='png' || $ext=='gif')
{
$sign = hash_hmac('sha256', $url, $GLOBALS['salt']); // We use the salt to sign data (it's random, secret, and specific to each installation)
- return array('src'=>indexUrl().'?do=genthumbnail&hmac='.htmlspecialchars($sign).'&url='.urlencode($url),
+ return array('src'=>indexUrl().'?do=genthumbnail&hmac='.$sign.'&url='.urlencode($url),
'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'thumbnail');
}
return array(); // No thumbnail.
@@ -2031,16 +1976,15 @@ function thumbnail($url,$href=false)
$t = computeThumbnail($url,$href);
if (count($t)==0) return ''; // Empty array = no thumbnail for this URL.
- $html=' ';
return $html;
}
-
// Returns the HTML code to display a thumbnail for a link
// for the picture wall (using lazy image loading)
// Understands various services (youtube.com...)
@@ -2052,26 +1996,23 @@ function lazyThumbnail($url,$href=false)
$t = computeThumbnail($url,$href);
if (count($t)==0) return ''; // Empty array = no thumbnail for this URL.
- $html='';
+ $html=' ';
- // Lazy image (only loaded by JavaScript when in the viewport).
- if (!empty($GLOBALS['disablejquery'])) // (except if jQuery is disabled)
- $html.=' ';
// No-JavaScript fallback.
- $html.=' ';
return $html;
@@ -2113,23 +2054,41 @@ function install()
if (!empty($_POST['setlogin']) && !empty($_POST['setpassword']))
{
$tz = 'UTC';
- if (!empty($_POST['continent']) && !empty($_POST['city']))
- if (isTZvalid($_POST['continent'],$_POST['city']))
+ if (!empty($_POST['continent']) && !empty($_POST['city'])) {
+ if (isTimeZoneValid($_POST['continent'], $_POST['city'])) {
$tz = $_POST['continent'].'/'.$_POST['city'];
+ }
+ }
$GLOBALS['timezone'] = $tz;
// Everything is ok, let's create config file.
$GLOBALS['login'] = $_POST['setlogin'];
$GLOBALS['salt'] = sha1(uniqid('',true).'_'.mt_rand()); // Salt renders rainbow-tables attacks useless.
$GLOBALS['hash'] = sha1($_POST['setpassword'].$GLOBALS['login'].$GLOBALS['salt']);
- $GLOBALS['title'] = (empty($_POST['title']) ? 'Shared links on '.htmlspecialchars(indexUrl()) : $_POST['title'] );
- writeConfig();
- echo '';
+ $GLOBALS['title'] = (empty($_POST['title']) ? 'Shared links on '.escape(indexUrl()) : $_POST['title'] );
+ $GLOBALS['config']['ENABLE_UPDATECHECK'] = !empty($_POST['updateCheck']);
+ try {
+ writeConfig($GLOBALS, isLoggedIn());
+ }
+ catch(Exception $e) {
+ error_log(
+ 'ERROR while writing config file after installation.' . PHP_EOL .
+ $e->getMessage()
+ );
+
+ // TODO: do not handle exceptions/errors in JS.
+ echo '';
+ exit;
+ }
+ echo '';
exit;
}
// Display config form:
- list($timezone_form,$timezone_js) = templateTZform();
- $timezone_html=''; if ($timezone_form!='') $timezone_html='Timezone: '.$timezone_form.' ';
+ list($timezone_form, $timezone_js) = generateTimeZoneForm();
+ $timezone_html = '';
+ if ($timezone_form != '') {
+ $timezone_html = 'Timezone: '.$timezone_form.' ';
+ }
$PAGE = new pageBuilder;
$PAGE->assign('timezone_html',$timezone_html);
@@ -2138,67 +2097,6 @@ function install()
exit;
}
-// Generates the timezone selection form and JavaScript.
-// Input: (optional) current timezone (can be 'UTC/UTC'). It will be pre-selected.
-// Output: array(html,js)
-// Example: list($htmlform,$js) = templateTZform('Europe/Paris'); // Europe/Paris pre-selected.
-// Returns array('','') if server does not support timezones list. (e.g. PHP 5.1 on free.fr)
-function templateTZform($ptz=false)
-{
- if (function_exists('timezone_identifiers_list')) // because of old PHP version (5.1) which can be found on free.fr
- {
- // Try to split the provided timezone.
- if ($ptz==false) { $l=timezone_identifiers_list(); $ptz=$l[0]; }
- $spos=strpos($ptz,'/'); $pcontinent=substr($ptz,0,$spos); $pcity=substr($ptz,$spos+1);
-
- // Display config form:
- $timezone_form = '';
- $timezone_js = '';
- // The list is in the form "Europe/Paris", "America/Argentina/Buenos_Aires"...
- // We split the list in continents/cities.
- $continents = array();
- $cities = array();
- foreach(timezone_identifiers_list() as $tz)
- {
- if ($tz=='UTC') $tz='UTC/UTC';
- $spos = strpos($tz,'/');
- if ($spos!==false)
- {
- $continent=substr($tz,0,$spos); $city=substr($tz,$spos+1);
- $continents[$continent]=1;
- if (!isset($cities[$continent])) $cities[$continent]='';
- $cities[$continent].=''.$city.' ';
- }
- }
- $continents_html = '';
- $continents = array_keys($continents);
- foreach($continents as $continent)
- $continents_html.=''.$continent.' ';
- $cities_html = $cities[$pcontinent];
- $timezone_form = "Continent: ${continents_html} ";
- $timezone_form .= " City: ${cities[$pcontinent]} ";
- $timezone_js = "" ;
- return array($timezone_form,$timezone_js);
- }
- return array('','');
-}
-
-// Tells if a timezone is valid or not.
-// If not valid, returns false.
-// If system does not support timezone list, returns false.
-function isTZvalid($continent,$city)
-{
- $tz = $continent.'/'.$city;
- if (function_exists('timezone_identifiers_list')) // because of old PHP version (5.1) which can be found on free.fr
- {
- if (in_array($tz, timezone_identifiers_list())) // it's a valid timezone?
- return true;
- }
- return false;
-}
if (!function_exists('json_encode')) {
function json_encode($data) {
switch ($type = gettype($data)) {
@@ -2236,66 +2134,7 @@ if (!function_exists('json_encode')) {
}
}
-// Webservices (for use with jQuery/jQueryUI)
-// e.g. index.php?ws=tags&term=minecr
-function processWS()
-{
- if (empty($_GET['ws']) || empty($_GET['term'])) return;
- $term = $_GET['term'];
- $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in).
- header('Content-Type: application/json; charset=utf-8');
-
- // Search in tags (case insensitive, cumulative search)
- if ($_GET['ws']=='tags')
- {
- $tags=explode(' ',str_replace(',',' ',$term)); $last = array_pop($tags); // Get the last term ("a b c d" ==> "a b c", "d")
- $addtags=''; if ($tags) $addtags=implode(' ',$tags).' '; // We will pre-pend previous tags
- $suggested=array();
- /* To speed up things, we store list of tags in session */
- if (empty($_SESSION['tags'])) $_SESSION['tags'] = $LINKSDB->allTags();
- foreach($_SESSION['tags'] as $key=>$value)
- {
- if (startsWith($key,$last,$case=false) && !in_array($key,$tags)) $suggested[$addtags.$key.' ']=0;
- }
- echo json_encode(array_keys($suggested));
- exit;
- }
-
- // Search a single tag (case sensitive, single tag search)
- if ($_GET['ws']=='singletag')
- {
- /* To speed up things, we store list of tags in session */
- if (empty($_SESSION['tags'])) $_SESSION['tags'] = $LINKSDB->allTags();
- foreach($_SESSION['tags'] as $key=>$value)
- {
- if (startsWith($key,$term,$case=true)) $suggested[$key]=0;
- }
- echo json_encode(array_keys($suggested));
- exit;
- }
-}
-// Re-write configuration file according to globals.
-// Requires some $GLOBALS to be set (login,hash,salt,title).
-// If the config file cannot be saved, an error message is displayed and the user is redirected to "Tools" menu.
-// (otherwise, the function simply returns.)
-function writeConfig()
-{
- if (is_file($GLOBALS['config']['CONFIG_FILE']) && !isLoggedIn()) die('You are not authorized to alter config.'); // Only logged in user can alter config.
- $config='';
- if (!file_put_contents($GLOBALS['config']['CONFIG_FILE'],$config) || strcmp(file_get_contents($GLOBALS['config']['CONFIG_FILE']),$config)!=0)
- {
- echo '';
- exit;
- }
-}
/* Because some f*cking services like flickr require an extra HTTP request to get the thumbnail URL,
I have deported the thumbnail URL code generation here, otherwise this would slow down page generation.
@@ -2383,7 +2222,7 @@ function genThumbnail()
// This is more complex: we have to perform a HTTP request, then parse the result.
// Maybe we should deport this to JavaScript ? Example: http://stackoverflow.com/questions/1361149/get-img-thumbnails-from-vimeo/4285098#4285098
$vid = substr(parse_url($url,PHP_URL_PATH),1);
- list($httpstatus,$headers,$data) = getHTTP('https://vimeo.com/api/v2/video/'.htmlspecialchars($vid).'.php',5);
+ list($httpstatus,$headers,$data) = getHTTP('https://vimeo.com/api/v2/video/'.escape($vid).'.php',5);
if (strpos($httpstatus,'200 OK')!==false)
{
$t = unserialize($data);
@@ -2524,12 +2363,20 @@ function invalidateCaches()
pageCache::purgeCache(); // Purge page cache shared by sessions.
}
+try {
+ mergeDeprecatedConfig($GLOBALS, isLoggedIn());
+} catch(Exception $e) {
+ error_log(
+ 'ERROR while merging deprecated options.php file.' . PHP_EOL .
+ $e->getMessage()
+ );
+}
+
if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=genthumbnail')) { genThumbnail(); exit; } // Thumbnail generation/cache does not need the link database.
if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=rss')) { showRSS(); exit; }
if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=atom')) { showATOM(); exit; }
if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=dailyrss')) { showDailyRSS(); exit; }
if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=daily')) { showDaily(); exit; }
-if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'ws=')) { processWS(); exit; } // Webservices (for jQuery/jQueryUI)
if (!isset($_SESSION['LINKS_PER_PAGE'])) $_SESSION['LINKS_PER_PAGE']=$GLOBALS['config']['LINKS_PER_PAGE'];
renderPage();
?>