I reviewed character escaping everywhere with the following ideas:

  * use a single common function to escape user data: `escape` using `htmlspecialchars`.
  * sanitize fields in `index.php` after reading them from datastore and before sending them to templates.
  	It means no escaping function in Twig templates.
    2 reasons:
    * it reduces risks of security issue for future user made templates
    * more readable templates
  * sanitize user configuration fields after loading them.
This commit is contained in:
ArthurHoaro 2015-06-11 13:53:27 +02:00
parent 0923a2bc1b
commit 5f85fcd863
12 changed files with 113 additions and 95 deletions

View file

@ -245,6 +245,11 @@ private function readdb()
foreach ($this->links as $link) { foreach ($this->links as $link) {
$this->urls[$link['url']] = $link['linkdate']; $this->urls[$link['url']] = $link['linkdate'];
} }
// Escape links data
foreach($this->links as &$link) {
sanitizeLink($link);
}
} }
/** /**

159
index.php
View file

@ -98,7 +98,7 @@ function stripslashes_deep($value) { $value = is_array($value) ? array_map('stri
if (!is_writable(realpath(dirname(__FILE__)))) die('<pre>ERROR: Shaarli does not have the right to write in its own directory.</pre>'); 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. // 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['timezone'])) $GLOBALS['timezone']=date_default_timezone_get();
if (empty($GLOBALS['redirector'])) $GLOBALS['redirector']=''; if (empty($GLOBALS['redirector'])) $GLOBALS['redirector']='';
if (empty($GLOBALS['disablesessionprotection'])) $GLOBALS['disablesessionprotection']=false; if (empty($GLOBALS['disablesessionprotection'])) $GLOBALS['disablesessionprotection']=false;
@ -111,6 +111,9 @@ function stripslashes_deep($value) { $value = is_array($value) ? array_map('stri
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. 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 // 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'])); define('STAY_SIGNED_IN_TOKEN', sha1($GLOBALS['hash'].$_SERVER["REMOTE_ADDR"].$GLOBALS['salt']));
@ -272,6 +275,17 @@ function nl2br_escaped($html)
return str_replace('>','&gt;',str_replace('<','&lt;',nl2br($html))); return str_replace('>','&gt;',str_replace('<','&lt;',nl2br($html)));
} }
function escape($str) {
return htmlspecialchars($str, ENT_COMPAT, 'UTF-8', false);
}
function sanitizeLink(&$link) {
$link['url'] = escape($link['url']); // useful?
$link['title'] = escape($link['title']);
$link['description'] = escape($link['description']);
$link['tags'] = escape($link['tags']);
}
// In a string, converts URLs to clickable links. // In a string, converts URLs to clickable links.
// Function inspired from http://www.php.net/manual/en/function.preg-replace.php#85722 // Function inspired from http://www.php.net/manual/en/function.preg-replace.php#85722
function text2clickable($url) function text2clickable($url)
@ -651,8 +665,8 @@ function __construct()
private function initialize() private function initialize()
{ {
$this->tpl = new RainTPL; $this->tpl = new RainTPL;
$this->tpl->assign('newversion',checkUpdate()); $this->tpl->assign('newversion',escape(checkUpdate()));
$this->tpl->assign('feedurl',htmlspecialchars(indexUrl())); $this->tpl->assign('feedurl',escape(indexUrl()));
$searchcrits=''; // Search criteria $searchcrits=''; // Search criteria
if (!empty($_GET['searchtags'])) $searchcrits.='&searchtags='.urlencode($_GET['searchtags']); if (!empty($_GET['searchtags'])) $searchcrits.='&searchtags='.urlencode($_GET['searchtags']);
elseif (!empty($_GET['searchterm'])) $searchcrits.='&searchterm='.urlencode($_GET['searchterm']); elseif (!empty($_GET['searchterm'])) $searchcrits.='&searchterm='.urlencode($_GET['searchterm']);
@ -716,15 +730,15 @@ function showRSS()
$nblinksToDisplay = $_GET['nb']=='all' ? count($linksToDisplay) : max($_GET['nb']+0,1) ; $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 '<?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"; echo '<description>Shared links</description><language>en-en</language><copyright>'.$pageaddr.'</copyright>'."\n\n";
if (!empty($GLOBALS['config']['PUBSUBHUB_URL'])) if (!empty($GLOBALS['config']['PUBSUBHUB_URL']))
{ {
echo '<!-- PubSubHubbub Discovery -->'; echo '<!-- PubSubHubbub Discovery -->';
echo '<link rel="hub" href="'.htmlspecialchars($GLOBALS['config']['PUBSUBHUB_URL']).'" 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="'.htmlspecialchars($pageaddr).'?do=rss" 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 -->'; echo '<!-- End Of PubSubHubbub Discovery -->';
} }
$i=0; $i=0;
@ -734,16 +748,16 @@ function showRSS()
$link = $linksToDisplay[$keys[$i]]; $link = $linksToDisplay[$keys[$i]];
$guid = $pageaddr.'?'.smallHash($link['linkdate']); $guid = $pageaddr.'?'.smallHash($link['linkdate']);
$rfc822date = linkdate2rfc822($link['linkdate']); $rfc822date = linkdate2rfc822($link['linkdate']);
$absurl = htmlspecialchars($link['url']); $absurl = $link['url'];
if (startsWith($absurl,'?')) $absurl=$pageaddr.$absurl; // make permalink URL absolute if (startsWith($absurl,'?')) $absurl=$pageaddr.$absurl; // make permalink URL absolute
if ($usepermalinks===true) if ($usepermalinks===true)
echo '<item><title>'.htmlspecialchars($link['title']).'</title><guid isPermaLink="true">'.$guid.'</guid><link>'.$guid.'</link>'; echo '<item><title>'.$link['title'].'</title><guid isPermaLink="true">'.$guid.'</guid><link>'.$guid.'</link>';
else else
echo '<item><title>'.htmlspecialchars($link['title']).'</title><guid isPermaLink="false">'.$guid.'</guid><link>'.$absurl.'</link>'; echo '<item><title>'.$link['title'].'</title><guid isPermaLink="false">'.$guid.'</guid><link>'.$absurl.'</link>';
if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) echo '<pubDate>'.htmlspecialchars($rfc822date)."</pubDate>\n"; 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) 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 // Add permalink in description
@ -751,10 +765,10 @@ function showRSS()
// If user wants permalinks first, put the final link in description // If user wants permalinks first, put the final link in description
if ($usepermalinks===true) $descriptionlink = '(<a href="'.$absurl.'">Link</a>)'; if ($usepermalinks===true) $descriptionlink = '(<a href="'.$absurl.'">Link</a>)';
if (strlen($link['description'])>0) $descriptionlink = '<br>'.$descriptionlink; 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++; $i++;
} }
echo '</channel></rss><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->'; echo '</channel></rss><!-- Cached version of '.escape(pageUrl()).' -->';
$cache->cache(ob_get_contents()); $cache->cache(ob_get_contents());
ob_end_flush(); ob_end_flush();
@ -779,7 +793,6 @@ function showATOM()
$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(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in).
// Optionally filter the results: // Optionally filter the results:
$linksToDisplay=array(); $linksToDisplay=array();
if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']); if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']);
@ -792,7 +805,7 @@ function showATOM()
$nblinksToDisplay = $_GET['nb']=='all' ? count($linksToDisplay) : max($_GET['nb']+0,1) ; $nblinksToDisplay = $_GET['nb']=='all' ? count($linksToDisplay) : max($_GET['nb']+0,1) ;
} }
$pageaddr=htmlspecialchars(indexUrl()); $pageaddr=escape(indexUrl());
$latestDate = ''; $latestDate = '';
$entries=''; $entries='';
$i=0; $i=0;
@ -803,44 +816,44 @@ function showATOM()
$guid = $pageaddr.'?'.smallHash($link['linkdate']); $guid = $pageaddr.'?'.smallHash($link['linkdate']);
$iso8601date = linkdate2iso8601($link['linkdate']); $iso8601date = linkdate2iso8601($link['linkdate']);
$latestDate = max($latestDate,$iso8601date); $latestDate = max($latestDate,$iso8601date);
$absurl = htmlspecialchars($link['url']); $absurl = $link['url'];
if (startsWith($absurl,'?')) $absurl=$pageaddr.$absurl; // make permalink URL absolute 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) if ($usepermalinks===true)
$entries.='<link href="'.$guid.'" /><id>'.$guid.'</id>'; $entries.='<link href="'.$guid.'" /><id>'.$guid.'</id>';
else else
$entries.='<link href="'.$absurl.'" /><id>'.$guid.'</id>'; $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 // 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 user wants permalinks first, put the final link in description
if ($usepermalinks===true) $descriptionlink = htmlspecialchars('(<a href="'.$absurl.'">Link</a>)'); if ($usepermalinks===true) $descriptionlink = '(<a href="'.$absurl.'">Link</a>)';
if (strlen($link['description'])>0) $descriptionlink = '&lt;br&gt;'.$descriptionlink; 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) if ($link['tags']!='') // Adding tags to each ATOM entry (as mentioned in ATOM specification)
{ {
foreach(explode(' ',$link['tags']) as $tag) 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"; $entries.="</entry>\n";
$i++; $i++;
} }
$feed='<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom">'; $feed='<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom">';
$feed.='<title>'.htmlspecialchars($GLOBALS['title']).'</title>'; $feed.='<title>'.$GLOBALS['title'].'</title>';
if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) $feed.='<updated>'.htmlspecialchars($latestDate).'</updated>'; if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) $feed.='<updated>'.escape($latestDate).'</updated>';
$feed.='<link rel="self" href="'.htmlspecialchars(serverUrl().$_SERVER["REQUEST_URI"]).'" />'; $feed.='<link rel="self" href="'.escape(serverUrl().$_SERVER["REQUEST_URI"]).'" />';
if (!empty($GLOBALS['config']['PUBSUBHUB_URL'])) if (!empty($GLOBALS['config']['PUBSUBHUB_URL']))
{ {
$feed.='<!-- PubSubHubbub Discovery -->'; $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.='<!-- End Of PubSubHubbub Discovery -->';
} }
$feed.='<author><name>'.htmlspecialchars($pageaddr).'</name><uri>'.htmlspecialchars($pageaddr).'</uri></author>'; $feed.='<author><name>'.$pageaddr.'</name><uri>'.$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.='<id>'.$pageaddr.'</id>'."\n\n"; // Yes, I know I should use a real IRI (RFC3987), but the site URL will do.
$feed.=$entries; $feed.=$entries;
$feed.='</feed><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->'; $feed.='</feed><!-- Cached version of '.escape(pageUrl()).' -->';
echo $feed; echo $feed;
$cache->cache(ob_get_contents()); $cache->cache(ob_get_contents());
@ -882,18 +895,18 @@ function showDailyRSS()
// Build the RSS feed. // Build the RSS feed.
header('Content-Type: application/rss+xml; charset=utf-8'); 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 '<?xml version="1.0" encoding="UTF-8"?><rss version="2.0">';
echo '<channel><title>Daily - '.htmlspecialchars($GLOBALS['title']).'</title><link>'.$pageaddr.'</link>'; echo '<channel><title>Daily - '.$GLOBALS['title'].'</title><link>'.$pageaddr.'</link>';
echo '<description>Daily shared links</description><language>en-en</language><copyright>'.$pageaddr.'</copyright>'."\n"; echo '<description>Daily shared links</description><language>en-en</language><copyright>'.$pageaddr.'</copyright>'."\n";
foreach($days as $day=>$linkdates) // For each day. foreach($days as $day=>$linkdates) // For each day.
{ {
$daydate = utf8_encode(strftime('%A %d, %B %Y',linkdate2timestamp($day.'_000000'))); // Full text date $daydate = utf8_encode(strftime('%A %d, %B %Y',linkdate2timestamp($day.'_000000'))); // Full text date
$rfc822date = linkdate2rfc822($day.'_000000'); $rfc822date = linkdate2rfc822($day.'_000000');
$absurl=htmlspecialchars(indexUrl().'?do=daily&day='.$day); // Absolute URL of the corresponding "Daily" page. $absurl=escape(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 '<item><title>'.$GLOBALS['title'].' - '.$daydate.'</title><guid>'.$absurl.'</guid><link>'.$absurl.'</link>';
echo '<pubDate>'.htmlspecialchars($rfc822date)."</pubDate>"; echo '<pubDate>'.escape($rfc822date)."</pubDate>";
// Build the HTML body of this RSS entry. // Build the HTML body of this RSS entry.
$html=''; $html='';
@ -903,7 +916,7 @@ function showDailyRSS()
foreach($linkdates as $linkdate) foreach($linkdates as $linkdate)
{ {
$l = $LINKSDB[$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['thumbnail'] = thumbnail($l['url']);
$l['timestamp'] = linkdate2timestamp($l['linkdate']); $l['timestamp'] = linkdate2timestamp($l['linkdate']);
if (startsWith($l['url'],'?')) $l['url']=indexUrl().$l['url']; // make permalink URL absolute if (startsWith($l['url'],'?')) $l['url']=indexUrl().$l['url']; // make permalink URL absolute
@ -917,7 +930,7 @@ function showDailyRSS()
echo '<description><![CDATA['.$html.']]></description>'."\n</item>\n\n"; echo '<description><![CDATA['.$html.']]></description>'."\n</item>\n\n";
} }
echo '</channel></rss><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->'; echo '</channel></rss><!-- Cached version of '.escape(pageUrl()).' -->';
$cache->cache(ob_get_contents()); $cache->cache(ob_get_contents());
ob_end_flush(); ob_end_flush();
@ -929,7 +942,6 @@ 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(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in).
$day=Date('Ymd',strtotime('-1 day')); // Yesterday, in format YYYYMMDD. $day=Date('Ymd',strtotime('-1 day')); // Yesterday, in format YYYYMMDD.
if (isset($_GET['day'])) $day=$_GET['day']; if (isset($_GET['day'])) $day=$_GET['day'];
@ -948,10 +960,11 @@ function showDaily()
// We pre-format some fields for proper output. // We pre-format some fields for proper output.
foreach($linksToDisplay as $key=>$link) foreach($linksToDisplay as $key=>$link)
{ {
$taglist = explode(' ',$link['tags']); $taglist = explode(' ',$link['tags']);
uasort($taglist, 'strcasecmp'); uasort($taglist, 'strcasecmp');
$linksToDisplay[$key]['taglist']=$taglist; $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]['thumbnail'] = thumbnail($link['url']);
$linksToDisplay[$key]['timestamp'] = linkdate2timestamp($link['linkdate']); $linksToDisplay[$key]['timestamp'] = linkdate2timestamp($link['linkdate']);
} }
@ -1002,7 +1015,7 @@ function renderPage()
$token=''; if (ban_canLogin()) $token=getToken(); // Do not waste token generation if not useful. $token=''; if (ban_canLogin()) $token=getToken(); // Do not waste token generation if not useful.
$PAGE = new pageBuilder; $PAGE = new pageBuilder;
$PAGE->assign('token',$token); $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'); $PAGE->renderPage('loginform');
exit; exit;
} }
@ -1030,7 +1043,7 @@ function renderPage()
// Get only links which have a thumbnail. // Get only links which have a thumbnail.
foreach($links as $link) foreach($links as $link)
{ {
$permalink='?'.htmlspecialchars(smallhash($link['linkdate']),ENT_QUOTES); $permalink='?'.escape(smallhash($link['linkdate']));
$thumb=lazyThumbnail($link['url'],$permalink); $thumb=lazyThumbnail($link['url'],$permalink);
if ($thumb!='') // Only output links which have a thumbnail. if ($thumb!='') // Only output links which have a thumbnail.
{ {
@ -1239,8 +1252,8 @@ function renderPage()
$PAGE = new pageBuilder; $PAGE = new pageBuilder;
$PAGE->assign('linkcount',count($LINKSDB)); $PAGE->assign('linkcount',count($LINKSDB));
$PAGE->assign('token',getToken()); $PAGE->assign('token',getToken());
$PAGE->assign('title',htmlspecialchars( empty($GLOBALS['title']) ? '' : $GLOBALS['title'] , ENT_QUOTES)); $PAGE->assign('title', empty($GLOBALS['title']) ? '' : $GLOBALS['title'] );
$PAGE->assign('redirector',htmlspecialchars( empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'] , ENT_QUOTES)); $PAGE->assign('redirector', empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'] );
list($timezone_form,$timezone_js) = templateTZform($GLOBALS['timezone']); list($timezone_form,$timezone_js) = templateTZform($GLOBALS['timezone']);
$PAGE->assign('timezone_form',$timezone_form); // FIXME: Put entire tz form generation in template? $PAGE->assign('timezone_form',$timezone_form); // FIXME: Put entire tz form generation in template?
$PAGE->assign('timezone_js',$timezone_js); $PAGE->assign('timezone_js',$timezone_js);
@ -1400,7 +1413,7 @@ function renderPage()
$PAGE->assign('link',$link); $PAGE->assign('link',$link);
$PAGE->assign('link_is_new',false); $PAGE->assign('link_is_new',false);
$PAGE->assign('token',getToken()); // XSRF protection. $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->assign('tags', $LINKSDB->allTags());
$PAGE->renderPage('editlink'); $PAGE->renderPage('editlink');
exit; exit;
@ -1524,10 +1537,10 @@ function renderPage()
($exportWhat=='private' && $link['private']!=0) || ($exportWhat=='private' && $link['private']!=0) ||
($exportWhat=='public' && $link['private']==0)) ($exportWhat=='public' && $link['private']==0))
{ {
echo '<DT><A HREF="'.htmlspecialchars($link['url']).'" ADD_DATE="'.linkdate2timestamp($link['linkdate']).'" PRIVATE="'.$link['private'].'"'; echo '<DT><A HREF="'.$link['url'].'" ADD_DATE="'.linkdate2timestamp($link['linkdate']).'" PRIVATE="'.$link['private'].'"';
if ($link['tags']!='') echo ' TAGS="'.htmlspecialchars(str_replace(' ',',',$link['tags'])).'"'; if ($link['tags']!='') echo ' TAGS="'.str_replace(' ',',',$link['tags']).'"';
echo '>'.htmlspecialchars($link['title'])."</A>\n"; echo '>'.$link['title']."</A>\n";
if ($link['description']!='') echo '<DD>'.htmlspecialchars($link['description'])."\n"; if ($link['description']!='') echo '<DD>'.$link['description']."\n";
} }
} }
exit; exit;
@ -1540,7 +1553,7 @@ function renderPage()
if (!isset($_POST['token']) || (!isset($_FILES)) || (isset($_FILES['filetoupload']['size']) && $_FILES['filetoupload']['size']==0)) if (!isset($_POST['token']) || (!isset($_FILES)) || (isset($_FILES['filetoupload']['size']) && $_FILES['filetoupload']['size']==0))
{ {
$returnurl = ( empty($_SERVER['HTTP_REFERER']) ? '?' : $_SERVER['HTTP_REFERER'] ); $returnurl = ( empty($_SERVER['HTTP_REFERER']) ? '?' : $_SERVER['HTTP_REFERER'] );
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=\''.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; exit;
} }
if (!tokenOk($_POST['token'])) die('Wrong token.'); if (!tokenOk($_POST['token'])) die('Wrong token.');
@ -1663,13 +1676,13 @@ function buildLinkList($PAGE,$LINKSDB)
if (isset($_GET['searchterm'])) // Fulltext search if (isset($_GET['searchterm'])) // Fulltext search
{ {
$linksToDisplay = $LINKSDB->filterFulltext(trim($_GET['searchterm'])); $linksToDisplay = $LINKSDB->filterFulltext(trim($_GET['searchterm']));
$search_crits=htmlspecialchars(trim($_GET['searchterm'])); $search_crits=escape(trim($_GET['searchterm']));
$search_type='fulltext'; $search_type='fulltext';
} }
elseif (isset($_GET['searchtags'])) // Search by tag elseif (isset($_GET['searchtags'])) // Search by tag
{ {
$linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags'])); $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags']));
$search_crits=explode(' ',trim($_GET['searchtags'])); $search_crits=explode(' ',escape(trim($_GET['searchtags'])));
$search_type='tags'; $search_type='tags';
} }
elseif (isset($_SERVER['QUERY_STRING']) && preg_match('/[a-zA-Z0-9-_@]{6}(&.+?)?/',$_SERVER['QUERY_STRING'])) // Detect smallHashes in URL elseif (isset($_SERVER['QUERY_STRING']) && preg_match('/[a-zA-Z0-9-_@]{6}(&.+?)?/',$_SERVER['QUERY_STRING'])) // Detect smallHashes in URL
@ -1721,7 +1734,7 @@ function buildLinkList($PAGE,$LINKSDB)
while ($i<$end && $i<count($keys)) while ($i<$end && $i<count($keys))
{ {
$link = $linksToDisplay[$keys[$i]]; $link = $linksToDisplay[$keys[$i]];
$link['description']=nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description'])))); $link['description']=nl2br(keepMultipleSpaces(text2clickable($link['description'])));
$title=$link['title']; $title=$link['title'];
$classLi = $i%2!=0 ? '' : 'publicLinkHightLight'; $classLi = $i%2!=0 ? '' : 'publicLinkHightLight';
$link['class'] = ($link['private']==0 ? $classLi : 'private'); $link['class'] = ($link['private']==0 ? $classLi : 'private');
@ -1867,7 +1880,7 @@ function computeThumbnail($url,$href=false)
if ("/talks/" !== substr($path,0,7)) return array(); // This is not a single video URL. 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) $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'); 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'thumbnail');
} }
@ -1878,7 +1891,7 @@ function computeThumbnail($url,$href=false)
if ($ext=='jpg' || $ext=='jpeg' || $ext=='png' || $ext=='gif') 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) $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'); 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'thumbnail');
} }
return array(); // No thumbnail. return array(); // No thumbnail.
@ -1897,11 +1910,11 @@ function thumbnail($url,$href=false)
$t = computeThumbnail($url,$href); $t = computeThumbnail($url,$href);
if (count($t)==0) return ''; // Empty array = no thumbnail for this URL. if (count($t)==0) return ''; // Empty array = no thumbnail for this URL.
$html='<a href="'.htmlspecialchars($t['href']).'"><img src="'.htmlspecialchars($t['src']).'"'; $html='<a href="'.escape($t['href']).'"><img src="'.escape($t['src']).'"';
if (!empty($t['width'])) $html.=' width="'.htmlspecialchars($t['width']).'"'; if (!empty($t['width'])) $html.=' width="'.escape($t['width']).'"';
if (!empty($t['height'])) $html.=' height="'.htmlspecialchars($t['height']).'"'; if (!empty($t['height'])) $html.=' height="'.escape($t['height']).'"';
if (!empty($t['style'])) $html.=' style="'.htmlspecialchars($t['style']).'"'; if (!empty($t['style'])) $html.=' style="'.escape($t['style']).'"';
if (!empty($t['alt'])) $html.=' alt="'.htmlspecialchars($t['alt']).'"'; if (!empty($t['alt'])) $html.=' alt="'.escape($t['alt']).'"';
$html.='></a>'; $html.='></a>';
return $html; return $html;
} }
@ -1917,23 +1930,23 @@ function lazyThumbnail($url,$href=false)
$t = computeThumbnail($url,$href); $t = computeThumbnail($url,$href);
if (count($t)==0) return ''; // Empty array = no thumbnail for this URL. 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 // Lazy image
$html.='<img class="b-lazy" src="#" data-src="'.htmlspecialchars($t['src']).'"'; $html.='<img class="b-lazy" src="#" data-src="'.escape($t['src']).'"';
if (!empty($t['width'])) $html.=' width="'.htmlspecialchars($t['width']).'"'; if (!empty($t['width'])) $html.=' width="'.escape($t['width']).'"';
if (!empty($t['height'])) $html.=' height="'.htmlspecialchars($t['height']).'"'; if (!empty($t['height'])) $html.=' height="'.escape($t['height']).'"';
if (!empty($t['style'])) $html.=' style="'.htmlspecialchars($t['style']).'"'; if (!empty($t['style'])) $html.=' style="'.escape($t['style']).'"';
if (!empty($t['alt'])) $html.=' alt="'.htmlspecialchars($t['alt']).'"'; if (!empty($t['alt'])) $html.=' alt="'.escape($t['alt']).'"';
$html.='>'; $html.='>';
// No-JavaScript fallback. // No-JavaScript fallback.
$html.='<noscript><img src="'.htmlspecialchars($t['src']).'"'; $html.='<noscript><img src="'.escape($t['src']).'"';
if (!empty($t['width'])) $html.=' width="'.htmlspecialchars($t['width']).'"'; if (!empty($t['width'])) $html.=' width="'.escape($t['width']).'"';
if (!empty($t['height'])) $html.=' height="'.htmlspecialchars($t['height']).'"'; if (!empty($t['height'])) $html.=' height="'.escape($t['height']).'"';
if (!empty($t['style'])) $html.=' style="'.htmlspecialchars($t['style']).'"'; if (!empty($t['style'])) $html.=' style="'.escape($t['style']).'"';
if (!empty($t['alt'])) $html.=' alt="'.htmlspecialchars($t['alt']).'"'; if (!empty($t['alt'])) $html.=' alt="'.escape($t['alt']).'"';
$html.='></noscript></a>'; $html.='></noscript></a>';
return $html; return $html;
@ -1983,7 +1996,7 @@ function install()
$GLOBALS['login'] = $_POST['setlogin']; $GLOBALS['login'] = $_POST['setlogin'];
$GLOBALS['salt'] = sha1(uniqid('',true).'_'.mt_rand()); // Salt renders rainbow-tables attacks useless. $GLOBALS['salt'] = sha1(uniqid('',true).'_'.mt_rand()); // Salt renders rainbow-tables attacks useless.
$GLOBALS['hash'] = sha1($_POST['setpassword'].$GLOBALS['login'].$GLOBALS['salt']); $GLOBALS['hash'] = sha1($_POST['setpassword'].$GLOBALS['login'].$GLOBALS['salt']);
$GLOBALS['title'] = (empty($_POST['title']) ? 'Shared links on '.htmlspecialchars(indexUrl()) : $_POST['title'] ); $GLOBALS['title'] = (empty($_POST['title']) ? 'Shared links on '.escape(indexUrl()) : $_POST['title'] );
$GLOBALS['config']['ENABLE_UPDATECHECK'] = !empty($_POST['updateCheck']); $GLOBALS['config']['ENABLE_UPDATECHECK'] = !empty($_POST['updateCheck']);
writeConfig(); writeConfig();
echo '<script>alert("Shaarli is now configured. Please enter your login/password and start shaaring your links!");document.location=\'?do=login\';</script>'; echo '<script>alert("Shaarli is now configured. Please enter your login/password and start shaaring your links!");document.location=\'?do=login\';</script>';
@ -2210,7 +2223,7 @@ function genThumbnail()
// This is more complex: we have to perform a HTTP request, then parse the result. // 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 // 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); $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) if (strpos($httpstatus,'200 OK')!==false)
{ {
$t = unserialize($data); $t = unserialize($data);

View file

@ -36,12 +36,12 @@
{if="$link.tags"} {if="$link.tags"}
<div class="dailyEntryTags"> <div class="dailyEntryTags">
{loop="link.taglist"} {loop="link.taglist"}
{$value|htmlspecialchars} - {$value} -
{/loop} {/loop}
</div> </div>
{/if} {/if}
<div class="dailyEntryTitle"> <div class="dailyEntryTitle">
<a href="{$link.url}">{$link.title|htmlspecialchars}</a> <a href="{$link.url}">{$link.title}</a>
</div> </div>
{if="$link.thumbnail"} {if="$link.thumbnail"}
<div class="dailyEntryThumbnail">{$link.thumbnail}</div> <div class="dailyEntryThumbnail">{$link.thumbnail}</div>

View file

@ -1,7 +1,7 @@
{loop="links"} {loop="links"}
<h3><a href="{$value.url}">{$value.title|htmlspecialchars}</a></h3> <h3><a href="{$value.url}">{$value.title}</a></h3>
<small>{if="!$GLOBALS['config']['HIDE_TIMESTAMPS']"}{function="strftime('%c', $value.timestamp)"} - {/if}{if="$value.tags"}{$value.tags|htmlspecialchars}{/if}<br> <small>{if="!$GLOBALS['config']['HIDE_TIMESTAMPS']"}{function="strftime('%c', $value.timestamp)"} - {/if}{if="$value.tags"}{$value.tags}{/if}<br>
{$value.url|htmlspecialchars}</small><br> {$value.url}</small><br>
{if="$value.thumbnail"}{$value.thumbnail}{/if}<br> {if="$value.thumbnail"}{$value.thumbnail}{/if}<br>
{if="$value.description"}{$value.formatedDescription}{/if} {if="$value.description"}{$value.formatedDescription}{/if}
<br><br><hr> <br><br><hr>

View file

@ -15,11 +15,11 @@
<div id="editlinkform"> <div id="editlinkform">
<form method="post" name="linkform"> <form method="post" name="linkform">
<input type="hidden" name="lf_linkdate" value="{$link.linkdate}"> <input type="hidden" name="lf_linkdate" value="{$link.linkdate}">
<label for="lf_url"><i>URL</i></label><br><input type="text" name="lf_url" id="lf_url" value="{$link.url|htmlspecialchars}" class="lf_input"><br> <label for="lf_url"><i>URL</i></label><br><input type="text" name="lf_url" id="lf_url" value="{$link.url}" class="lf_input"><br>
<label for="lf_title"><i>Title</i></label><br><input type="text" name="lf_title" id="lf_title" value="{$link.title|htmlspecialchars}" class="lf_input"><br> <label for="lf_title"><i>Title</i></label><br><input type="text" name="lf_title" id="lf_title" value="{$link.title}" class="lf_input"><br>
<label for="lf_description"><i>Description</i></label><br><textarea name="lf_description" id="lf_description" rows="4" cols="25">{$link.description|htmlspecialchars}</textarea><br> <label for="lf_description"><i>Description</i></label><br><textarea name="lf_description" id="lf_description" rows="4" cols="25">{$link.description}</textarea><br>
<label for="lf_tags"><i>Tags</i></label><br> <label for="lf_tags"><i>Tags</i></label><br>
<input type="text" id="lf_tags" name="lf_tags" id="lf_tags" value="{$link.tags|htmlspecialchars}" class="lf_input" <input type="text" id="lf_tags" name="lf_tags" id="lf_tags" value="{$link.tags}" class="lf_input"
data-list="{loop="$tags"}{$key}, {/loop}" data-multiple autocomplete="off" ><br> data-list="{loop="$tags"}{$key}, {/loop}" data-multiple autocomplete="off" ><br>
{if="($link_is_new && $GLOBALS['privateLinkByDefault']==true) || $link.private == true"} {if="($link_is_new && $GLOBALS['privateLinkByDefault']==true) || $link.private == true"}
<input type="checkbox" checked="checked" name="lf_private" id="lf_private"> <input type="checkbox" checked="checked" name="lf_private" id="lf_private">
@ -32,7 +32,7 @@
<input type="submit" value="Cancel" name="cancel_edit" class="bigbutton"> <input type="submit" value="Cancel" name="cancel_edit" class="bigbutton">
{if="!$link_is_new"}<input type="submit" value="Delete" name="delete_link" class="bigbutton delete" onClick="return confirmDeleteLink();">{/if} {if="!$link_is_new"}<input type="submit" value="Delete" name="delete_link" class="bigbutton delete" onClick="return confirmDeleteLink();">{/if}
<input type="hidden" name="token" value="{$token}"> <input type="hidden" name="token" value="{$token}">
{if="$http_referer"}<input type="hidden" name="returnurl" value="{$http_referer|htmlspecialchars}">{/if} {if="$http_referer"}<input type="hidden" name="returnurl" value="{$http_referer}">{/if}
</form> </form>
</div> </div>
</div> </div>

View file

@ -5,11 +5,11 @@
<div id="pageheader"> <div id="pageheader">
{include="page.header"} {include="page.header"}
<div id="uploaddiv"> <div id="uploaddiv">
Import Netscape HTML bookmarks (as exported from Firefox/Chrome/Opera/Delicious/Diigo...) (Max: {$maxfilesize|htmlspecialchars} bytes). Import Netscape HTML bookmarks (as exported from Firefox/Chrome/Opera/Delicious/Diigo...) (Max: {$maxfilesize} bytes).
<form method="POST" action="?do=upload" enctype="multipart/form-data" name="uploadform" id="uploadform"> <form method="POST" action="?do=upload" enctype="multipart/form-data" name="uploadform" id="uploadform">
<input type="hidden" name="token" value="{$token}"> <input type="hidden" name="token" value="{$token}">
<input type="file" name="filetoupload"> <input type="file" name="filetoupload">
<input type="hidden" name="MAX_FILE_SIZE" value="{$maxfilesize|htmlspecialchars}"> <input type="hidden" name="MAX_FILE_SIZE" value="{$maxfilesize}">
<input type="submit" name="import_file" value="Import" class="bigbutton"><br> <input type="submit" name="import_file" value="Import" class="bigbutton"><br>
<input type="checkbox" name="private" id="private"><label for="private">&nbsp;Import all links as private</label><br> <input type="checkbox" name="private" id="private"><label for="private">&nbsp;Import all links as private</label><br>
<input type="checkbox" name="overwrite" id="overwrite"><label for="overwrite">&nbsp;Overwrite existing links</label> <input type="checkbox" name="overwrite" id="overwrite"><label for="overwrite">&nbsp;Overwrite existing links</label>

View file

@ -33,7 +33,7 @@
{if="$search_type=='tags'"} {if="$search_type=='tags'"}
<div id="searchcriteria">{$result_count} results for tags <i> <div id="searchcriteria">{$result_count} results for tags <i>
{loop="search_crits"} {loop="search_crits"}
<span class="linktag" title="Remove tag"><a href="?removetag={$value|htmlspecialchars}">{$value|htmlspecialchars} <span class="remove">x</span></a></span> <span class="linktag" title="Remove tag"><a href="?removetag={$value}">{$value} <span class="remove">x</span></a></span>
{/loop}</i></div> {/loop}</i></div>
{/if} {/if}
{/if} {/if}
@ -50,7 +50,7 @@
<input type="hidden" name="token" value="{$token}"><input type="hidden" name="delete_link"><input type="image" alt="Delete" src="images/delete_icon.png#" title="Delete" class="button_delete" onClick="return confirmDeleteLink();"></form> <input type="hidden" name="token" value="{$token}"><input type="hidden" name="delete_link"><input type="image" alt="Delete" src="images/delete_icon.png#" title="Delete" class="button_delete" onClick="return confirmDeleteLink();"></form>
</div> </div>
{/if} {/if}
<span class="linktitle"><a href="{$redirector}{$value.url|htmlspecialchars}">{$value.title|htmlspecialchars}</a></span> <span class="linktitle"><a href="{$redirector}{$value.url}">{$value.title}</a></span>
<br> <br>
{if="$value.description"}<div class="linkdescription">{$value.description}</div>{/if} {if="$value.description"}<div class="linkdescription">{$value.description}</div>{/if}
{if="!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()"} {if="!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()"}
@ -59,15 +59,15 @@
<span class="linkdate" title="Short link here"><a href="?{$value.linkdate|smallHash}">permalink</a> - </span> <span class="linkdate" title="Short link here"><a href="?{$value.linkdate|smallHash}">permalink</a> - </span>
{/if} {/if}
{if="$GLOBALS['config']['ARCHIVE_ORG']"} {if="$GLOBALS['config']['ARCHIVE_ORG']"}
<span class="linkarchive"><a href="https://web.archive.org/web/{$value.url|htmlspecialchars}">archive</a> - </span> <span class="linkarchive"><a href="https://web.archive.org/web/{$value.url}">archive</a> - </span>
{/if} {/if}
<div class="linkqrcode"><a href="http://qrfree.kaywa.com/?l=1&amp;s=8&amp;d={$scripturl|urlencode}%3F{$value.linkdate|smallHash}" <div class="linkqrcode"><a href="http://qrfree.kaywa.com/?l=1&amp;s=8&amp;d={$scripturl|urlencode}%3F{$value.linkdate|smallHash}"
onclick="return showQrCode(this);" class="qrcode" data-permalink="{$scripturl}?{$value.linkdate|smallHash}"> onclick="return showQrCode(this);" class="qrcode" data-permalink="{$scripturl}?{$value.linkdate|smallHash}">
<img src="images/qrcode.png#" alt="QR-Code" title="{function="strftime('%c', $value.timestamp)"}"></a></div> - <img src="images/qrcode.png#" alt="QR-Code" title="{function="strftime('%c', $value.timestamp)"}"></a></div> -
<a href="{$value.url|htmlspecialchars}"><span class="linkurl" title="Short link">{$value.url|htmlspecialchars}</span></a><br> <a href="{$value.url}"><span class="linkurl" title="Short link">{$value.url}</span></a><br>
{if="$value.tags"} {if="$value.tags"}
<div class="linktaglist"> <div class="linktaglist">
{loop="value.taglist"}<span class="linktag" title="Add tag"><a href="?addtag={$value|urlencode}">{$value|htmlspecialchars}</a></span> {/loop} {loop="value.taglist"}<span class="linktag" title="Add tag"><a href="?addtag={$value|urlencode}">{$value}</a></span> {/loop}
</div> </div>
{/if} {/if}
</div> </div>

View file

@ -17,7 +17,7 @@
<input type="checkbox" name="longlastingsession" id="longlastingsession" tabindex="3"> <input type="checkbox" name="longlastingsession" id="longlastingsession" tabindex="3">
Stay signed in (Do not check on public computers)</label> Stay signed in (Do not check on public computers)</label>
<input type="hidden" name="token" value="{$token}"> <input type="hidden" name="token" value="{$token}">
{if="$returnurl"}<input type="hidden" name="returnurl" value="{$returnurl|htmlspecialchars}">{/if} {if="$returnurl"}<input type="hidden" name="returnurl" value="{$returnurl}">{/if}
</form> </form>
{/if} {/if}
</div> </div>

View file

@ -2,7 +2,7 @@
<b><a href="https://github.com/shaarli/Shaarli">Shaarli</a></b> - The personal, minimalist, super-fast, no-database delicious clone by the <a href="https://github.com/shaarli/Shaarli">Shaarli</a> community - <a href="doc/Home.html">Help/documentation</a> <b><a href="https://github.com/shaarli/Shaarli">Shaarli</a></b> - The personal, minimalist, super-fast, no-database delicious clone by the <a href="https://github.com/shaarli/Shaarli">Shaarli</a> community - <a href="doc/Home.html">Help/documentation</a>
</div> </div>
{if="$newversion"} {if="$newversion"}
<div id="newversion"><span id="version_id">&#x25CF;</span> Shaarli {$newversion|htmlspecialchars} is <a href="https://github.com/shaarli/Shaarli/releases">available</a>.</div> <div id="newversion"><span id="version_id">&#x25CF;</span> Shaarli {$newversion} is <a href="https://github.com/shaarli/Shaarli/releases">available</a>.</div>
{/if} {/if}
{if="isLoggedIn()"} {if="isLoggedIn()"}
<script>function confirmDeleteLink() { var agree=confirm("Are you sure you want to delete this link ?"); if (agree) return true ; else return false ; }</script> <script>function confirmDeleteLink() { var agree=confirm("Are you sure you want to delete this link ?"); if (agree) return true ; else return false ; }</script>

View file

@ -8,7 +8,7 @@
<div id="menu"> <div id="menu">
<ul> <ul>
<li><span id="shaarli_title"> <li><span id="shaarli_title">
<a href="{$titleLink}">{$shaarlititle|htmlspecialchars}</a> <a href="{$titleLink}">{$shaarlititle}</a>
</span> </span>
</li> </li>

View file

@ -9,7 +9,7 @@
<div id="picwall_container"> <div id="picwall_container">
{loop="linksToDisplay"} {loop="linksToDisplay"}
<div class="picwall_pictureframe"> <div class="picwall_pictureframe">
{$value.thumbnail}<a href="{$value.url}"><span class="info">{$value.title|htmlspecialchars}</span></a> {$value.thumbnail}<a href="{$value.url}"><span class="info">{$value.title}</span></a>
</div> </div>
{/loop} {/loop}
</div> </div>

View file

@ -6,7 +6,7 @@
<div class="center"> <div class="center">
<div id="cloudtag"> <div id="cloudtag">
{loop="tags"} {loop="tags"}
<span class="count">{$value.count}</span><a href="?searchtags={$key|urlencode}" style="font-size:{$value.size}pt;">{$key|htmlspecialchars}</a> <span class="count">{$value.count}</span><a href="?searchtags={$key|urlencode}" style="font-size:{$value.size}pt;">{$key}</a>
{/loop} {/loop}
</div> </div>
</div> </div>