]> git.immae.eu Git - github/shaarli/Shaarli.git/blobdiff - index.php
Bump version to v0.6.2
[github/shaarli/Shaarli.git] / index.php
index 7bcd0d15377658051835193365df7e1f6d904b8c..40a6fbe5a1a5c6e0c2f1d3b06782617df2da9c54 100644 (file)
--- a/index.php
+++ b/index.php
@@ -1,6 +1,6 @@
 <?php
 /**
- * Shaarli v0.6.0 - Shaare your links...
+ * Shaarli v0.6.2 - Shaare your links...
  *
  * The personal, minimalist, super-fast, no-database Delicious clone.
  *
@@ -44,6 +44,9 @@ $GLOBALS['config']['DATASTORE'] = $GLOBALS['config']['DATADIR'].'/datastore.php'
 // Banned IPs
 $GLOBALS['config']['IPBANS_FILENAME'] = $GLOBALS['config']['DATADIR'].'/ipbans.php';
 
+// Access log
+$GLOBALS['config']['LOG_FILE'] = $GLOBALS['config']['DATADIR'].'/log.txt';
+
 // For updates check of Shaarli
 $GLOBALS['config']['UPDATECHECK_FILENAME'] = $GLOBALS['config']['DATADIR'].'/lastupdatecheck.txt';
 
@@ -52,7 +55,7 @@ $GLOBALS['config']['RAINTPL_TMP'] = 'tmp/';
 // Raintpl template directory (keep the trailing slash!)
 $GLOBALS['config']['RAINTPL_TPL'] = 'tpl/';
 
-// Thuumbnail cache directory
+// Thumbnail cache directory
 $GLOBALS['config']['CACHEDIR'] = 'cache';
 
 // Atom & RSS feed cache directory
@@ -89,7 +92,8 @@ $GLOBALS['config']['ENABLE_THUMBNAILS'] = true;
 $GLOBALS['config']['ENABLE_LOCALCACHE'] = true;
 
 // Update check frequency for Shaarli. 86400 seconds=24 hours
-$GLOBALS['config']['UPDATECHECK_INTERVAL'] = 86400 ;
+$GLOBALS['config']['UPDATECHECK_BRANCH'] = 'stable';
+$GLOBALS['config']['UPDATECHECK_INTERVAL'] = 86400;
 
 
 /*
@@ -115,7 +119,7 @@ $GLOBALS['config']['PUBSUBHUB_URL'] = '';
 /*
  * PHP configuration
  */
-define('shaarli_version', '0.6.0');
+define('shaarli_version', '0.6.2');
 
 // http://server.com/x/shaarli --> /shaarli/
 define('WEB_PATH', substr($_SERVER["REQUEST_URI"], 0, 1+strrpos($_SERVER["REQUEST_URI"], '/', 0)));
@@ -141,8 +145,10 @@ if (is_file($GLOBALS['config']['CONFIG_FILE'])) {
 }
 
 // Shaarli library
+require_once 'application/ApplicationUtils.php';
 require_once 'application/Cache.php';
 require_once 'application/CachedPage.php';
+require_once 'application/FileUtils.php';
 require_once 'application/HttpUtils.php';
 require_once 'application/LinkDB.php';
 require_once 'application/TimeZone.php';
@@ -154,10 +160,10 @@ require_once 'application/Router.php';
 
 // Ensure the PHP version is supported
 try {
-    checkPHPVersion('5.3', PHP_VERSION);
-} catch(Exception $e) {
+    ApplicationUtils::checkPHPVersion('5.3', PHP_VERSION);
+} catch(Exception $exc) {
     header('Content-Type: text/plain; charset=utf-8');
-    echo $e->getMessage();
+    echo $exc->getMessage();
     exit;
 }
 
@@ -216,9 +222,6 @@ header("Cache-Control: no-store, no-cache, must-revalidate");
 header("Cache-Control: post-check=0, pre-check=0", false);
 header("Pragma: no-cache");
 
-// Directories creations (Note that your web host may require different rights than 705.)
-if (!is_writable(realpath(dirname(__FILE__)))) die('<pre>ERROR: Shaarli does not have the right to write in its own directory.</pre>');
-
 // Handling of old config file which do not have the new parameters.
 if (empty($GLOBALS['title'])) $GLOBALS['title']='Shared links on '.escape(index_url($_SERVER));
 if (empty($GLOBALS['timezone'])) $GLOBALS['timezone']=date_default_timezone_get();
@@ -228,8 +231,24 @@ if (empty($GLOBALS['privateLinkByDefault'])) $GLOBALS['privateLinkByDefault']=fa
 if (empty($GLOBALS['titleLink'])) $GLOBALS['titleLink']='?';
 // I really need to rewrite Shaarli with a proper configuation manager.
 
-// Run config screen if first run:
 if (! is_file($GLOBALS['config']['CONFIG_FILE'])) {
+    // Ensure Shaarli has proper access to its resources
+    $errors = ApplicationUtils::checkResourcePermissions($GLOBALS['config']);
+
+    if ($errors != array()) {
+        $message = '<p>Insufficient permissions:</p><ul>';
+
+        foreach ($errors as $error) {
+            $message .= '<li>'.$error.'</li>';
+        }
+        $message .= '</ul>';
+
+        header('Content-Type: text/html; charset=utf-8');
+        echo $message;
+        exit;
+    }
+
+    // Display the installation form if no existing config is found
     install();
 }
 
@@ -248,11 +267,11 @@ header('Content-Type: text/html; charset=utf-8'); // We use UTF-8 for proper int
 //==================================================================================================
 
 function setup_login_state() {
-       $userIsLoggedIn = false; // By default, we do not consider the user as logged in;
-       $loginFailure = false; // If set to true, every attempt to authenticate the user will fail. This indicates that an important condition isn't met.
        if ($GLOBALS['config']['OPEN_SHAARLI']) {
-           $userIsLoggedIn = true;
+           return true;
        }
+       $userIsLoggedIn = false; // By default, we do not consider the user as logged in;
+       $loginFailure = false; // If set to true, every attempt to authenticate the user will fail. This indicates that an important condition isn't met.
        if (!isset($GLOBALS['login'])) {
            $userIsLoggedIn = false;  // Shaarli is not configured yet.
            $loginFailure = true;
@@ -287,56 +306,15 @@ function setup_login_state() {
 }
 $userIsLoggedIn = setup_login_state();
 
-// Checks if an update is available for Shaarli.
-// (at most once a day, and only for registered user.)
-// Output: '' = no new version.
-//         other= the available version.
-function checkUpdate()
-{
-    if (!isLoggedIn()) return ''; // Do not check versions for visitors.
-    if (empty($GLOBALS['config']['ENABLE_UPDATECHECK'])) return ''; // Do not check if the user doesn't want to.
-
-    // Get latest version number at most once a day.
-    if (!is_file($GLOBALS['config']['UPDATECHECK_FILENAME']) || (filemtime($GLOBALS['config']['UPDATECHECK_FILENAME'])<time()-($GLOBALS['config']['UPDATECHECK_INTERVAL'])))
-    {
-        $version = shaarli_version;
-        list($headers, $data) = get_http_url('https://raw.githubusercontent.com/shaarli/Shaarli/master/shaarli_version.php', 2);
-        if (strpos($headers[0], '200 OK') !== false) {
-            $version = str_replace(' */ ?>', '', str_replace('<?php /* ', '', $data));
-        }
-        // If failed, never mind. We don't want to bother the user with that.
-        file_put_contents($GLOBALS['config']['UPDATECHECK_FILENAME'],$version); // touch file date
-    }
-    // Compare versions:
-    $newestversion=file_get_contents($GLOBALS['config']['UPDATECHECK_FILENAME']);
-    if (version_compare($newestversion,shaarli_version)==1) return $newestversion;
-    return '';
-}
-
 
 // -----------------------------------------------------------------------------------------------
 // Log to text file
 function logm($message)
 {
     $t = strval(date('Y/m/d_H:i:s')).' - '.$_SERVER["REMOTE_ADDR"].' - '.strval($message)."\n";
-    file_put_contents($GLOBALS['config']['DATADIR'].'/log.txt',$t,FILE_APPEND);
+    file_put_contents($GLOBALS['config']['LOG_FILE'], $t, FILE_APPEND);
 }
 
-// In a string, converts URLs to clickable links.
-// Function inspired from http://www.php.net/manual/en/function.preg-replace.php#85722
-function text2clickable($url)
-{
-    $redir = empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'];
-    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
-// even in the absence of <pre>  (This is used in description to keep text formatting)
-function keepMultipleSpaces($text)
-{
-    return str_replace('  ',' &nbsp;',$text);
-
-}
 // ------------------------------------------------------------------------------------------
 // Sniff browser language to display dates in the right format automatically.
 // (Note that is may not work on your server if the corresponding local is not installed.)
@@ -654,7 +632,25 @@ class pageBuilder
     private function initialize()
     {
         $this->tpl = new RainTPL;
-        $this->tpl->assign('newversion', escape(checkUpdate()));
+
+        try {
+            $version = ApplicationUtils::checkUpdate(
+                shaarli_version,
+                $GLOBALS['config']['UPDATECHECK_FILENAME'],
+                $GLOBALS['config']['UPDATECHECK_INTERVAL'],
+                $GLOBALS['config']['ENABLE_UPDATECHECK'],
+                isLoggedIn(),
+                $GLOBALS['config']['UPDATECHECK_BRANCH']
+            );
+            $this->tpl->assign('newVersion', escape($version));
+            $this->tpl->assign('versionError', '');
+
+        } catch (Exception $exc) {
+            logm($exc->getMessage());
+            $this->tpl->assign('newVersion', '');
+            $this->tpl->assign('versionError', escape($exc->getMessage()));
+        }
+
         $this->tpl->assign('feedurl', escape(index_url($_SERVER)));
         $searchcrits = ''; // Search criteria
         if (!empty($_GET['searchtags'])) {
@@ -727,8 +723,9 @@ function showRSS()
     // If cached was not found (or not usable), then read the database and build the response:
     $LINKSDB = new LinkDB(
         $GLOBALS['config']['DATASTORE'],
-        isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI'],
-        $GLOBALS['config']['HIDE_PUBLIC_LINKS']
+        isLoggedIn(),
+        $GLOBALS['config']['HIDE_PUBLIC_LINKS'],
+        $GLOBALS['redirector']
     );
     // Read links from database (and filter private links if user it not logged in).
 
@@ -779,7 +776,9 @@ function showRSS()
         // 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($link['description']))).$descriptionlink.']]></description>'."\n</item>\n";
+        echo '<description><![CDATA['.
+            format_description($link['description'], $GLOBALS['redirector']) .
+            $descriptionlink . ']]></description>' . "\n</item>\n";
         $i++;
     }
     echo '</channel></rss><!-- Cached version of '.escape(page_url($_SERVER)).' -->';
@@ -816,8 +815,9 @@ function showATOM()
     // Read links from database (and filter private links if used it not logged in).
     $LINKSDB = new LinkDB(
         $GLOBALS['config']['DATASTORE'],
-        isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI'],
-        $GLOBALS['config']['HIDE_PUBLIC_LINKS']
+        isLoggedIn(),
+        $GLOBALS['config']['HIDE_PUBLIC_LINKS'],
+        $GLOBALS['redirector']
     );
 
     // Optionally filter the results:
@@ -858,7 +858,9 @@ function showATOM()
         if ($usepermalinks===true) $descriptionlink = '(<a href="'.$absurl.'">Link</a>)';
         if (strlen($link['description'])>0) $descriptionlink = '<br>'.$descriptionlink;
 
-        $entries.='<content type="html"><![CDATA['.nl2br(keepMultipleSpaces(text2clickable($link['description']))).$descriptionlink."]]></content>\n";
+        $entries .= '<content type="html"><![CDATA['.
+            format_description($link['description'], $GLOBALS['redirector']) .
+            $descriptionlink . "]]></content>\n";
         if ($link['tags']!='') // Adding tags to each ATOM entry (as mentioned in ATOM specification)
         {
             foreach(explode(' ',$link['tags']) as $tag)
@@ -910,8 +912,9 @@ function showDailyRSS() {
     // Read links from database (and filter private links if used it not logged in).
     $LINKSDB = new LinkDB(
         $GLOBALS['config']['DATASTORE'],
-        isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI'],
-        $GLOBALS['config']['HIDE_PUBLIC_LINKS']
+        isLoggedIn(),
+        $GLOBALS['config']['HIDE_PUBLIC_LINKS'],
+        $GLOBALS['redirector']
     );
 
     /* Some Shaarlies may have very few links, so we need to look
@@ -965,7 +968,7 @@ function showDailyRSS() {
         // We pre-format some fields for proper output.
         foreach ($linkdates as $linkdate) {
             $l = $LINKSDB[$linkdate];
-            $l['formatedDescription'] = nl2br(keepMultipleSpaces(text2clickable($l['description'])));
+            $l['formatedDescription'] = format_description($l['description'], $GLOBALS['redirector']);
             $l['thumbnail'] = thumbnail($l['url']);
             $l['timestamp'] = linkdate2timestamp($l['linkdate']);
             if (startsWith($l['url'], '?')) {
@@ -992,13 +995,18 @@ function showDailyRSS() {
     exit;
 }
 
-// "Daily" page.
-function showDaily()
+/**
+ * Show the 'Daily' page.
+ *
+ * @param PageBuilder $pageBuilder Template engine wrapper.
+ */
+function showDaily($pageBuilder)
 {
     $LINKSDB = new LinkDB(
         $GLOBALS['config']['DATASTORE'],
-        isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI'],
-        $GLOBALS['config']['HIDE_PUBLIC_LINKS']
+        isLoggedIn(),
+        $GLOBALS['config']['HIDE_PUBLIC_LINKS'],
+        $GLOBALS['redirector']
     );
 
     $day=Date('Ymd',strtotime('-1 day')); // Yesterday, in format YYYYMMDD.
@@ -1029,7 +1037,7 @@ function showDaily()
         $taglist = explode(' ',$link['tags']);
         uasort($taglist, 'strcasecmp');
         $linksToDisplay[$key]['taglist']=$taglist;
-        $linksToDisplay[$key]['formatedDescription']=nl2br(keepMultipleSpaces(text2clickable($link['description'])));
+        $linksToDisplay[$key]['formatedDescription'] = format_description($link['description'], $GLOBALS['redirector']);
         $linksToDisplay[$key]['thumbnail'] = thumbnail($link['url']);
         $linksToDisplay[$key]['timestamp'] = linkdate2timestamp($link['linkdate']);
     }
@@ -1055,7 +1063,7 @@ function showDaily()
         array_push($columns[$index],$link); // Put entry in this column.
         $fill[$index]+=$length;
     }
-    $PAGE = new pageBuilder;
+
     $data = array(
         'linksToDisplay' => $linksToDisplay,
         'linkcount' => count($LINKSDB),
@@ -1068,10 +1076,10 @@ function showDaily()
     $pluginManager->executeHooks('render_daily', $data, array('loggedin' => isLoggedIn()));
 
     foreach ($data as $key => $value) {
-        $PAGE->assign($key, $value);
+        $pageBuilder->assign($key, $value);
     }
 
-    $PAGE->renderPage('daily');
+    $pageBuilder->renderPage('daily');
     exit;
 }
 
@@ -1088,8 +1096,9 @@ function renderPage()
 {
     $LINKSDB = new LinkDB(
         $GLOBALS['config']['DATASTORE'],
-        isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI'],
-        $GLOBALS['config']['HIDE_PUBLIC_LINKS']
+        isLoggedIn(),
+        $GLOBALS['config']['HIDE_PUBLIC_LINKS'],
+        $GLOBALS['redirector']
     );
 
     $PAGE = new pageBuilder;
@@ -1204,6 +1213,19 @@ function renderPage()
         exit;
     }
 
+    // Daily page.
+    if ($targetPage == Router::$PAGE_DAILY) {
+        showDaily($PAGE);
+    }
+
+    // Display openseach plugin (XML)
+    if ($targetPage == Router::$PAGE_OPENSEARCH) {
+        header('Content-Type: application/xml; charset=utf-8');
+        $PAGE->assign('serverurl', index_url($_SERVER));
+        $PAGE->renderPage('opensearch');
+        exit;
+    }
+
     // -------- User clicks on a tag in a link: The tag is added to the list of searched tags (searchtags=...)
     if (isset($_GET['addtag']))
     {
@@ -1453,7 +1475,7 @@ function renderPage()
                 $value['tags']=trim(implode(' ',$tags));
                 $LINKSDB[$key]=$value;
             }
-            $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); // Save to disk.
+            $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']);
             echo '<script>alert("Tag was removed from '.count($linksToAlter).' links.");document.location=\'?\';</script>';
             exit;
         }
@@ -1611,11 +1633,11 @@ function renderPage()
             $link_is_new = true;
             $linkdate = strval(date('Ymd_His'));
             // Get title if it was provided in URL (by the bookmarklet).
-            $title = (empty($_GET['title']) ? '' : $_GET['title'] );
+            $title = empty($_GET['title']) ? '' : escape($_GET['title']);
             // Get description if it was provided in URL (by the bookmarklet). [Bronco added that]
-            $description = (empty($_GET['description']) ? '' : $_GET['description']);
-            $tags = (empty($_GET['tags']) ? '' : $_GET['tags'] );
-            $private = (!empty($_GET['private']) && $_GET['private'] === "1" ? 1 : 0);
+            $description = empty($_GET['description']) ? '' : escape($_GET['description']);
+            $tags = empty($_GET['tags']) ? '' : escape($_GET['tags']);
+            $private = !empty($_GET['private']) && $_GET['private'] === "1" ? 1 : 0;
             // If this is an HTTP(S) link, we try go get the page to extract the title (otherwise we will to straight to the edit form.)
             if (empty($title) && strpos(get_url_scheme($url), 'http') !== false) {
                 // Short timeout to keep the application responsive
@@ -1751,11 +1773,12 @@ HTML;
 // Process the import file form.
 function importFile()
 {
-    if (!(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI'])) { die('Not allowed.'); }
+    if (!isLoggedIn()) { die('Not allowed.'); }
     $LINKSDB = new LinkDB(
         $GLOBALS['config']['DATASTORE'],
-        isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI'],
-        $GLOBALS['config']['HIDE_PUBLIC_LINKS']
+        isLoggedIn(),
+        $GLOBALS['config']['HIDE_PUBLIC_LINKS'],
+        $GLOBALS['redirector']
     );
     $filename=$_FILES['filetoupload']['name'];
     $filesize=$_FILES['filetoupload']['size'];
@@ -1906,8 +1929,7 @@ function buildLinkList($PAGE,$LINKSDB)
     while ($i<$end && $i<count($keys))
     {
         $link = $linksToDisplay[$keys[$i]];
-        $link['description']=nl2br(keepMultipleSpaces(text2clickable($link['description'])));
-        $title=$link['title'];
+        $link['description'] = format_description($link['description'], $GLOBALS['redirector']);
         $classLi =  $i%2!=0 ? '' : 'publicLinkHightLight';
         $link['class'] = ($link['private']==0 ? $classLi : 'private');
         $link['timestamp']=linkdate2timestamp($link['linkdate']);
@@ -1948,6 +1970,10 @@ function buildLinkList($PAGE,$LINKSDB)
         'links' => $linkDisp,
         'tags' => $LINKSDB->allTags(),
     );
+    // FIXME! temporary fix - see #399.
+    if (!empty($GLOBALS['pagetitle']) && count($linkDisp) == 1) {
+        $data['pagetitle'] = $GLOBALS['pagetitle'];
+    }
 
     $pluginManager = PluginManager::getInstance();
     $pluginManager->executeHooks('render_linklist', $data, array('loggedin' => isLoggedIn()));
@@ -2213,45 +2239,6 @@ function install()
     exit;
 }
 
-if (!function_exists('json_encode')) {
-    function json_encode($data) {
-        switch ($type = gettype($data)) {
-            case 'NULL':
-                return 'null';
-            case 'boolean':
-                return ($data ? 'true' : 'false');
-            case 'integer':
-            case 'double':
-            case 'float':
-                return $data;
-            case 'string':
-                return '"' . addslashes($data) . '"';
-            case 'object':
-                $data = get_object_vars($data);
-            case 'array':
-                $output_index_count = 0;
-                $output_indexed = array();
-                $output_associative = array();
-                foreach ($data as $key => $value) {
-                    $output_indexed[] = json_encode($value);
-                    $output_associative[] = json_encode($key) . ':' . json_encode($value);
-                    if ($output_index_count !== NULL && $output_index_count++ !== $key) {
-                        $output_index_count = NULL;
-                    }
-                }
-                if ($output_index_count !== NULL) {
-                    return '[' . implode(',', $output_indexed) . ']';
-                } else {
-                    return '{' . implode(',', $output_associative) . '}';
-                }
-            default:
-                return ''; // Not supported
-        }
-    }
-}
-
-
-
 /* Because some f*cking services like flickr require an extra HTTP request to get the thumbnail URL,
    I have deported the thumbnail URL code generation here, otherwise this would slow down page generation.
    The following function takes the URL a link (e.g. a flickr page) and return the proper thumbnail.
@@ -2481,7 +2468,6 @@ if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=g
 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=rss')) { showRSS(); exit; }
 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=atom')) { showATOM(); exit; }
 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=dailyrss')) { showDailyRSS(); exit; }
-if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=daily')) { showDaily(); exit; }
 if (!isset($_SESSION['LINKS_PER_PAGE'])) $_SESSION['LINKS_PER_PAGE']=$GLOBALS['config']['LINKS_PER_PAGE'];
 renderPage();
 ?>