aboutsummaryrefslogtreecommitdiffhomepage
path: root/index.php
diff options
context:
space:
mode:
authorArthurHoaro <arthur@hoa.ro>2015-06-11 13:53:27 +0200
committerArthurHoaro <arthur@hoa.ro>2015-06-23 16:35:36 +0200
commit5f85fcd863fe261921953ea3bd1742f3e1b7cf68 (patch)
tree5615922c1c696ec04cc60625a8d401b2b297a462 /index.php
parent0923a2bc1b097bf1def882722db489d83d95c423 (diff)
downloadShaarli-5f85fcd863fe261921953ea3bd1742f3e1b7cf68.tar.gz
Shaarli-5f85fcd863fe261921953ea3bd1742f3e1b7cf68.tar.zst
Shaarli-5f85fcd863fe261921953ea3bd1742f3e1b7cf68.zip
Working on shaarli/Shaarli#224
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.
Diffstat (limited to 'index.php')
-rw-r--r--index.php159
1 files changed, 86 insertions, 73 deletions
diff --git a/index.php b/index.php
index 5aa7116f..39b01a2e 100644
--- a/index.php
+++ b/index.php
@@ -98,7 +98,7 @@ header("Pragma: no-cache");
98if (!is_writable(realpath(dirname(__FILE__)))) die('<pre>ERROR: Shaarli does not have the right to write in its own directory.</pre>'); 98if (!is_writable(realpath(dirname(__FILE__)))) die('<pre>ERROR: Shaarli does not have the right to write in its own directory.</pre>');
99 99
100// Handling of old config file which do not have the new parameters. 100// Handling of old config file which do not have the new parameters.
101if (empty($GLOBALS['title'])) $GLOBALS['title']='Shared links on '.htmlspecialchars(indexUrl()); 101if (empty($GLOBALS['title'])) $GLOBALS['title']='Shared links on '.escape(indexUrl());
102if (empty($GLOBALS['timezone'])) $GLOBALS['timezone']=date_default_timezone_get(); 102if (empty($GLOBALS['timezone'])) $GLOBALS['timezone']=date_default_timezone_get();
103if (empty($GLOBALS['redirector'])) $GLOBALS['redirector']=''; 103if (empty($GLOBALS['redirector'])) $GLOBALS['redirector']='';
104if (empty($GLOBALS['disablesessionprotection'])) $GLOBALS['disablesessionprotection']=false; 104if (empty($GLOBALS['disablesessionprotection'])) $GLOBALS['disablesessionprotection']=false;
@@ -111,6 +111,9 @@ if (empty($GLOBALS['titleLink'])) $GLOBALS['titleLink']='?';
111if (!is_file($GLOBALS['config']['CONFIG_FILE'])) install(); 111if (!is_file($GLOBALS['config']['CONFIG_FILE'])) install();
112 112
113require $GLOBALS['config']['CONFIG_FILE']; // Read login/password hash into $GLOBALS. 113require $GLOBALS['config']['CONFIG_FILE']; // Read login/password hash into $GLOBALS.
114$GLOBALS['title'] = !empty($GLOBALS['title']) ? escape($GLOBALS['title']) : '';
115$GLOBALS['titleLink'] = !empty($GLOBALS['titleLink']) ? escape($GLOBALS['titleLink']) : '';
116$GLOBALS['redirector'] = !empty($GLOBALS['redirector']) ? escape($GLOBALS['redirector']) : '';
114 117
115// a token depending of deployment salt, user password, and the current ip 118// a token depending of deployment salt, user password, and the current ip
116define('STAY_SIGNED_IN_TOKEN', sha1($GLOBALS['hash'].$_SERVER["REMOTE_ADDR"].$GLOBALS['salt'])); 119define('STAY_SIGNED_IN_TOKEN', sha1($GLOBALS['hash'].$_SERVER["REMOTE_ADDR"].$GLOBALS['salt']));
@@ -272,6 +275,17 @@ function nl2br_escaped($html)
272 return str_replace('>','&gt;',str_replace('<','&lt;',nl2br($html))); 275 return str_replace('>','&gt;',str_replace('<','&lt;',nl2br($html)));
273} 276}
274 277
278function escape($str) {
279 return htmlspecialchars($str, ENT_COMPAT, 'UTF-8', false);
280}
281
282function sanitizeLink(&$link) {
283 $link['url'] = escape($link['url']); // useful?
284 $link['title'] = escape($link['title']);
285 $link['description'] = escape($link['description']);
286 $link['tags'] = escape($link['tags']);
287}
288
275// In a string, converts URLs to clickable links. 289// In a string, converts URLs to clickable links.
276// Function inspired from http://www.php.net/manual/en/function.preg-replace.php#85722 290// Function inspired from http://www.php.net/manual/en/function.preg-replace.php#85722
277function text2clickable($url) 291function text2clickable($url)
@@ -651,8 +665,8 @@ class pageBuilder
651 private function initialize() 665 private function initialize()
652 { 666 {
653 $this->tpl = new RainTPL; 667 $this->tpl = new RainTPL;
654 $this->tpl->assign('newversion',checkUpdate()); 668 $this->tpl->assign('newversion',escape(checkUpdate()));
655 $this->tpl->assign('feedurl',htmlspecialchars(indexUrl())); 669 $this->tpl->assign('feedurl',escape(indexUrl()));
656 $searchcrits=''; // Search criteria 670 $searchcrits=''; // Search criteria
657 if (!empty($_GET['searchtags'])) $searchcrits.='&searchtags='.urlencode($_GET['searchtags']); 671 if (!empty($_GET['searchtags'])) $searchcrits.='&searchtags='.urlencode($_GET['searchtags']);
658 elseif (!empty($_GET['searchterm'])) $searchcrits.='&searchterm='.urlencode($_GET['searchterm']); 672 elseif (!empty($_GET['searchterm'])) $searchcrits.='&searchterm='.urlencode($_GET['searchterm']);
@@ -716,15 +730,15 @@ function showRSS()
716 $nblinksToDisplay = $_GET['nb']=='all' ? count($linksToDisplay) : max($_GET['nb']+0,1) ; 730 $nblinksToDisplay = $_GET['nb']=='all' ? count($linksToDisplay) : max($_GET['nb']+0,1) ;
717 } 731 }
718 732
719 $pageaddr=htmlspecialchars(indexUrl()); 733 $pageaddr=escape(indexUrl());
720 echo '<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">'; 734 echo '<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">';
721 echo '<channel><title>'.htmlspecialchars($GLOBALS['title']).'</title><link>'.$pageaddr.'</link>'; 735 echo '<channel><title>'.$GLOBALS['title'].'</title><link>'.$pageaddr.'</link>';
722 echo '<description>Shared links</description><language>en-en</language><copyright>'.$pageaddr.'</copyright>'."\n\n"; 736 echo '<description>Shared links</description><language>en-en</language><copyright>'.$pageaddr.'</copyright>'."\n\n";
723 if (!empty($GLOBALS['config']['PUBSUBHUB_URL'])) 737 if (!empty($GLOBALS['config']['PUBSUBHUB_URL']))
724 { 738 {
725 echo '<!-- PubSubHubbub Discovery -->'; 739 echo '<!-- PubSubHubbub Discovery -->';
726 echo '<link rel="hub" href="'.htmlspecialchars($GLOBALS['config']['PUBSUBHUB_URL']).'" xmlns="http://www.w3.org/2005/Atom" />'; 740 echo '<link rel="hub" href="'.escape($GLOBALS['config']['PUBSUBHUB_URL']).'" xmlns="http://www.w3.org/2005/Atom" />';
727 echo '<link rel="self" href="'.htmlspecialchars($pageaddr).'?do=rss" xmlns="http://www.w3.org/2005/Atom" />'; 741 echo '<link rel="self" href="'.$pageaddr.'?do=rss" xmlns="http://www.w3.org/2005/Atom" />';
728 echo '<!-- End Of PubSubHubbub Discovery -->'; 742 echo '<!-- End Of PubSubHubbub Discovery -->';
729 } 743 }
730 $i=0; 744 $i=0;
@@ -734,16 +748,16 @@ function showRSS()
734 $link = $linksToDisplay[$keys[$i]]; 748 $link = $linksToDisplay[$keys[$i]];
735 $guid = $pageaddr.'?'.smallHash($link['linkdate']); 749 $guid = $pageaddr.'?'.smallHash($link['linkdate']);
736 $rfc822date = linkdate2rfc822($link['linkdate']); 750 $rfc822date = linkdate2rfc822($link['linkdate']);
737 $absurl = htmlspecialchars($link['url']); 751 $absurl = $link['url'];
738 if (startsWith($absurl,'?')) $absurl=$pageaddr.$absurl; // make permalink URL absolute 752 if (startsWith($absurl,'?')) $absurl=$pageaddr.$absurl; // make permalink URL absolute
739 if ($usepermalinks===true) 753 if ($usepermalinks===true)
740 echo '<item><title>'.htmlspecialchars($link['title']).'</title><guid isPermaLink="true">'.$guid.'</guid><link>'.$guid.'</link>'; 754 echo '<item><title>'.$link['title'].'</title><guid isPermaLink="true">'.$guid.'</guid><link>'.$guid.'</link>';
741 else 755 else
742 echo '<item><title>'.htmlspecialchars($link['title']).'</title><guid isPermaLink="false">'.$guid.'</guid><link>'.$absurl.'</link>'; 756 echo '<item><title>'.$link['title'].'</title><guid isPermaLink="false">'.$guid.'</guid><link>'.$absurl.'</link>';
743 if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) echo '<pubDate>'.htmlspecialchars($rfc822date)."</pubDate>\n"; 757 if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) echo '<pubDate>'.escape($rfc822date)."</pubDate>\n";
744 if ($link['tags']!='') // Adding tags to each RSS entry (as mentioned in RSS specification) 758 if ($link['tags']!='') // Adding tags to each RSS entry (as mentioned in RSS specification)
745 { 759 {
746 foreach(explode(' ',$link['tags']) as $tag) { echo '<category domain="'.htmlspecialchars($pageaddr).'">'.htmlspecialchars($tag).'</category>'."\n"; } 760 foreach(explode(' ',$link['tags']) as $tag) { echo '<category domain="'.$pageaddr.'">'.$tag.'</category>'."\n"; }
747 } 761 }
748 762
749 // Add permalink in description 763 // Add permalink in description
@@ -751,10 +765,10 @@ function showRSS()
751 // If user wants permalinks first, put the final link in description 765 // If user wants permalinks first, put the final link in description
752 if ($usepermalinks===true) $descriptionlink = '(<a href="'.$absurl.'">Link</a>)'; 766 if ($usepermalinks===true) $descriptionlink = '(<a href="'.$absurl.'">Link</a>)';
753 if (strlen($link['description'])>0) $descriptionlink = '<br>'.$descriptionlink; 767 if (strlen($link['description'])>0) $descriptionlink = '<br>'.$descriptionlink;
754 echo '<description><![CDATA['.nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description'])))).$descriptionlink.']]></description>'."\n</item>\n"; 768 echo '<description><![CDATA['.nl2br(keepMultipleSpaces(text2clickable($link['description']))).$descriptionlink.']]></description>'."\n</item>\n";
755 $i++; 769 $i++;
756 } 770 }
757 echo '</channel></rss><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->'; 771 echo '</channel></rss><!-- Cached version of '.escape(pageUrl()).' -->';
758 772
759 $cache->cache(ob_get_contents()); 773 $cache->cache(ob_get_contents());
760 ob_end_flush(); 774 ob_end_flush();
@@ -779,7 +793,6 @@ function showATOM()
779 793
780 $LINKSDB = new LinkDB(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in). 794 $LINKSDB = new LinkDB(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in).
781 795
782
783 // Optionally filter the results: 796 // Optionally filter the results:
784 $linksToDisplay=array(); 797 $linksToDisplay=array();
785 if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']); 798 if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']);
@@ -792,7 +805,7 @@ function showATOM()
792 $nblinksToDisplay = $_GET['nb']=='all' ? count($linksToDisplay) : max($_GET['nb']+0,1) ; 805 $nblinksToDisplay = $_GET['nb']=='all' ? count($linksToDisplay) : max($_GET['nb']+0,1) ;
793 } 806 }
794 807
795 $pageaddr=htmlspecialchars(indexUrl()); 808 $pageaddr=escape(indexUrl());
796 $latestDate = ''; 809 $latestDate = '';
797 $entries=''; 810 $entries='';
798 $i=0; 811 $i=0;
@@ -803,44 +816,44 @@ function showATOM()
803 $guid = $pageaddr.'?'.smallHash($link['linkdate']); 816 $guid = $pageaddr.'?'.smallHash($link['linkdate']);
804 $iso8601date = linkdate2iso8601($link['linkdate']); 817 $iso8601date = linkdate2iso8601($link['linkdate']);
805 $latestDate = max($latestDate,$iso8601date); 818 $latestDate = max($latestDate,$iso8601date);
806 $absurl = htmlspecialchars($link['url']); 819 $absurl = $link['url'];
807 if (startsWith($absurl,'?')) $absurl=$pageaddr.$absurl; // make permalink URL absolute 820 if (startsWith($absurl,'?')) $absurl=$pageaddr.$absurl; // make permalink URL absolute
808 $entries.='<entry><title>'.htmlspecialchars($link['title']).'</title>'; 821 $entries.='<entry><title>'.$link['title'].'</title>';
809 if ($usepermalinks===true) 822 if ($usepermalinks===true)
810 $entries.='<link href="'.$guid.'" /><id>'.$guid.'</id>'; 823 $entries.='<link href="'.$guid.'" /><id>'.$guid.'</id>';
811 else 824 else
812 $entries.='<link href="'.$absurl.'" /><id>'.$guid.'</id>'; 825 $entries.='<link href="'.$absurl.'" /><id>'.$guid.'</id>';
813 if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) $entries.='<updated>'.htmlspecialchars($iso8601date).'</updated>'; 826 if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) $entries.='<updated>'.escape($iso8601date).'</updated>';
814 827
815 // Add permalink in description 828 // Add permalink in description
816 $descriptionlink = htmlspecialchars('(<a href="'.$guid.'">Permalink</a>)'); 829 $descriptionlink = '(<a href="'.$guid.'">Permalink</a>)';
817 // If user wants permalinks first, put the final link in description 830 // If user wants permalinks first, put the final link in description
818 if ($usepermalinks===true) $descriptionlink = htmlspecialchars('(<a href="'.$absurl.'">Link</a>)'); 831 if ($usepermalinks===true) $descriptionlink = '(<a href="'.$absurl.'">Link</a>)';
819 if (strlen($link['description'])>0) $descriptionlink = '&lt;br&gt;'.$descriptionlink; 832 if (strlen($link['description'])>0) $descriptionlink = '<br>'.$descriptionlink;
820 833
821 $entries.='<content type="html">'.htmlspecialchars(nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description']))))).$descriptionlink."</content>\n"; 834 $entries.='<content type="html"><![CDATA['.nl2br(keepMultipleSpaces(text2clickable($link['description']))).$descriptionlink."]]></content>\n";
822 if ($link['tags']!='') // Adding tags to each ATOM entry (as mentioned in ATOM specification) 835 if ($link['tags']!='') // Adding tags to each ATOM entry (as mentioned in ATOM specification)
823 { 836 {
824 foreach(explode(' ',$link['tags']) as $tag) 837 foreach(explode(' ',$link['tags']) as $tag)
825 { $entries.='<category scheme="'.htmlspecialchars($pageaddr,ENT_QUOTES).'" term="'.htmlspecialchars($tag,ENT_QUOTES).'" />'."\n"; } 838 { $entries.='<category scheme="'.$pageaddr.'" term="'.$tag.'" />'."\n"; }
826 } 839 }
827 $entries.="</entry>\n"; 840 $entries.="</entry>\n";
828 $i++; 841 $i++;
829 } 842 }
830 $feed='<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom">'; 843 $feed='<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom">';
831 $feed.='<title>'.htmlspecialchars($GLOBALS['title']).'</title>'; 844 $feed.='<title>'.$GLOBALS['title'].'</title>';
832 if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) $feed.='<updated>'.htmlspecialchars($latestDate).'</updated>'; 845 if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) $feed.='<updated>'.escape($latestDate).'</updated>';
833 $feed.='<link rel="self" href="'.htmlspecialchars(serverUrl().$_SERVER["REQUEST_URI"]).'" />'; 846 $feed.='<link rel="self" href="'.escape(serverUrl().$_SERVER["REQUEST_URI"]).'" />';
834 if (!empty($GLOBALS['config']['PUBSUBHUB_URL'])) 847 if (!empty($GLOBALS['config']['PUBSUBHUB_URL']))
835 { 848 {
836 $feed.='<!-- PubSubHubbub Discovery -->'; 849 $feed.='<!-- PubSubHubbub Discovery -->';
837 $feed.='<link rel="hub" href="'.htmlspecialchars($GLOBALS['config']['PUBSUBHUB_URL']).'" />'; 850 $feed.='<link rel="hub" href="'.escape($GLOBALS['config']['PUBSUBHUB_URL']).'" />';
838 $feed.='<!-- End Of PubSubHubbub Discovery -->'; 851 $feed.='<!-- End Of PubSubHubbub Discovery -->';
839 } 852 }
840 $feed.='<author><name>'.htmlspecialchars($pageaddr).'</name><uri>'.htmlspecialchars($pageaddr).'</uri></author>'; 853 $feed.='<author><name>'.$pageaddr.'</name><uri>'.$pageaddr.'</uri></author>';
841 $feed.='<id>'.htmlspecialchars($pageaddr).'</id>'."\n\n"; // Yes, I know I should use a real IRI (RFC3987), but the site URL will do. 854 $feed.='<id>'.$pageaddr.'</id>'."\n\n"; // Yes, I know I should use a real IRI (RFC3987), but the site URL will do.
842 $feed.=$entries; 855 $feed.=$entries;
843 $feed.='</feed><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->'; 856 $feed.='</feed><!-- Cached version of '.escape(pageUrl()).' -->';
844 echo $feed; 857 echo $feed;
845 858
846 $cache->cache(ob_get_contents()); 859 $cache->cache(ob_get_contents());
@@ -882,18 +895,18 @@ function showDailyRSS()
882 895
883 // Build the RSS feed. 896 // Build the RSS feed.
884 header('Content-Type: application/rss+xml; charset=utf-8'); 897 header('Content-Type: application/rss+xml; charset=utf-8');
885 $pageaddr=htmlspecialchars(indexUrl()); 898 $pageaddr=escape(indexUrl());
886 echo '<?xml version="1.0" encoding="UTF-8"?><rss version="2.0">'; 899 echo '<?xml version="1.0" encoding="UTF-8"?><rss version="2.0">';
887 echo '<channel><title>Daily - '.htmlspecialchars($GLOBALS['title']).'</title><link>'.$pageaddr.'</link>'; 900 echo '<channel><title>Daily - '.$GLOBALS['title'].'</title><link>'.$pageaddr.'</link>';
888 echo '<description>Daily shared links</description><language>en-en</language><copyright>'.$pageaddr.'</copyright>'."\n"; 901 echo '<description>Daily shared links</description><language>en-en</language><copyright>'.$pageaddr.'</copyright>'."\n";
889 902
890 foreach($days as $day=>$linkdates) // For each day. 903 foreach($days as $day=>$linkdates) // For each day.
891 { 904 {
892 $daydate = utf8_encode(strftime('%A %d, %B %Y',linkdate2timestamp($day.'_000000'))); // Full text date 905 $daydate = utf8_encode(strftime('%A %d, %B %Y',linkdate2timestamp($day.'_000000'))); // Full text date
893 $rfc822date = linkdate2rfc822($day.'_000000'); 906 $rfc822date = linkdate2rfc822($day.'_000000');
894 $absurl=htmlspecialchars(indexUrl().'?do=daily&day='.$day); // Absolute URL of the corresponding "Daily" page. 907 $absurl=escape(indexUrl().'?do=daily&day='.$day); // Absolute URL of the corresponding "Daily" page.
895 echo '<item><title>'.htmlspecialchars($GLOBALS['title'].' - '.$daydate).'</title><guid>'.$absurl.'</guid><link>'.$absurl.'</link>'; 908 echo '<item><title>'.$GLOBALS['title'].' - '.$daydate.'</title><guid>'.$absurl.'</guid><link>'.$absurl.'</link>';
896 echo '<pubDate>'.htmlspecialchars($rfc822date)."</pubDate>"; 909 echo '<pubDate>'.escape($rfc822date)."</pubDate>";
897 910
898 // Build the HTML body of this RSS entry. 911 // Build the HTML body of this RSS entry.
899 $html=''; 912 $html='';
@@ -903,7 +916,7 @@ function showDailyRSS()
903 foreach($linkdates as $linkdate) 916 foreach($linkdates as $linkdate)
904 { 917 {
905 $l = $LINKSDB[$linkdate]; 918 $l = $LINKSDB[$linkdate];
906 $l['formatedDescription']=nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($l['description'])))); 919 $l['formatedDescription']=nl2br(keepMultipleSpaces(text2clickable($l['description'])));
907 $l['thumbnail'] = thumbnail($l['url']); 920 $l['thumbnail'] = thumbnail($l['url']);
908 $l['timestamp'] = linkdate2timestamp($l['linkdate']); 921 $l['timestamp'] = linkdate2timestamp($l['linkdate']);
909 if (startsWith($l['url'],'?')) $l['url']=indexUrl().$l['url']; // make permalink URL absolute 922 if (startsWith($l['url'],'?')) $l['url']=indexUrl().$l['url']; // make permalink URL absolute
@@ -917,7 +930,7 @@ function showDailyRSS()
917 echo '<description><![CDATA['.$html.']]></description>'."\n</item>\n\n"; 930 echo '<description><![CDATA['.$html.']]></description>'."\n</item>\n\n";
918 931
919 } 932 }
920 echo '</channel></rss><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->'; 933 echo '</channel></rss><!-- Cached version of '.escape(pageUrl()).' -->';
921 934
922 $cache->cache(ob_get_contents()); 935 $cache->cache(ob_get_contents());
923 ob_end_flush(); 936 ob_end_flush();
@@ -929,7 +942,6 @@ function showDaily()
929{ 942{
930 $LINKSDB = new LinkDB(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in). 943 $LINKSDB = new LinkDB(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in).
931 944
932
933 $day=Date('Ymd',strtotime('-1 day')); // Yesterday, in format YYYYMMDD. 945 $day=Date('Ymd',strtotime('-1 day')); // Yesterday, in format YYYYMMDD.
934 if (isset($_GET['day'])) $day=$_GET['day']; 946 if (isset($_GET['day'])) $day=$_GET['day'];
935 947
@@ -948,10 +960,11 @@ function showDaily()
948 // We pre-format some fields for proper output. 960 // We pre-format some fields for proper output.
949 foreach($linksToDisplay as $key=>$link) 961 foreach($linksToDisplay as $key=>$link)
950 { 962 {
963
951 $taglist = explode(' ',$link['tags']); 964 $taglist = explode(' ',$link['tags']);
952 uasort($taglist, 'strcasecmp'); 965 uasort($taglist, 'strcasecmp');
953 $linksToDisplay[$key]['taglist']=$taglist; 966 $linksToDisplay[$key]['taglist']=$taglist;
954 $linksToDisplay[$key]['formatedDescription']=nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description'])))); 967 $linksToDisplay[$key]['formatedDescription']=nl2br(keepMultipleSpaces(text2clickable($link['description'])));
955 $linksToDisplay[$key]['thumbnail'] = thumbnail($link['url']); 968 $linksToDisplay[$key]['thumbnail'] = thumbnail($link['url']);
956 $linksToDisplay[$key]['timestamp'] = linkdate2timestamp($link['linkdate']); 969 $linksToDisplay[$key]['timestamp'] = linkdate2timestamp($link['linkdate']);
957 } 970 }
@@ -1002,7 +1015,7 @@ function renderPage()
1002 $token=''; if (ban_canLogin()) $token=getToken(); // Do not waste token generation if not useful. 1015 $token=''; if (ban_canLogin()) $token=getToken(); // Do not waste token generation if not useful.
1003 $PAGE = new pageBuilder; 1016 $PAGE = new pageBuilder;
1004 $PAGE->assign('token',$token); 1017 $PAGE->assign('token',$token);
1005 $PAGE->assign('returnurl',(isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER']:'')); 1018 $PAGE->assign('returnurl',(isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']):''));
1006 $PAGE->renderPage('loginform'); 1019 $PAGE->renderPage('loginform');
1007 exit; 1020 exit;
1008 } 1021 }
@@ -1030,7 +1043,7 @@ function renderPage()
1030 // Get only links which have a thumbnail. 1043 // Get only links which have a thumbnail.
1031 foreach($links as $link) 1044 foreach($links as $link)
1032 { 1045 {
1033 $permalink='?'.htmlspecialchars(smallhash($link['linkdate']),ENT_QUOTES); 1046 $permalink='?'.escape(smallhash($link['linkdate']));
1034 $thumb=lazyThumbnail($link['url'],$permalink); 1047 $thumb=lazyThumbnail($link['url'],$permalink);
1035 if ($thumb!='') // Only output links which have a thumbnail. 1048 if ($thumb!='') // Only output links which have a thumbnail.
1036 { 1049 {
@@ -1239,8 +1252,8 @@ function renderPage()
1239 $PAGE = new pageBuilder; 1252 $PAGE = new pageBuilder;
1240 $PAGE->assign('linkcount',count($LINKSDB)); 1253 $PAGE->assign('linkcount',count($LINKSDB));
1241 $PAGE->assign('token',getToken()); 1254 $PAGE->assign('token',getToken());
1242 $PAGE->assign('title',htmlspecialchars( empty($GLOBALS['title']) ? '' : $GLOBALS['title'] , ENT_QUOTES)); 1255 $PAGE->assign('title', empty($GLOBALS['title']) ? '' : $GLOBALS['title'] );
1243 $PAGE->assign('redirector',htmlspecialchars( empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'] , ENT_QUOTES)); 1256 $PAGE->assign('redirector', empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'] );
1244 list($timezone_form,$timezone_js) = templateTZform($GLOBALS['timezone']); 1257 list($timezone_form,$timezone_js) = templateTZform($GLOBALS['timezone']);
1245 $PAGE->assign('timezone_form',$timezone_form); // FIXME: Put entire tz form generation in template? 1258 $PAGE->assign('timezone_form',$timezone_form); // FIXME: Put entire tz form generation in template?
1246 $PAGE->assign('timezone_js',$timezone_js); 1259 $PAGE->assign('timezone_js',$timezone_js);
@@ -1400,7 +1413,7 @@ function renderPage()
1400 $PAGE->assign('link',$link); 1413 $PAGE->assign('link',$link);
1401 $PAGE->assign('link_is_new',false); 1414 $PAGE->assign('link_is_new',false);
1402 $PAGE->assign('token',getToken()); // XSRF protection. 1415 $PAGE->assign('token',getToken()); // XSRF protection.
1403 $PAGE->assign('http_referer',(isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '')); 1416 $PAGE->assign('http_referer',(isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : ''));
1404 $PAGE->assign('tags', $LINKSDB->allTags()); 1417 $PAGE->assign('tags', $LINKSDB->allTags());
1405 $PAGE->renderPage('editlink'); 1418 $PAGE->renderPage('editlink');
1406 exit; 1419 exit;
@@ -1524,10 +1537,10 @@ HTML;
1524 ($exportWhat=='private' && $link['private']!=0) || 1537 ($exportWhat=='private' && $link['private']!=0) ||
1525 ($exportWhat=='public' && $link['private']==0)) 1538 ($exportWhat=='public' && $link['private']==0))
1526 { 1539 {
1527 echo '<DT><A HREF="'.htmlspecialchars($link['url']).'" ADD_DATE="'.linkdate2timestamp($link['linkdate']).'" PRIVATE="'.$link['private'].'"'; 1540 echo '<DT><A HREF="'.$link['url'].'" ADD_DATE="'.linkdate2timestamp($link['linkdate']).'" PRIVATE="'.$link['private'].'"';
1528 if ($link['tags']!='') echo ' TAGS="'.htmlspecialchars(str_replace(' ',',',$link['tags'])).'"'; 1541 if ($link['tags']!='') echo ' TAGS="'.str_replace(' ',',',$link['tags']).'"';
1529 echo '>'.htmlspecialchars($link['title'])."</A>\n"; 1542 echo '>'.$link['title']."</A>\n";
1530 if ($link['description']!='') echo '<DD>'.htmlspecialchars($link['description'])."\n"; 1543 if ($link['description']!='') echo '<DD>'.$link['description']."\n";
1531 } 1544 }
1532 } 1545 }
1533 exit; 1546 exit;
@@ -1540,7 +1553,7 @@ HTML;
1540 if (!isset($_POST['token']) || (!isset($_FILES)) || (isset($_FILES['filetoupload']['size']) && $_FILES['filetoupload']['size']==0)) 1553 if (!isset($_POST['token']) || (!isset($_FILES)) || (isset($_FILES['filetoupload']['size']) && $_FILES['filetoupload']['size']==0))
1541 { 1554 {
1542 $returnurl = ( empty($_SERVER['HTTP_REFERER']) ? '?' : $_SERVER['HTTP_REFERER'] ); 1555 $returnurl = ( empty($_SERVER['HTTP_REFERER']) ? '?' : $_SERVER['HTTP_REFERER'] );
1543 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>'; 1556 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>';
1544 exit; 1557 exit;
1545 } 1558 }
1546 if (!tokenOk($_POST['token'])) die('Wrong token.'); 1559 if (!tokenOk($_POST['token'])) die('Wrong token.');
@@ -1663,13 +1676,13 @@ function buildLinkList($PAGE,$LINKSDB)
1663 if (isset($_GET['searchterm'])) // Fulltext search 1676 if (isset($_GET['searchterm'])) // Fulltext search
1664 { 1677 {
1665 $linksToDisplay = $LINKSDB->filterFulltext(trim($_GET['searchterm'])); 1678 $linksToDisplay = $LINKSDB->filterFulltext(trim($_GET['searchterm']));
1666 $search_crits=htmlspecialchars(trim($_GET['searchterm'])); 1679 $search_crits=escape(trim($_GET['searchterm']));
1667 $search_type='fulltext'; 1680 $search_type='fulltext';
1668 } 1681 }
1669 elseif (isset($_GET['searchtags'])) // Search by tag 1682 elseif (isset($_GET['searchtags'])) // Search by tag
1670 { 1683 {
1671 $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags'])); 1684 $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags']));
1672 $search_crits=explode(' ',trim($_GET['searchtags'])); 1685 $search_crits=explode(' ',escape(trim($_GET['searchtags'])));
1673 $search_type='tags'; 1686 $search_type='tags';
1674 } 1687 }
1675 elseif (isset($_SERVER['QUERY_STRING']) && preg_match('/[a-zA-Z0-9-_@]{6}(&.+?)?/',$_SERVER['QUERY_STRING'])) // Detect smallHashes in URL 1688 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)
1721 while ($i<$end && $i<count($keys)) 1734 while ($i<$end && $i<count($keys))
1722 { 1735 {
1723 $link = $linksToDisplay[$keys[$i]]; 1736 $link = $linksToDisplay[$keys[$i]];
1724 $link['description']=nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description'])))); 1737 $link['description']=nl2br(keepMultipleSpaces(text2clickable($link['description'])));
1725 $title=$link['title']; 1738 $title=$link['title'];
1726 $classLi = $i%2!=0 ? '' : 'publicLinkHightLight'; 1739 $classLi = $i%2!=0 ? '' : 'publicLinkHightLight';
1727 $link['class'] = ($link['private']==0 ? $classLi : 'private'); 1740 $link['class'] = ($link['private']==0 ? $classLi : 'private');
@@ -1867,7 +1880,7 @@ function computeThumbnail($url,$href=false)
1867 if ("/talks/" !== substr($path,0,7)) return array(); // This is not a single video URL. 1880 if ("/talks/" !== substr($path,0,7)) return array(); // This is not a single video URL.
1868 } 1881 }
1869 $sign = hash_hmac('sha256', $url, $GLOBALS['salt']); // We use the salt to sign data (it's random, secret, and specific to each installation) 1882 $sign = hash_hmac('sha256', $url, $GLOBALS['salt']); // We use the salt to sign data (it's random, secret, and specific to each installation)
1870 return array('src'=>indexUrl().'?do=genthumbnail&hmac='.htmlspecialchars($sign).'&url='.urlencode($url), 1883 return array('src'=>indexUrl().'?do=genthumbnail&hmac='.$sign.'&url='.urlencode($url),
1871 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'thumbnail'); 1884 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'thumbnail');
1872 } 1885 }
1873 1886
@@ -1878,7 +1891,7 @@ function computeThumbnail($url,$href=false)
1878 if ($ext=='jpg' || $ext=='jpeg' || $ext=='png' || $ext=='gif') 1891 if ($ext=='jpg' || $ext=='jpeg' || $ext=='png' || $ext=='gif')
1879 { 1892 {
1880 $sign = hash_hmac('sha256', $url, $GLOBALS['salt']); // We use the salt to sign data (it's random, secret, and specific to each installation) 1893 $sign = hash_hmac('sha256', $url, $GLOBALS['salt']); // We use the salt to sign data (it's random, secret, and specific to each installation)
1881 return array('src'=>indexUrl().'?do=genthumbnail&hmac='.htmlspecialchars($sign).'&url='.urlencode($url), 1894 return array('src'=>indexUrl().'?do=genthumbnail&hmac='.$sign.'&url='.urlencode($url),
1882 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'thumbnail'); 1895 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'thumbnail');
1883 } 1896 }
1884 return array(); // No thumbnail. 1897 return array(); // No thumbnail.
@@ -1897,11 +1910,11 @@ function thumbnail($url,$href=false)
1897 $t = computeThumbnail($url,$href); 1910 $t = computeThumbnail($url,$href);
1898 if (count($t)==0) return ''; // Empty array = no thumbnail for this URL. 1911 if (count($t)==0) return ''; // Empty array = no thumbnail for this URL.
1899 1912
1900 $html='<a href="'.htmlspecialchars($t['href']).'"><img src="'.htmlspecialchars($t['src']).'"'; 1913 $html='<a href="'.escape($t['href']).'"><img src="'.escape($t['src']).'"';
1901 if (!empty($t['width'])) $html.=' width="'.htmlspecialchars($t['width']).'"'; 1914 if (!empty($t['width'])) $html.=' width="'.escape($t['width']).'"';
1902 if (!empty($t['height'])) $html.=' height="'.htmlspecialchars($t['height']).'"'; 1915 if (!empty($t['height'])) $html.=' height="'.escape($t['height']).'"';
1903 if (!empty($t['style'])) $html.=' style="'.htmlspecialchars($t['style']).'"'; 1916 if (!empty($t['style'])) $html.=' style="'.escape($t['style']).'"';
1904 if (!empty($t['alt'])) $html.=' alt="'.htmlspecialchars($t['alt']).'"'; 1917 if (!empty($t['alt'])) $html.=' alt="'.escape($t['alt']).'"';
1905 $html.='></a>'; 1918 $html.='></a>';
1906 return $html; 1919 return $html;
1907} 1920}
@@ -1917,23 +1930,23 @@ function lazyThumbnail($url,$href=false)
1917 $t = computeThumbnail($url,$href); 1930 $t = computeThumbnail($url,$href);
1918 if (count($t)==0) return ''; // Empty array = no thumbnail for this URL. 1931 if (count($t)==0) return ''; // Empty array = no thumbnail for this URL.
1919 1932
1920 $html='<a href="'.htmlspecialchars($t['href']).'">'; 1933 $html='<a href="'.escape($t['href']).'">';
1921 1934
1922 // Lazy image 1935 // Lazy image
1923 $html.='<img class="b-lazy" src="#" data-src="'.htmlspecialchars($t['src']).'"'; 1936 $html.='<img class="b-lazy" src="#" data-src="'.escape($t['src']).'"';
1924 1937
1925 if (!empty($t['width'])) $html.=' width="'.htmlspecialchars($t['width']).'"'; 1938 if (!empty($t['width'])) $html.=' width="'.escape($t['width']).'"';
1926 if (!empty($t['height'])) $html.=' height="'.htmlspecialchars($t['height']).'"'; 1939 if (!empty($t['height'])) $html.=' height="'.escape($t['height']).'"';
1927 if (!empty($t['style'])) $html.=' style="'.htmlspecialchars($t['style']).'"'; 1940 if (!empty($t['style'])) $html.=' style="'.escape($t['style']).'"';
1928 if (!empty($t['alt'])) $html.=' alt="'.htmlspecialchars($t['alt']).'"'; 1941 if (!empty($t['alt'])) $html.=' alt="'.escape($t['alt']).'"';
1929 $html.='>'; 1942 $html.='>';
1930 1943
1931 // No-JavaScript fallback. 1944 // No-JavaScript fallback.
1932 $html.='<noscript><img src="'.htmlspecialchars($t['src']).'"'; 1945 $html.='<noscript><img src="'.escape($t['src']).'"';
1933 if (!empty($t['width'])) $html.=' width="'.htmlspecialchars($t['width']).'"'; 1946 if (!empty($t['width'])) $html.=' width="'.escape($t['width']).'"';
1934 if (!empty($t['height'])) $html.=' height="'.htmlspecialchars($t['height']).'"'; 1947 if (!empty($t['height'])) $html.=' height="'.escape($t['height']).'"';
1935 if (!empty($t['style'])) $html.=' style="'.htmlspecialchars($t['style']).'"'; 1948 if (!empty($t['style'])) $html.=' style="'.escape($t['style']).'"';
1936 if (!empty($t['alt'])) $html.=' alt="'.htmlspecialchars($t['alt']).'"'; 1949 if (!empty($t['alt'])) $html.=' alt="'.escape($t['alt']).'"';
1937 $html.='></noscript></a>'; 1950 $html.='></noscript></a>';
1938 1951
1939 return $html; 1952 return $html;
@@ -1983,7 +1996,7 @@ function install()
1983 $GLOBALS['login'] = $_POST['setlogin']; 1996 $GLOBALS['login'] = $_POST['setlogin'];
1984 $GLOBALS['salt'] = sha1(uniqid('',true).'_'.mt_rand()); // Salt renders rainbow-tables attacks useless. 1997 $GLOBALS['salt'] = sha1(uniqid('',true).'_'.mt_rand()); // Salt renders rainbow-tables attacks useless.
1985 $GLOBALS['hash'] = sha1($_POST['setpassword'].$GLOBALS['login'].$GLOBALS['salt']); 1998 $GLOBALS['hash'] = sha1($_POST['setpassword'].$GLOBALS['login'].$GLOBALS['salt']);
1986 $GLOBALS['title'] = (empty($_POST['title']) ? 'Shared links on '.htmlspecialchars(indexUrl()) : $_POST['title'] ); 1999 $GLOBALS['title'] = (empty($_POST['title']) ? 'Shared links on '.escape(indexUrl()) : $_POST['title'] );
1987 $GLOBALS['config']['ENABLE_UPDATECHECK'] = !empty($_POST['updateCheck']); 2000 $GLOBALS['config']['ENABLE_UPDATECHECK'] = !empty($_POST['updateCheck']);
1988 writeConfig(); 2001 writeConfig();
1989 echo '<script>alert("Shaarli is now configured. Please enter your login/password and start shaaring your links!");document.location=\'?do=login\';</script>'; 2002 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()
2210 // This is more complex: we have to perform a HTTP request, then parse the result. 2223 // This is more complex: we have to perform a HTTP request, then parse the result.
2211 // Maybe we should deport this to JavaScript ? Example: http://stackoverflow.com/questions/1361149/get-img-thumbnails-from-vimeo/4285098#4285098 2224 // Maybe we should deport this to JavaScript ? Example: http://stackoverflow.com/questions/1361149/get-img-thumbnails-from-vimeo/4285098#4285098
2212 $vid = substr(parse_url($url,PHP_URL_PATH),1); 2225 $vid = substr(parse_url($url,PHP_URL_PATH),1);
2213 list($httpstatus,$headers,$data) = getHTTP('https://vimeo.com/api/v2/video/'.htmlspecialchars($vid).'.php',5); 2226 list($httpstatus,$headers,$data) = getHTTP('https://vimeo.com/api/v2/video/'.escape($vid).'.php',5);
2214 if (strpos($httpstatus,'200 OK')!==false) 2227 if (strpos($httpstatus,'200 OK')!==false)
2215 { 2228 {
2216 $t = unserialize($data); 2229 $t = unserialize($data);