]> git.immae.eu Git - github/shaarli/Shaarli.git/blobdiff - index.php
Added https to list of authorized protocols.
[github/shaarli/Shaarli.git] / index.php
index 569a8bb416d3ca994046dd8a25f57168c6fef17f..222f329587355f84d1bb1ad8af6f7ee345c581d1 100644 (file)
--- a/index.php
+++ b/index.php
@@ -37,6 +37,14 @@ $cookie=session_get_cookie_params();
 $cookiedir = ''; if(dirname($_SERVER['SCRIPT_NAME'])!='/') $cookiedir=dirname($_SERVER["SCRIPT_NAME"]).'/';
 session_set_cookie_params($cookie['lifetime'],$cookiedir); // 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 to use 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).
@@ -88,7 +96,8 @@ require $GLOBALS['config']['CONFIG_FILE'];  // Read login/password hash into $GL
 if (empty($GLOBALS['title'])) $GLOBALS['title']='Shared links on '.htmlspecialchars(indexUrl());
 if (empty($GLOBALS['timezone'])) $GLOBALS['timezone']=date_default_timezone_get();
 if (empty($GLOBALS['disablesessionprotection'])) $GLOBALS['disablesessionprotection']=false;
-
+if (empty($GLOBALS['disablejquery'])) $GLOBALS['disablejquery']=false;
+// I really need to rewrite Shaarli with a proper configuation manager.
 
 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.
@@ -172,7 +181,7 @@ class pageCache
         if (is_dir($GLOBALS['config']['PAGECACHE']))
         {
             $handler = opendir($GLOBALS['config']['PAGECACHE']);
-            if ($handle!==false)
+            if ($handler!==false)
             {
                 while (($filename = readdir($handler))!==false) 
                 {
@@ -223,7 +232,7 @@ function smallHash($text)
 function text2clickable($url)
 {
     $redir = empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'];
-    return preg_replace('!(((?:https?|ftp|file)://|apt:)\S+[[:alnum:]]/?)!si','<a href="'.$redir.'$1" rel="nofollow">$1</a>',$url);
+    return preg_replace('!(((?:https?|ftp|file)://|apt:|magnet:)\S+[[:alnum:]]/?)!si','<a href="'.$redir.'$1" rel="nofollow">$1</a>',$url);
 }
 
 // This function inserts &nbsp; where relevant so that multiple spaces are properly displayed in HTML
@@ -265,12 +274,6 @@ function pubsubhub()
 
 // ------------------------------------------------------------------------------------------
 // Session management
-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 to use sessionID in URL if cookies are disabled.
-session_name('shaarli');
-session_start();
 
 // Returns the IP address of the client (Used to prevent session cookie hijacking.)
 function allIPs()
@@ -303,6 +306,8 @@ function check_auth($login,$password)
 function isLoggedIn()
 {
     if ($GLOBALS['config']['OPEN_SHAARLI']) return true;
+    
+    if (!isset($GLOBALS['login'])) return false;  // Shaarli is not configured yet.
 
     // 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'])
@@ -317,7 +322,7 @@ function isLoggedIn()
 }
 
 // Force logout.
-function logout() { if (isset($_SESSION)) { unset($_SESSION['uid']); unset($_SESSION['ip']); unset($_SESSION['username']);}  }
+function logout() { if (isset($_SESSION)) { unset($_SESSION['uid']); unset($_SESSION['ip']); unset($_SESSION['username']); unset($_SESSION['privateonly']); }  }
 
 
 // ------------------------------------------------------------------------------------------
@@ -405,7 +410,9 @@ if (isset($_POST['login']))
     else
     {
         ban_loginFailed();
-        echo '<script language="JavaScript">alert("Wrong login/password.");document.location=\'?do=login\';</script>'; // Redirect to login screen.
+        $redir = '';
+        if (isset($_GET['post'])) { $redir = '&post='.urlencode($_GET['post']).(!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').(!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.
         exit;
     }
 }
@@ -427,7 +434,11 @@ function serverUrl()
 // (eg. http://sebsauvage.net/links/)
 function indexUrl()
 {
-    return serverUrl() . ($_SERVER["SCRIPT_NAME"] == '/index.php' ? '/' : $_SERVER["SCRIPT_NAME"]);
+    $scriptname = $_SERVER["SCRIPT_NAME"];
+    // If the script is named 'index.php', we remove it (for better looking URLs,
+    // eg. http://mysite.com/shaarli/?abcde instead of http://mysite.com/shaarli/index.php?abcde)
+    if (endswith($scriptname,'index.php')) $scriptname = substr($scriptname,0,strlen($scriptname)-9);
+    return serverUrl() . $scriptname;
 }
 
 // Returns the absolute URL of current script, WITH the query.
@@ -572,7 +583,7 @@ if (!isset($_SESSION['tokens'])) $_SESSION['tokens']=array();  // Token are atta
 // Returns a token.
 function getToken()
 {
-    $rnd = sha1(uniqid('',true).'_'.mt_rand());  // We generate a random string.
+    $rnd = sha1(uniqid('',true).'_'.mt_rand().$GLOBALS['salt']);  // We generate a random string.
     $_SESSION['tokens'][$rnd]=1;  // Store it on the server side.
     return $rnd;
 }
@@ -858,6 +869,10 @@ function showRSS()
 {
     header('Content-Type: application/rss+xml; charset=utf-8');
 
+    // $usepermalink : If true, use permalink instead of final link.
+    // User just has to add 'permalink' in URL parameters. eg. http://mysite.com/shaarli/?do=rss&permalinks
+    $usepermalinks = isset($_GET['permalinks']);
+
     // Cache system
     $query = $_SERVER["QUERY_STRING"];
     $cache = new pageCache(pageUrl(),startsWith($query,'do=rss') && !isLoggedIn());
@@ -892,16 +907,25 @@ function showRSS()
         $rfc822date = linkdate2rfc822($link['linkdate']);
         $absurl = htmlspecialchars($link['url']);
         if (startsWith($absurl,'?')) $absurl=$pageaddr.$absurl;  // make permalink URL absolute
-        echo '<item><title>'.htmlspecialchars($link['title']).'</title><guid>'.$guid.'</guid><link>'.$absurl.'</link>';
+        if ($usepermalinks===true)
+            echo '<item><title>'.htmlspecialchars($link['title']).'</title><guid isPermaLink="false">'.$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";
         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"; }
         }
-        echo '<description><![CDATA['.nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description'])))).']]></description>'."\n</item>\n";
+
+        // Add permalink in description
+        $descriptionlink = '(<a href="'.$guid.'">Permalink</a>)';
+        // 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";
         $i++;
     }
-    echo '</channel></rss>';
+    echo '</channel></rss><!-- Cached version of '.pageUrl().' -->';
 
     $cache->cache(ob_get_contents());
     ob_end_flush();
@@ -914,6 +938,10 @@ function showATOM()
 {
     header('Content-Type: application/atom+xml; charset=utf-8');
 
+    // $usepermalink : If true, use permalink instead of final link.
+    // User just has to add 'permalink' in URL parameters. eg. http://mysite.com/shaarli/?do=atom&permalinks
+    $usepermalinks = isset($_GET['permalinks']);
+
     // Cache system
     $query = $_SERVER["QUERY_STRING"];
     $cache = new pageCache(pageUrl(),startsWith($query,'do=atom') && !isLoggedIn());
@@ -942,9 +970,20 @@ function showATOM()
         $latestDate = max($latestDate,$iso8601date);
         $absurl = htmlspecialchars($link['url']);
         if (startsWith($absurl,'?')) $absurl=$pageaddr.$absurl;  // make permalink URL absolute
-        $entries.='<entry><title>'.htmlspecialchars($link['title']).'</title><link href="'.$absurl.'" /><id>'.$guid.'</id>';
+        $entries.='<entry><title>'.htmlspecialchars($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>';
-        $entries.='<content type="html">'.htmlspecialchars(nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description'])))))."</content>\n";
+
+        // Add permalink in description
+        $descriptionlink = htmlspecialchars('(<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 = '&lt;br&gt;'.$descriptionlink;
+
+        $entries.='<content type="html">'.htmlspecialchars(nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($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)
@@ -966,7 +1005,7 @@ function showATOM()
     $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.=$entries;
-    $feed.='</feed>';
+    $feed.='</feed><!-- Cached version of '.pageUrl().' -->';
     echo $feed;
     
     $cache->cache(ob_get_contents());
@@ -1043,7 +1082,7 @@ function showDailyRSS()
         echo '<description><![CDATA['.$html.']]></description>'."\n</item>\n\n";
 
     }    
-    echo '</channel></rss>';
+    echo '</channel></rss><!-- Cached version of '.pageUrl().' -->';
     
     $cache->cache(ob_get_contents());
     ob_end_flush();
@@ -1074,7 +1113,9 @@ function showDaily()
     // We pre-format some fields for proper output.
     foreach($linksToDisplay as $key=>$link)
     {
-        $linksToDisplay[$key]['taglist']=explode(' ',$link['tags']);
+        $taglist = explode(' ',$link['tags']);
+        uasort($taglist, 'strcasecmp');
+        $linksToDisplay[$key]['taglist']=$taglist;
         $linksToDisplay[$key]['formatedDescription']=nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description']))));
         $linksToDisplay[$key]['thumbnail'] = thumbnail($link['url']);            
     }
@@ -1224,7 +1265,11 @@ function renderPage()
     if (isset($_GET['linksperpage']))
     {
         if (is_numeric($_GET['linksperpage'])) { $_SESSION['LINKS_PER_PAGE']=abs(intval($_GET['linksperpage'])); }
-        header('Location: '.(empty($_SERVER['HTTP_REFERER'])?'?':$_SERVER['HTTP_REFERER']));
+        // Make sure the referer is from Shaarli itself.
+        $referer = '?';
+        if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['SERVER_NAME'])==0)
+            $referer = $_SERVER['HTTP_REFERER'];
+        header('Location: '.$referer);
         exit;
     }
     
@@ -1239,7 +1284,11 @@ function renderPage()
         {
             unset($_SESSION['privateonly']); // See all links
         }
-        header('Location: '.(empty($_SERVER['HTTP_REFERER'])?'?':$_SERVER['HTTP_REFERER']));
+        // Make sure the referer is from Shaarli itself.
+        $referer = '?';
+        if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['SERVER_NAME'])==0)
+            $referer = $_SERVER['HTTP_REFERER'];
+        header('Location: '.$referer);
         exit;
     }
 
@@ -1313,6 +1362,7 @@ function renderPage()
             $GLOBALS['title']=$_POST['title'];
             $GLOBALS['redirector']=$_POST['redirector'];
             $GLOBALS['disablesessionprotection']=!empty($_POST['disablesessionprotection']);
+            $GLOBALS['disablejquery']=!empty($_POST['disablejquery']);
             writeConfig();
             echo '<script language="JavaScript">alert("Configuration was saved.");document.location=\'?do=tools\';</script>';
             exit;
@@ -1395,7 +1445,10 @@ function renderPage()
         if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away !
         $tags = trim(preg_replace('/\s\s+/',' ', $_POST['lf_tags'])); // Remove multiple spaces.
         $linkdate=$_POST['lf_linkdate'];
-        $link = array('title'=>trim($_POST['lf_title']),'url'=>trim($_POST['lf_url']),'description'=>trim($_POST['lf_description']),'private'=>(isset($_POST['lf_private']) ? 1 : 0),
+        $url = trim($_POST['lf_url']);
+        if (!startsWith($url,'http:') && !startsWith($url,'https:') && !startsWith($url,'ftp:') && !startsWith($url,'magnet:') && !startsWith($url,'?'))
+            $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;
@@ -1405,6 +1458,7 @@ function renderPage()
         // 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.
         exit;
     }
@@ -1415,6 +1469,7 @@ function renderPage()
         // 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($_POST['lf_linkdate']);  // Scroll to the link which has been edited.
         header('Location: '.$returnurl); // After canceling, redirect to the page the user was on.
         exit;
     }
@@ -1724,7 +1779,9 @@ function buildLinkList($PAGE,$LINKSDB)
         $classLi =  $i%2!=0 ? '' : 'publicLinkHightLight';
         $link['class'] = ($link['private']==0 ? $classLi : 'private');
         $link['localdate']=linkdate2locale($link['linkdate']);
-        $link['taglist']=explode(' ',$link['tags']);
+        $taglist = explode(' ',$link['tags']);
+        uasort($taglist, 'strcasecmp');
+        $link['taglist']=$taglist;
         $linkDisp[$keys[$i]] = $link;
         $i++;
     }
@@ -1910,6 +1967,11 @@ function lazyThumbnail($url,$href=false)
     $html='<a href="'.htmlspecialchars($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']).'"';
+
     $html.='<img class="lazyimage" src="#" data-original="'.htmlspecialchars($t['src']).'"';
     if (!empty($t['width']))  $html.=' width="'.htmlspecialchars($t['width']).'"';
     if (!empty($t['height'])) $html.=' height="'.htmlspecialchars($t['height']).'"';
@@ -1917,7 +1979,7 @@ function lazyThumbnail($url,$href=false)
     if (!empty($t['alt']))    $html.=' alt="'.htmlspecialchars($t['alt']).'"';
     $html.='>';
     
-    // No-javascript fallback:
+    // 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']).'"';
@@ -1937,6 +1999,28 @@ function install()
     // On free.fr host, make sure the /sessions directory exists, otherwise login will not work.
     if (endsWith($_SERVER['SERVER_NAME'],'.free.fr') && !is_dir($_SERVER['DOCUMENT_ROOT'].'/sessions')) mkdir($_SERVER['DOCUMENT_ROOT'].'/sessions',0705);
 
+
+    // This part makes sure sessions works correctly.
+    // (Because on some hosts, session.save_path may not be set correctly,
+    // or we may not have write access to it.)
+    if (isset($_GET['test_session']) && ( !isset($_SESSION) || !isset($_SESSION['session_tested']) || $_SESSION['session_tested']!='Working'))
+    {   // Step 2: Check if data in session is correct.
+        echo '<pre>Sessions do not seem to work correctly on your server.<br>';
+        echo 'Make sure the variable session.save_path is set correctly in your php config, and that you have write access to it.<br>';
+        echo 'It currently points to '.session_save_path().'<br><br><a href="?">Click to try again.</a></pre>';
+        die;
+    }
+    if (!isset($_SESSION['session_tested']))
+    {   // Step 1 : Try to store data in session and reload page.
+        $_SESSION['session_tested'] = 'Working';  // Try to set a variable in session.
+        header('Location: '.indexUrl().'?test_session');  // Redirect to check stored data.
+    }
+    if (isset($_GET['test_session']))
+    {   // Step 3: Sessions are ok. Remove test parameter from URL.
+        header('Location: '.indexUrl());
+    }
+
+
     if (!empty($_POST['setlogin']) && !empty($_POST['setpassword']))
     {
         $tz = 'UTC';
@@ -2002,8 +2086,8 @@ function templateTZform($ptz=false)
         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><br /><br />";
-        $timezone_form .= "City: <select name=\"city\" id=\"city\">${cities[$pcontinent]}</select><br /><br />";
+        $timezone_form = "Continent: <select name=\"continent\" id=\"continent\" onChange=\"onChangecontinent();\">${continents_html}</select>";
+        $timezone_form .= "&nbsp;&nbsp;&nbsp;&nbsp;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).";" ;
@@ -2074,12 +2158,11 @@ function processWS()
 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.
-    if (empty($GLOBALS['redirector'])) $GLOBALS['redirector']='';
-    if (empty($GLOBALS['disablesessionprotection'])) $GLOBALS['disablesessionprotection']=false;
     $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[\'redirector\']='.var_export($GLOBALS['redirector'],true).'; ';
     $config .= '$GLOBALS[\'disablesessionprotection\']='.var_export($GLOBALS['disablesessionprotection'],true).'; ';
+    $config .= '$GLOBALS[\'disablejquery\']='.var_export($GLOBALS['disablejquery'],true).'; ';
     $config .= ' ?>';
     if (!file_put_contents($GLOBALS['config']['CONFIG_FILE'],$config) || strcmp(file_get_contents($GLOBALS['config']['CONFIG_FILE']),$config)!=0)
     {
@@ -2302,6 +2385,7 @@ function resizeImage($filepath)
     imagejpeg($im2, $tempname, 90);
     imagedestroy($im);
     imagedestroy($im2);
+    unlink($filepath);
     rename($tempname,$filepath);  // Overwrite original picture with thumbnail.
     return true;
 }