<?php
-// Shaarli 0.0.42 beta - Shaare your links...
+// Shaarli 0.5.2 - Shaare your links...
// The personal, minimalist, super-fast, no-database Delicious clone. By sebsauvage.net
// http://sebsauvage.net/wiki/doku.php?id=php:shaarli
// Licence: http://www.opensource.org/licenses/zlib-license.php
-// Requires: PHP 5.1.x (but autocomplete fields will only work if you have PHP 5.2.x)
+// Requires: PHP 5.3.x
// -----------------------------------------------------------------------------------------------
-// NEVER TRUST IN PHP.INI
-// Some hosts do not define a default timezone in php.ini,
-// so we have to do this for avoid the strict standard error.
-date_default_timezone_set('UTC');
+
+// Set 'UTC' as the default timezone if it is not defined in php.ini
+// See http://php.net/manual/en/datetime.configuration.php#ini.date.timezone
+if (date_default_timezone_get() == '') {
+ date_default_timezone_set('UTC');
+}
// -----------------------------------------------------------------------------------------------
-// Hardcoded parameter (These parameters can be overwritten by creating the file /config/options.php)
+// Hardcoded parameter (These parameters can be overwritten by editing the file /data/config.php)
+// You should not touch any code below (or at your own risks!)
$GLOBALS['config']['DATADIR'] = 'data'; // Data subdirectory
$GLOBALS['config']['CONFIG_FILE'] = $GLOBALS['config']['DATADIR'].'/config.php'; // Configuration file (user login/password)
$GLOBALS['config']['DATASTORE'] = $GLOBALS['config']['DATADIR'].'/datastore.php'; // Data storage file.
$GLOBALS['config']['UPDATECHECK_INTERVAL'] = 86400 ; // Updates check frequency for Shaarli. 86400 seconds=24 hours
// Note: You must have publisher.php in the same directory as Shaarli index.php
$GLOBALS['config']['ARCHIVE_ORG'] = false; // For each link, add a link to an archived version on archive.org
+$GLOBALS['config']['ENABLE_RSS_PERMALINKS'] = true; // Enable RSS permalinks by default. This corresponds to the default behavior of shaarli before this was added as an option.
+$GLOBALS['config']['HIDE_PUBLIC_LINKS'] = false;
// -----------------------------------------------------------------------------------------------
-// You should not touch below (or at your own risks!)
-// Optional config file.
-if (is_file($GLOBALS['config']['DATADIR'].'/options.php')) require($GLOBALS['config']['DATADIR'].'/options.php');
-
-define('shaarli_version','0.0.42 beta');
-define('PHPPREFIX','<?php /* '); // Prefix to encapsulate data in PHP code.
-define('PHPSUFFIX',' */ ?>'); // Suffix to encapsulate data in PHP code.
+define('shaarli_version','0.5.2');
// 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.
-
-// 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.
-ini_set('session.use_cookies', 1); // Use cookies to store session.
-ini_set('session.use_only_cookies', 1); // Force cookies for session (phpsessionID forbidden in URL).
-ini_set('session.use_trans_sid', false); // Prevent PHP form using sessionID in URL if cookies are disabled.
-session_name('shaarli');
-if (session_id() == '') session_start(); // Start session if needed (Some server auto-start sessions).
-
// PHP Settings
ini_set('max_input_time','60'); // High execution time in case of problematic imports/exports.
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/Cache.php';
+require_once 'application/CachedPage.php';
+require_once 'application/LinkDB.php';
+require_once 'application/TimeZone.php';
+require_once 'application/Url.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;
+}
+
+// Force cookie path (but do not change lifetime)
+$cookie = session_get_cookie_params();
+$cookiedir = '';
+if (dirname($_SERVER['SCRIPT_NAME']) != '/') {
+ $cookiedir = dirname($_SERVER["SCRIPT_NAME"]).'/';
+}
+// Set default cookie expiration and path.
+session_set_cookie_params($cookie['lifetime'], $cookiedir, $_SERVER['SERVER_NAME']);
+// Set session parameters on server side.
+// If the user does not access any page within this time, his/her session is considered expired.
+define('INACTIVITY_TIMEOUT', 3600); // in seconds.
+// Use cookies to store session.
+ini_set('session.use_cookies', 1);
+// Force cookies for session (phpsessionID forbidden in URL).
+ini_set('session.use_only_cookies', 1);
+// Prevent PHP form using sessionID in URL if cookies are disabled.
+ini_set('session.use_trans_sid', false);
+
+// Regenerate session id if invalid or not defined in cookie.
+if (isset($_COOKIE['shaarli']) && !is_session_id_valid($_COOKIE['shaarli'])) {
+ $_COOKIE['shaarli'] = uniqid();
+}
+session_name('shaarli');
+// Start session if needed (Some server auto-start sessions).
+if (session_id() == '') {
+ session_start();
+}
+
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
if (!is_writable(realpath(dirname(__FILE__)))) die('<pre>ERROR: Shaarli does not have the right to write in its own directory.</pre>');
// 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']));
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 PHP version is obsolete! Shaarli requires at least php 5.1.0, and thus cannot run. Sorry. Your PHP version has known security vulnerabilities and should be updated as soon as possible.';
- 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.)
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'])<time()-($GLOBALS['config']['UPDATECHECK_INTERVAL'])))
{
$version=shaarli_version;
- list($httpstatus,$headers,$data) = getHTTP('https://raw.githubusercontent.com/shaarli/Shaarli/master/shaarli_version.txt',2);
- if (strpos($httpstatus,'200 OK')!==false) $version=$data;
+ list($httpstatus,$headers,$data) = getHTTP('https://raw.githubusercontent.com/shaarli/Shaarli/master/shaarli_version.php',2);
+ if (strpos($httpstatus,'200 OK')!==false) $version=str_replace(' */ ?>','',str_replace('<?php /* ','',$data));
// If failed, never mind. We don't want to bother the user with that.
file_put_contents($GLOBALS['config']['UPDATECHECK_FILENAME'],$version); // touch file date
}
}
-// -----------------------------------------------------------------------------------------------
-// Simple cache system (mainly for the RSS/ATOM feeds).
-
-class pageCache
-{
- private $url; // Full URL of the page to cache (typically the value returned by pageUrl())
- private $shouldBeCached; // boolean: Should this url be cached?
- private $filename; // Name of the cache file for this url.
-
- /*
- $url = URL (typically the value returned by pageUrl())
- $shouldBeCached = boolean. If false, the cache will be disabled.
- */
- public function __construct($url,$shouldBeCached)
- {
- $this->url = $url;
- $this->filename = $GLOBALS['config']['PAGECACHE'].'/'.sha1($url).'.cache';
- $this->shouldBeCached = $shouldBeCached;
- }
-
- // If the page should be cached and a cached version exists,
- // returns the cached version (otherwise, return null).
- public function cachedVersion()
- {
- if (!$this->shouldBeCached) return null;
- if (is_file($this->filename)) { return file_get_contents($this->filename); exit; }
- return null;
- }
-
- // Put a page in the cache.
- public function cache($page)
- {
- if (!$this->shouldBeCached) return;
- file_put_contents($this->filename,$page);
- }
-
- // Purge the whole cache.
- // (call with pageCache::purgeCache())
- public static function purgeCache()
- {
- if (is_dir($GLOBALS['config']['PAGECACHE']))
- {
- $handler = opendir($GLOBALS['config']['PAGECACHE']);
- if ($handler!==false)
- {
- while (($filename = readdir($handler))!==false)
- {
- if (endsWith($filename,'.cache')) { unlink($GLOBALS['config']['PAGECACHE'].'/'.$filename); }
- }
- closedir($handler);
- }
- }
- }
-
-}
-
-
// -----------------------------------------------------------------------------------------------
// Log to text file
function logm($message)
file_put_contents($GLOBALS['config']['DATADIR'].'/log.txt',$t,FILE_APPEND);
}
-// Same as nl2br(), but escapes < and >
-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)
// (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.
}
// ------------------------------------------------------------------------------------------
// 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);
}
$_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:
- 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; }
- if (isset($_POST['returnurl']))
- {
- if (endsWith($_POST['returnurl'],'?do=login')) { header('Location: ?'); exit; } // Prevent loops over login screen.
- header('Location: '.$_POST['returnurl']); exit;
+ if (isset($_GET['post'])) {
+ $uri = '?post='. urlencode($_GET['post']);
+ foreach (array('description', 'source', 'title') as $param) {
+ if (!empty($_GET[$param])) {
+ $uri .= '&'.$param.'='.urlencode($_GET[$param]);
+ }
+ }
+ header('Location: '. $uri);
+ exit;
+ }
+
+ if (isset($_GET['edit_link'])) {
+ header('Location: ?edit_link='. escape($_GET['edit_link']));
+ exit;
+ }
+
+ if (isset($_POST['returnurl'])) {
+ // Prevent loops over login screen.
+ if (strpos($_POST['returnurl'], 'do=login') === false) {
+ header('Location: '. escape($_POST['returnurl']));
+ exit;
+ }
}
header('Location: ?'); exit;
}
{
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 '<script language="JavaScript">alert("Wrong login/password.");document.location=\'?do=login'.$redir.'\';</script>'; // Redirect to login screen.
+ if (isset($_GET['post'])) {
+ $redir = '?post=' . urlencode($_GET['post']);
+ foreach (array('description', 'source', 'title') as $param) {
+ if (!empty($_GET[$param])) {
+ $redir .= '&' . $param . '=' . urlencode($_GET[$param]);
+ }
+ }
+ }
+ echo '<script>alert("Wrong login/password.");document.location=\'?do=login'.$redir.'\';</script>'; // Redirect to login screen.
exit;
}
}
{
$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.
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.
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 )
{
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']);
}
}
-// ------------------------------------------------------------------------------------------
-/* 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"?
- // Using mb_convert_case($val, MB_CASE_LOWER, 'UTF-8') allows us to perform searches on
- // Unicode text. See https://github.com/shaarli/Shaarli/issues/75 for examples.
- $filtered=array();
- $s = mb_convert_case($searchterms, MB_CASE_LOWER, 'UTF-8');
- foreach($this->links as $l)
- {
- $found= (strpos(mb_convert_case($l['title'], MB_CASE_LOWER, 'UTF-8'),$s) !== false)
- || (strpos(mb_convert_case($l['description'], MB_CASE_LOWER, 'UTF-8'),$s) !== false)
- || (strpos(mb_convert_case($l['url'], MB_CASE_LOWER, 'UTF-8'),$s) !== false)
- || (strpos(mb_convert_case($l['tags'], MB_CASE_LOWER, 'UTF-8'),$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)
- {
- // Same as above, we use UTF-8 conversion to handle various graphemes (i.e. cyrillic, or greek)
- // TODO: is $casesensitive ever true ?
- $t = str_replace(',',' ',($casesensitive?$tags:mb_convert_case($tags, MB_CASE_LOWER, 'UTF-8')));
- $searchtags=explode(' ',$t);
- $filtered=array();
- foreach($this->links as $l)
- {
- $linktags = explode(' ',($casesensitive?$l['tags']:mb_convert_case($l['tags'], MB_CASE_LOWER, 'UTF-8')));
- 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()
// $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"];
- $cache = new pageCache(pageUrl(),startsWith($query,'do=rss') && !isLoggedIn());
- $cached = $cache->cachedVersion(); if (!empty($cached)) { echo $cached; exit; }
+ $cache = new CachedPage(
+ $GLOBALS['config']['PAGECACHE'],
+ pageUrl(),
+ startsWith($query,'do=rss') && !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 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 '<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">';
- echo '<channel><title>'.htmlspecialchars($GLOBALS['title']).'</title><link>'.$pageaddr.'</link>';
+ echo '<channel><title>'.$GLOBALS['title'].'</title><link>'.$pageaddr.'</link>';
echo '<description>Shared links</description><language>en-en</language><copyright>'.$pageaddr.'</copyright>'."\n\n";
if (!empty($GLOBALS['config']['PUBSUBHUB_URL']))
{
echo '<!-- PubSubHubbub Discovery -->';
- echo '<link rel="hub" href="'.htmlspecialchars($GLOBALS['config']['PUBSUBHUB_URL']).'" xmlns="http://www.w3.org/2005/Atom" />';
- echo '<link rel="self" href="'.htmlspecialchars($pageaddr).'?do=rss" xmlns="http://www.w3.org/2005/Atom" />';
+ echo '<link rel="hub" href="'.escape($GLOBALS['config']['PUBSUBHUB_URL']).'" xmlns="http://www.w3.org/2005/Atom" />';
+ echo '<link rel="self" href="'.$pageaddr.'?do=rss" xmlns="http://www.w3.org/2005/Atom" />';
echo '<!-- End Of PubSubHubbub Discovery -->';
}
$i=0;
$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 '<item><title>'.htmlspecialchars($link['title']).'</title><guid isPermaLink="false">'.$guid.'</guid><link>'.$guid.'</link>';
+ echo '<item><title>'.$link['title'].'</title><guid isPermaLink="true">'.$guid.'</guid><link>'.$guid.'</link>';
else
- echo '<item><title>'.htmlspecialchars($link['title']).'</title><guid isPermaLink="false">'.$guid.'</guid><link>'.$absurl.'</link>';
- if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) echo '<pubDate>'.htmlspecialchars($rfc822date)."</pubDate>\n";
+ echo '<item><title>'.$link['title'].'</title><guid isPermaLink="false">'.$guid.'</guid><link>'.$absurl.'</link>';
+ if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) echo '<pubDate>'.escape($rfc822date)."</pubDate>\n";
if ($link['tags']!='') // Adding tags to each RSS entry (as mentioned in RSS specification)
{
- foreach(explode(' ',$link['tags']) as $tag) { echo '<category domain="'.htmlspecialchars($pageaddr).'">'.htmlspecialchars($tag).'</category>'."\n"; }
+ foreach(explode(' ',$link['tags']) as $tag) { echo '<category domain="'.$pageaddr.'">'.$tag.'</category>'."\n"; }
}
// Add permalink in description
// If user wants permalinks first, put the final link in description
if ($usepermalinks===true) $descriptionlink = '(<a href="'.$absurl.'">Link</a>)';
if (strlen($link['description'])>0) $descriptionlink = '<br>'.$descriptionlink;
- echo '<description><![CDATA['.nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description'])))).$descriptionlink.']]></description>'."\n</item>\n";
+ echo '<description><![CDATA['.nl2br(keepMultipleSpaces(text2clickable($link['description']))).$descriptionlink.']]></description>'."\n</item>\n";
$i++;
}
- echo '</channel></rss><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->';
+ echo '</channel></rss><!-- Cached version of '.escape(pageUrl()).' -->';
$cache->cache(ob_get_contents());
ob_end_flush();
// $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"];
- $cache = new pageCache(pageUrl(),startsWith($query,'do=atom') && !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).
+ $cache = new CachedPage(
+ $GLOBALS['config']['PAGECACHE'],
+ pageUrl(),
+ startsWith($query,'do=atom') && !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:
+ // 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;
$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.='<entry><title>'.htmlspecialchars($link['title']).'</title>';
+ $entries.='<entry><title>'.$link['title'].'</title>';
if ($usepermalinks===true)
$entries.='<link href="'.$guid.'" /><id>'.$guid.'</id>';
else
$entries.='<link href="'.$absurl.'" /><id>'.$guid.'</id>';
- if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) $entries.='<updated>'.htmlspecialchars($iso8601date).'</updated>';
+ if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) $entries.='<updated>'.escape($iso8601date).'</updated>';
// Add permalink in description
- $descriptionlink = htmlspecialchars('(<a href="'.$guid.'">Permalink</a>)');
+ $descriptionlink = '(<a href="'.$guid.'">Permalink</a>)';
// If user wants permalinks first, put the final link in description
- if ($usepermalinks===true) $descriptionlink = htmlspecialchars('(<a href="'.$absurl.'">Link</a>)');
- if (strlen($link['description'])>0) $descriptionlink = '<br>'.$descriptionlink;
+ if ($usepermalinks===true) $descriptionlink = '(<a href="'.$absurl.'">Link</a>)';
+ if (strlen($link['description'])>0) $descriptionlink = '<br>'.$descriptionlink;
- $entries.='<content type="html">'.htmlspecialchars(nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description']))))).$descriptionlink."</content>\n";
+ $entries.='<content type="html"><![CDATA['.nl2br(keepMultipleSpaces(text2clickable($link['description']))).$descriptionlink."]]></content>\n";
if ($link['tags']!='') // Adding tags to each ATOM entry (as mentioned in ATOM specification)
{
foreach(explode(' ',$link['tags']) as $tag)
- { $entries.='<category scheme="'.htmlspecialchars($pageaddr,ENT_QUOTES).'" term="'.htmlspecialchars($tag,ENT_QUOTES).'" />'."\n"; }
+ { $entries.='<category scheme="'.$pageaddr.'" term="'.$tag.'" />'."\n"; }
}
$entries.="</entry>\n";
$i++;
}
$feed='<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom">';
- $feed.='<title>'.htmlspecialchars($GLOBALS['title']).'</title>';
- if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) $feed.='<updated>'.htmlspecialchars($latestDate).'</updated>';
- $feed.='<link rel="self" href="'.htmlspecialchars(serverUrl().$_SERVER["REQUEST_URI"]).'" />';
+ $feed.='<title>'.$GLOBALS['title'].'</title>';
+ if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) $feed.='<updated>'.escape($latestDate).'</updated>';
+ $feed.='<link rel="self" href="'.escape(serverUrl().$_SERVER["REQUEST_URI"]).'" />';
if (!empty($GLOBALS['config']['PUBSUBHUB_URL']))
{
$feed.='<!-- PubSubHubbub Discovery -->';
- $feed.='<link rel="hub" href="'.htmlspecialchars($GLOBALS['config']['PUBSUBHUB_URL']).'" />';
+ $feed.='<link rel="hub" href="'.escape($GLOBALS['config']['PUBSUBHUB_URL']).'" />';
$feed.='<!-- End Of PubSubHubbub Discovery -->';
}
- $feed.='<author><name>'.htmlspecialchars($pageaddr).'</name><uri>'.htmlspecialchars($pageaddr).'</uri></author>';
- $feed.='<id>'.htmlspecialchars($pageaddr).'</id>'."\n\n"; // Yes, I know I should use a real IRI (RFC3987), but the site URL will do.
+ $feed.='<author><name>'.$pageaddr.'</name><uri>'.$pageaddr.'</uri></author>';
+ $feed.='<id>'.$pageaddr.'</id>'."\n\n"; // Yes, I know I should use a real IRI (RFC3987), but the site URL will do.
$feed.=$entries;
- $feed.='</feed><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->';
+ $feed.='</feed><!-- Cached version of '.escape(pageUrl()).' -->';
echo $feed;
$cache->cache(ob_get_contents());
// 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 CachedPage(
+ $GLOBALS['config']['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 '<?xml version="1.0" encoding="UTF-8"?><rss version="2.0">';
- echo '<channel><title>Daily - '.htmlspecialchars($GLOBALS['title']).'</title><link>'.$pageaddr.'</link>';
- echo '<description>Daily shared links</description><language>en-en</language><copyright>'.$pageaddr.'</copyright>'."\n";
-
- foreach($days as $day=>$linkdates) // For each day.
- {
- $daydate = utf8_encode(strftime('%A %d, %B %Y',linkdate2timestamp($day.'_000000'))); // Full text date
+ echo '<channel>';
+ echo '<title>Daily - '. $GLOBALS['title'] . '</title>';
+ echo '<link>'. $pageaddr .'</link>';
+ echo '<description>Daily shared links</description>';
+ echo '<language>en-en</language>';
+ echo '<copyright>'. $pageaddr .'</copyright>'. 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 '<item><title>'.htmlspecialchars($GLOBALS['title'].' - '.$daydate).'</title><guid>'.$absurl.'</guid><link>'.$absurl.'</link>';
- echo '<pubDate>'.htmlspecialchars($rfc822date)."</pubDate>";
+ $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 '<description><![CDATA['.$html.']]></description>'."\n</item>\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 '</channel></rss><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->';
+ echo '</channel></rss><!-- Cached version of '. escape(pageUrl()) .' -->';
$cache->cache(ob_get_contents());
ob_end_flush();
// "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 ($i<count($days)-1) $nextday=$days[$i+1];
}
- $linksToDisplay=$LINKSDB->filterDay($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.
$PAGE->assign('linksToDisplay',$linksToDisplay);
$PAGE->assign('linkcount',count($LINKSDB));
$PAGE->assign('cols', $columns);
- $PAGE->assign('day',utf8_encode(strftime('%A %d, %B %Y',linkdate2timestamp($day.'_000000'))));
+ $PAGE->assign('day',linkdate2timestamp($day.'_000000'));
$PAGE->assign('previousday',$previousday);
$PAGE->assign('nextday',$nextday);
$PAGE->renderPage('daily');
// 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'))
$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;
}
// -------- User wants to logout.
if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=logout'))
{
- invalidateCaches();
+ invalidateCaches($GLOBALS['config']['PAGECACHE']);
logout();
header('Location: ?');
exit;
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);
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));
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']) {
}
// -------- 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));
}
// -------- 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;
}
exit;
}
+ if (isset($_GET['edit_link'])) {
+ header('Location: ?do=login&edit_link='. escape($_GET['edit_link']));
+ exit;
+ }
+
$PAGE = new pageBuilder;
buildLinkList($PAGE,$LINKSDB); // Compute list of links to display
$PAGE->renderPage('linklist');
// Make sure old password is correct.
$oldhash = sha1($_POST['oldpassword'].$GLOBALS['login'].$GLOBALS['salt']);
- if ($oldhash!=$GLOBALS['hash']) { echo '<script language="JavaScript">alert("The old password is not correct.");document.location=\'?do=changepasswd\';</script>'; exit; }
+ if ($oldhash!=$GLOBALS['hash']) { echo '<script>alert("The old password is not correct.");document.location=\'?do=changepasswd\';</script>'; 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 '<script language="JavaScript">alert("Your password has been changed.");document.location=\'?do=tools\';</script>';
+ 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 '<script>alert("'. $e->getMessage() .'");document.location=\'?do=tools\';</script>';
+ exit;
+ }
+ echo '<script>alert("Your password has been changed.");document.location=\'?do=tools\';</script>';
exit;
}
else // show the change password form.
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 '<script language="JavaScript">alert("Configuration was saved.");document.location=\'?do=tools\';</script>';
+ $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 '<script>alert("'. $e->getMessage() .'");document.location=\'?do=tools\';</script>';
+ exit;
+ }
+ echo '<script>alert("Configuration was saved.");document.location=\'?do=tools\';</script>';
exit;
}
else // Show the configuration form.
$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;
$PAGE = new pageBuilder;
$PAGE->assign('linkcount',count($LINKSDB));
$PAGE->assign('token',getToken());
+ $PAGE->assign('tags', $LINKSDB->allTags());
$PAGE->renderPage('changetag');
exit;
}
$value['tags']=trim(implode(' ',$tags));
$LINKSDB[$key]=$value;
}
- $LINKSDB->savedb(); // Save to disk.
- echo '<script language="JavaScript">alert("Tag was removed from '.count($linksToAlter).' links.");document.location=\'?\';</script>';
+ $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); // Save to disk.
+ echo '<script>alert("Tag was removed from '.count($linksToAlter).' links.");document.location=\'?\';</script>';
exit;
}
$value['tags']=trim(implode(' ',$tags));
$LINKSDB[$key]=$value;
}
- $LINKSDB->savedb(); // Save to disk.
- echo '<script language="JavaScript">alert("Tag was renamed in '.count($linksToAlter).' links.");document.location=\'?searchtags='.urlencode($_POST['totag']).'\';</script>';
+ $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); // Save to disk.
+ echo '<script>alert("Tag was renamed in '.count($linksToAlter).' links.");document.location=\'?searchtags='.urlencode($_POST['totag']).'\';</script>';
exit;
}
}
{
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));
if ($link['title']=='') $link['title']=$link['url']; // If title is empty, use the URL as title.
$LINKSDB[$linkdate] = $link;
- $LINKSDB->savedb(); // Save to disk.
+ $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); // Save to disk.
pubsubhub();
// If we are called from the bookmarklet, we must close the popup:
- if (isset($_GET['source']) && $_GET['source']=='bookmarklet') { echo '<script language="JavaScript">self.close();</script>'; 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 '<script>self.close();</script>'; 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;
}
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 '<script language="JavaScript">self.close();</script>'; exit; }
+ if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo '<script>self.close();</script>'; 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;
}
// - we are protected from XSRF by the token.
$linkdate=$_POST['lf_linkdate'];
unset($LINKSDB[$linkdate]);
- $LINKSDB->savedb(); // save to disk
+ $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); // save to disk
// If we are called from the bookmarklet, we must close the popup:
- if (isset($_GET['source']) && $_GET['source']=='bookmarklet') { echo '<script language="JavaScript">self.close();</script>'; 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 '<script>self.close();</script>'; 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;
}
$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;
}
// -------- User want to post a new link: Display link edit form.
- if (isset($_GET['post']))
- {
- $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);
+ if (isset($_GET['post'])) {
+ $url = new Url($_GET['post']);
+ $url->cleanup();
$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)
+ // Check if URL is not already in database (in this case, we will edit the existing link)
+ $link = $LINKSDB->getLinkFromUrl((string)$url);
if (!$link)
{
- $link_is_new = true; // This is a new link
+ $link_is_new = true;
$linkdate = strval(date('Ymd_His'));
- $title = (empty($_GET['title']) ? '' : $_GET['title'] ); // Get title if it was provided in URL (by the bookmarklet).
- $description = (empty($_GET['description']) ? '' : $_GET['description']); // Get description if it was provided in URL (by the bookmarklet). [Bronco added that]
- $tags = (empty($_GET['tags']) ? '' : $_GET['tags'] ); // Get tags if it was provided in URL
- $private = (!empty($_GET['private']) && $_GET['private'] === "1" ? 1 : 0); // Get private if it was provided in URL
- if (($url!='') && parse_url($url,PHP_URL_SCHEME)=='') $url = 'http://'.$url;
- // 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.)
- if (empty($title) && parse_url($url,PHP_URL_SCHEME)=='http')
- {
+ // Get title if it was provided in URL (by the bookmarklet).
+ $title = (empty($_GET['title']) ? '' : $_GET['title'] );
+ // Get description if it was provided in URL (by the bookmarklet). [Bronco added that]
+ $description = (empty($_GET['description']) ? '' : $_GET['description']);
+ $tags = (empty($_GET['tags']) ? '' : $_GET['tags'] );
+ $private = (!empty($_GET['private']) && $_GET['private'] === "1" ? 1 : 0);
+ // If this is an HTTP(S) link, we try go get the page to extract the title (otherwise we will to straight to the edit form.)
+ if (empty($title) && strpos($url->getScheme(), 'http') !== false) {
list($status,$headers,$data) = getHTTP($url,4); // Short timeout to keep the application responsive.
// FIXME: Decode charset according to specified in either 1) HTTP response headers or 2) <head> in html
- if (strpos($status,'200 OK')!==false)
- {
- // Look for charset in html header.
- preg_match('#<meta .*charset=.*>#Usi', $data, $meta);
-
- // If found, extract encoding.
- if (!empty($meta[0]))
- {
- // Get encoding specified in header.
- preg_match('#charset="?(.*)"#si', $meta[0], $enc);
- // If charset not found, use utf-8.
- $html_charset = (!empty($enc[1])) ? strtolower($enc[1]) : 'utf-8';
- }
- else { $html_charset = 'utf-8'; }
-
- // Extract title
- $title = html_extract_title($data);
- if (!empty($title))
- {
- // Re-encode title in utf-8 if necessary.
- $title = ($html_charset == 'iso-8859-1') ? utf8_encode($title) : $title;
- }
- }
+ if (strpos($status,'200 OK')!==false) {
+ // Look for charset in html header.
+ preg_match('#<meta .*charset=.*>#Usi', $data, $meta);
+
+ // If found, extract encoding.
+ if (!empty($meta[0])) {
+ // Get encoding specified in header.
+ preg_match('#charset="?(.*)"#si', $meta[0], $enc);
+ // If charset not found, use utf-8.
+ $html_charset = (!empty($enc[1])) ? strtolower($enc[1]) : 'utf-8';
+ }
+ else {
+ $html_charset = 'utf-8';
+ }
+
+ // Extract title
+ $title = html_extract_title($data);
+ if (!empty($title)) {
+ // Re-encode title in utf-8 if necessary.
+ $title = ($html_charset == 'iso-8859-1') ? utf8_encode($title) : $title;
+ }
+ }
}
- if ($url=='') // In case of empty URL, this is just a text (with a link that points to itself)
- {
- $url='?'.smallHash($linkdate);
- $title='Note: ';
+ if ($url == '') {
+ $url = '?' . smallHash($linkdate);
+ $title = 'Note: ';
}
- $link = array('linkdate'=>$linkdate,'title'=>$title,'url'=>$url,'description'=>$description,'tags'=>$tags,'private'=>$private);
+ $link = array(
+ 'linkdate' => $linkdate,
+ 'title' => $title,
+ 'url' => (string)$url,
+ 'description' => $description,
+ 'tags' => $tags,
+ 'private' => $private
+ );
}
$PAGE = new pageBuilder;
$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;
}
($exportWhat=='private' && $link['private']!=0) ||
($exportWhat=='public' && $link['private']==0))
{
- echo '<DT><A HREF="'.htmlspecialchars($link['url']).'" ADD_DATE="'.linkdate2timestamp($link['linkdate']).'" PRIVATE="'.$link['private'].'"';
- if ($link['tags']!='') echo ' TAGS="'.htmlspecialchars(str_replace(' ',',',$link['tags'])).'"';
- echo '>'.htmlspecialchars($link['title'])."</A>\n";
- if ($link['description']!='') echo '<DD>'.htmlspecialchars($link['description'])."\n";
+ echo '<DT><A HREF="'.$link['url'].'" ADD_DATE="'.linkdate2timestamp($link['linkdate']).'" PRIVATE="'.$link['private'].'"';
+ if ($link['tags']!='') echo ' TAGS="'.str_replace(' ',',',$link['tags']).'"';
+ echo '>'.$link['title']."</A>\n";
+ if ($link['description']!='') echo '<DD>'.$link['description']."\n";
}
}
exit;
if (!isset($_POST['token']) || (!isset($_FILES)) || (isset($_FILES['filetoupload']['size']) && $_FILES['filetoupload']['size']==0))
{
$returnurl = ( empty($_SERVER['HTTP_REFERER']) ? '?' : $_SERVER['HTTP_REFERER'] );
- echo '<script language="JavaScript">alert("The file you are trying to upload is probably bigger than what this webserver can accept ('.getMaxFileSize().' bytes). Please upload in smaller chunks.");document.location=\''.htmlspecialchars($returnurl).'\';</script>';
+ echo '<script>alert("The file you are trying to upload is probably bigger than what this webserver can accept ('.getMaxFileSize().' bytes). Please upload in smaller chunks.");document.location=\''.escape($returnurl).'\';</script>';
exit;
}
if (!tokenOk($_POST['token'])) die('Wrong token.');
// -------- 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;
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']);
}
}
}
- $LINKSDB->savedb();
+ $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']);
- echo '<script language="JavaScript">alert("File '.json_encode($filename).' ('.$filesize.' bytes) was successfully processed: '.$import_count.' links imported.");document.location=\'?\';</script>';
+ echo '<script>alert("File '.json_encode($filename).' ('.$filesize.' bytes) was successfully processed: '.$import_count.' links imported.");document.location=\'?\';</script>';
}
else
{
- echo '<script language="JavaScript">alert("File '.json_encode($filename).' ('.$filesize.' bytes) has an unknown file format. Nothing was imported.");document.location=\'?\';</script>';
+ echo '<script>alert("File '.json_encode($filename).' ('.$filesize.' bytes) has an unknown file format. Nothing was imported.");document.location=\'?\';</script>';
}
}
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
{
header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found");
echo '<h1>404 Not found.</h1>Oh crap. The link you are trying to reach does not exist or has been deleted.';
- echo '<br>You would mind <a href="?">clicking here</a>?';
+ echo '<br>Would you mind <a href="?">clicking here</a>?';
exit;
}
$search_type='permalink';
else
$linksToDisplay = $LINKSDB; // Otherwise, display without filtering.
+
// Option: Show only private links
if (!empty($_SESSION['privateonly']))
{
while ($i<$end && $i<count($keys))
{
$link = $linksToDisplay[$keys[$i]];
- $link['description']=nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description']))));
+ $link['description']=nl2br(keepMultipleSpaces(text2clickable($link['description'])));
$title=$link['title'];
$classLi = $i%2!=0 ? '' : 'publicLinkHightLight';
$link['class'] = ($link['private']==0 ? $classLi : 'private');
- $link['localdate']=linkdate2locale($link['linkdate']);
+ $link['timestamp']=linkdate2timestamp($link['linkdate']);
$taglist = explode(' ',$link['tags']);
uasort($taglist, 'strcasecmp');
$link['taglist']=$taglist;
+
+ if ($link["url"][0] === '?' && // Check for both signs of a note: starting with ? and 7 chars long. I doubt that you'll post any links that look like this.
+ strlen($link["url"]) === 7) {
+ $link["url"] = indexUrl() . $link["url"];
+ }
+
$linkDisp[$keys[$i]] = $link;
$i++;
}
$PAGE->assign('redirector',empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector']); // Optional redirector URL.
$PAGE->assign('token',$token);
$PAGE->assign('links',$linkDisp);
+ $PAGE->assign('tags', $LINKSDB->allTags());
return;
}
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');
}
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.
$t = computeThumbnail($url,$href);
if (count($t)==0) return ''; // Empty array = no thumbnail for this URL.
- $html='<a href="'.htmlspecialchars($t['href']).'"><img src="'.htmlspecialchars($t['src']).'"';
- if (!empty($t['width'])) $html.=' width="'.htmlspecialchars($t['width']).'"';
- if (!empty($t['height'])) $html.=' height="'.htmlspecialchars($t['height']).'"';
- if (!empty($t['style'])) $html.=' style="'.htmlspecialchars($t['style']).'"';
- if (!empty($t['alt'])) $html.=' alt="'.htmlspecialchars($t['alt']).'"';
+ $html='<a href="'.escape($t['href']).'"><img src="'.escape($t['src']).'"';
+ if (!empty($t['width'])) $html.=' width="'.escape($t['width']).'"';
+ if (!empty($t['height'])) $html.=' height="'.escape($t['height']).'"';
+ if (!empty($t['style'])) $html.=' style="'.escape($t['style']).'"';
+ if (!empty($t['alt'])) $html.=' alt="'.escape($t['alt']).'"';
$html.='></a>';
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...)
$t = computeThumbnail($url,$href);
if (count($t)==0) return ''; // Empty array = no thumbnail for this URL.
- $html='<a href="'.htmlspecialchars($t['href']).'">';
+ $html='<a href="'.escape($t['href']).'">';
- // Lazy image (only loaded by JavaScript when in the viewport).
- if (!empty($GLOBALS['disablejquery'])) // (except if jQuery is disabled)
- $html.='<img class="lazyimage" src="'.htmlspecialchars($t['src']).'"';
- else
- $html.='<img class="lazyimage" src="#" data-original="'.htmlspecialchars($t['src']).'"';
+ // Lazy image
+ $html.='<img class="b-lazy" src="#" data-src="'.escape($t['src']).'"';
- if (!empty($t['width'])) $html.=' width="'.htmlspecialchars($t['width']).'"';
- if (!empty($t['height'])) $html.=' height="'.htmlspecialchars($t['height']).'"';
- if (!empty($t['style'])) $html.=' style="'.htmlspecialchars($t['style']).'"';
- if (!empty($t['alt'])) $html.=' alt="'.htmlspecialchars($t['alt']).'"';
+ if (!empty($t['width'])) $html.=' width="'.escape($t['width']).'"';
+ if (!empty($t['height'])) $html.=' height="'.escape($t['height']).'"';
+ if (!empty($t['style'])) $html.=' style="'.escape($t['style']).'"';
+ if (!empty($t['alt'])) $html.=' alt="'.escape($t['alt']).'"';
$html.='>';
// No-JavaScript fallback.
- $html.='<noscript><img src="'.htmlspecialchars($t['src']).'"';
- if (!empty($t['width'])) $html.=' width="'.htmlspecialchars($t['width']).'"';
- if (!empty($t['height'])) $html.=' height="'.htmlspecialchars($t['height']).'"';
- if (!empty($t['style'])) $html.=' style="'.htmlspecialchars($t['style']).'"';
- if (!empty($t['alt'])) $html.=' alt="'.htmlspecialchars($t['alt']).'"';
+ $html.='<noscript><img src="'.escape($t['src']).'"';
+ if (!empty($t['width'])) $html.=' width="'.escape($t['width']).'"';
+ if (!empty($t['height'])) $html.=' height="'.escape($t['height']).'"';
+ if (!empty($t['style'])) $html.=' style="'.escape($t['style']).'"';
+ if (!empty($t['alt'])) $html.=' alt="'.escape($t['alt']).'"';
$html.='></noscript></a>';
return $html;
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 '<script language="JavaScript">alert("Shaarli is now configured. Please enter your login/password and start shaaring your links!");document.location=\'?do=login\';</script>';
+ $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 '<script>alert("'. $e->getMessage() .'");document.location=\'?\';</script>';
+ exit;
+ }
+ echo '<script>alert("Shaarli is now configured. Please enter your login/password and start shaaring your links!");document.location=\'?do=login\';</script>';
exit;
}
// Display config form:
- list($timezone_form,$timezone_js) = templateTZform();
- $timezone_html=''; if ($timezone_form!='') $timezone_html='<tr><td valign="top"><b>Timezone:</b></td><td>'.$timezone_form.'</td></tr>';
+ list($timezone_form, $timezone_js) = generateTimeZoneForm();
+ $timezone_html = '';
+ if ($timezone_form != '') {
+ $timezone_html = '<tr><td><b>Timezone:</b></td><td>'.$timezone_form.'</td></tr>';
+ }
$PAGE = new pageBuilder;
$PAGE->assign('timezone_html',$timezone_html);
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].='<option value="'.$city.'"'.($pcity==$city?' selected':'').'>'.$city.'</option>';
- }
- }
- $continents_html = '';
- $continents = array_keys($continents);
- foreach($continents as $continent)
- $continents_html.='<option value="'.$continent.'"'.($pcontinent==$continent?' selected':'').'>'.$continent.'</option>';
- $cities_html = $cities[$pcontinent];
- $timezone_form = "Continent: <select name=\"continent\" id=\"continent\" onChange=\"onChangecontinent();\">${continents_html}</select>";
- $timezone_form .= " City: <select name=\"city\" id=\"city\">${cities[$pcontinent]}</select><br />";
- $timezone_js = "<script language=\"JavaScript\">";
- $timezone_js .= "function onChangecontinent(){document.getElementById(\"city\").innerHTML = citiescontinent[document.getElementById(\"continent\").value];}";
- $timezone_js .= "var citiescontinent = ".json_encode($cities).";" ;
- $timezone_js .= "</script>" ;
- 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)) {
}
}
-// 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='<?php $GLOBALS[\'login\']='.var_export($GLOBALS['login'],true).'; $GLOBALS[\'hash\']='.var_export($GLOBALS['hash'],true).'; $GLOBALS[\'salt\']='.var_export($GLOBALS['salt'],true).'; ';
- $config .='$GLOBALS[\'timezone\']='.var_export($GLOBALS['timezone'],true).'; date_default_timezone_set('.var_export($GLOBALS['timezone'],true).'); $GLOBALS[\'title\']='.var_export($GLOBALS['title'],true).';';
- $config .= '$GLOBALS[\'titleLink\']='.var_export($GLOBALS['titleLink'],true).'; ';
- $config .= '$GLOBALS[\'redirector\']='.var_export($GLOBALS['redirector'],true).'; ';
- $config .= '$GLOBALS[\'disablesessionprotection\']='.var_export($GLOBALS['disablesessionprotection'],true).'; ';
- $config .= '$GLOBALS[\'disablejquery\']='.var_export($GLOBALS['disablejquery'],true).'; ';
- $config .= '$GLOBALS[\'privateLinkByDefault\']='.var_export($GLOBALS['privateLinkByDefault'],true).'; ';
- $config .= ' ?>';
- if (!file_put_contents($GLOBALS['config']['CONFIG_FILE'],$config) || strcmp(file_get_contents($GLOBALS['config']['CONFIG_FILE']),$config)!=0)
- {
- echo '<script language="JavaScript">alert("Shaarli could not create the config file. Please make sure Shaarli has the right to write in the folder is it installed in.");document.location=\'?\';</script>';
- 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.
// 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);
return true;
}
-// Invalidate caches when the database is changed or the user logs out.
-// (e.g. tags cache).
-function invalidateCaches()
-{
- unset($_SESSION['tags']); // Purge cache attached to session.
- 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=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();
?>