X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=index.php;h=850b350e9c5ad1c2dbae08c5573b929aaf22749b;hb=refs%2Ftags%2Fv0.6.5;hp=be181a2c8006208b0b35d9d6a7156da3aa6b4868;hpb=c580024cfbe5f0d290b09157b9665d1b4131d7f4;p=github%2Fshaarli%2FShaarli.git diff --git a/index.php b/index.php index be181a2c..850b350e 100644 --- a/index.php +++ b/index.php @@ -1,6 +1,6 @@ /shaarli/ define('WEB_PATH', substr($_SERVER["REQUEST_URI"], 0, 1+strrpos($_SERVER["REQUEST_URI"], '/', 0))); @@ -141,23 +151,28 @@ 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/LinkFilter.php'; +require_once 'application/LinkUtils.php'; require_once 'application/TimeZone.php'; require_once 'application/Url.php'; require_once 'application/Utils.php'; require_once 'application/Config.php'; require_once 'application/PluginManager.php'; require_once 'application/Router.php'; +require_once 'application/Updater.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 +231,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('
ERROR: Shaarli does not have the right to write in its own directory.
'); - // 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 +240,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 = '

Insufficient permissions:

'; + + header('Content-Type: text/html; charset=utf-8'); + echo $message; + exit; + } + + // Display the installation form if no existing config is found install(); } @@ -240,7 +268,10 @@ $GLOBALS['redirector'] = !empty($GLOBALS['redirector']) ? escape($GLOBALS['redir // a token depending of deployment salt, user password, and the current ip define('STAY_SIGNED_IN_TOKEN', sha1($GLOBALS['hash'].$_SERVER["REMOTE_ADDR"].$GLOBALS['salt'])); -autoLocale(); // Sniff browser language and set date format accordingly. +// Sniff browser language and set date format accordingly. +if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { + autoLocale($_SERVER['HTTP_ACCEPT_LANGUAGE']); +} header('Content-Type: text/html; charset=utf-8'); // We use UTF-8 for proper international characters handling. //================================================================================================== @@ -287,75 +318,6 @@ 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'])', '', str_replace('$1',$url); -} - -// This function inserts   where relevant so that multiple spaces are properly displayed in HTML -// even in the absence of
  (This is used in description to keep text formatting)
-function keepMultipleSpaces($text)
-{
-    return str_replace('  ','  ',$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.)
-function autoLocale()
-{
-    $attempts = array('en_US'); // Default if browser does not send HTTP_ACCEPT_LANGUAGE
-    if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) // e.g. "fr,fr-fr;q=0.8,en;q=0.5,en-us;q=0.3"
-    {   // (It's a bit crude, but it works very well. Preferred language is always presented first.)
-        if (preg_match('/([a-z]{2})-?([a-z]{2})?/i',$_SERVER['HTTP_ACCEPT_LANGUAGE'],$matches)) {
-            $loc = $matches[1] . (!empty($matches[2]) ? '_' . strtoupper($matches[2]) : '');
-            $attempts = array($loc.'.UTF-8', $loc, str_replace('_', '-', $loc).'.UTF-8', str_replace('_', '-', $loc),
-                $loc . '_' . strtoupper($loc).'.UTF-8', $loc . '_' . strtoupper($loc),
-                $loc . '_' . $loc.'.UTF-8', $loc . '_' . $loc, $loc . '-' . strtoupper($loc).'.UTF-8',
-                $loc . '-' . strtoupper($loc), $loc . '-' . $loc.'.UTF-8', $loc . '-' . $loc);
-        }
-    }
-    setlocale(LC_TIME, $attempts);  // LC_TIME = Set local for date/time format only.
-}
-
 // ------------------------------------------------------------------------------------------
 // PubSubHubbub protocol support (if enabled)  [UNTESTED]
 // (Source: http://aldarone.fr/les-flux-rss-shaarli-et-pubsubhubbub/ )
@@ -400,10 +362,10 @@ function check_auth($login,$password)
     if ($login==$GLOBALS['login'] && $hash==$GLOBALS['hash'])
     {   // Login/password is correct.
 		fillSessionInfo();
-        logm('Login successful');
+        logm($GLOBALS['config']['LOG_FILE'], $_SERVER['REMOTE_ADDR'], 'Login successful');
         return True;
     }
-    logm('Login failed for user '.$login);
+    logm($GLOBALS['config']['LOG_FILE'], $_SERVER['REMOTE_ADDR'], 'Login failed for user '.$login);
     return False;
 }
 
@@ -440,7 +402,7 @@ function ban_loginFailed()
     if ($gb['FAILURES'][$ip]>($GLOBALS['config']['BAN_AFTER']-1))
     {
         $gb['BANS'][$ip]=time()+$GLOBALS['config']['BAN_DURATION'];
-        logm('IP address banned from login');
+        logm($GLOBALS['config']['LOG_FILE'], $_SERVER['REMOTE_ADDR'], 'IP address banned from login');
     }
     $GLOBALS['IPBANS'] = $gb;
     file_put_contents($GLOBALS['config']['IPBANS_FILENAME'], "");
@@ -464,7 +426,7 @@ function ban_canLogin()
         // User is banned. Check if the ban has expired:
         if ($gb['BANS'][$ip]<=time())
         {   // Ban expired, user can try to login again.
-            logm('Ban lifted.');
+            logm($GLOBALS['config']['LOG_FILE'], $_SERVER['REMOTE_ADDR'], 'Ban lifted.');
             unset($gb['FAILURES'][$ip]); unset($gb['BANS'][$ip]);
             file_put_contents($GLOBALS['config']['IPBANS_FILENAME'], "");
             return true; // Ban has expired, user can login.
@@ -500,7 +462,7 @@ if (isset($_POST['login']))
             session_set_cookie_params(0,$cookiedir,$_SERVER['SERVER_NAME']); // 0 means "When browser closes"
             session_regenerate_id(true);
         }
-        
+
         // Optional redirect after login:
         if (isset($_GET['post'])) {
             $uri = '?post='. urlencode($_GET['post']);
@@ -572,40 +534,6 @@ function getMaxFileSize()
     return $maxsize;
 }
 
-/*  Converts a linkdate time (YYYYMMDD_HHMMSS) of an article to a timestamp (Unix epoch)
-    (used to build the ADD_DATE attribute in Netscape-bookmarks file)
-    PS: I could have used strptime(), but it does not exist on Windows. I'm too kind. */
-function linkdate2timestamp($linkdate)
-{
-    if(strcmp($linkdate, '_000000') !== 0 || !$linkdate){
-        $Y=$M=$D=$h=$m=$s=0;
-        $r = sscanf($linkdate,'%4d%2d%2d_%2d%2d%2d',$Y,$M,$D,$h,$m,$s);
-        return mktime($h,$m,$s,$M,$D,$Y);
-    }
-    return time();
-}
-
-/*  Converts a linkdate time (YYYYMMDD_HHMMSS) of an article to a RFC822 date.
-    (used to build the pubDate attribute in RSS feed.)  */
-function linkdate2rfc822($linkdate)
-{
-    return date('r',linkdate2timestamp($linkdate)); // 'r' is for RFC822 date format.
-}
-
-/*  Converts a linkdate time (YYYYMMDD_HHMMSS) of an article to a ISO 8601 date.
-    (used to build the updated tags in ATOM feed.)  */
-function linkdate2iso8601($linkdate)
-{
-    return date('c',linkdate2timestamp($linkdate)); // 'c' is for ISO 8601 date format.
-}
-
-// Extract title from an HTML document.
-// (Returns an empty string if not found.)
-function html_extract_title($html)
-{
-  return preg_match('!(.*?)!is', $html, $matches) ? trim(str_replace("\n",' ', $matches[1])) : '' ;
-}
-
 // ------------------------------------------------------------------------------------------
 // Token management for XSRF protection
 // Token should be used in any form which acts on data (create,update,delete,import...).
@@ -654,13 +582,31 @@ 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($GLOBALS['config']['LOG_FILE'], $_SERVER['REMOTE_ADDR'], $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'])) {
             $searchcrits .= '&searchtags=' . urlencode($_GET['searchtags']);
         }
-        elseif (!empty($_GET['searchterm'])) {
+        if (!empty($_GET['searchterm'])) {
             $searchcrits .= '&searchterm=' . urlencode($_GET['searchterm']);
         }
         $this->tpl->assign('searchcrits', $searchcrits);
@@ -698,6 +644,18 @@ class pageBuilder
         if ($this->tpl===false) $this->initialize(); // Lazy initialization
         $this->tpl->draw($page);
     }
+
+    /**
+    * Render a 404 page (uses the template : tpl/404.tpl)
+    *
+    * usage : $PAGE->render404('The link was deleted')
+    * @param string $message A messate to display what is not found
+    */
+    public function render404($message='The page you are trying to reach does not exist or has been deleted.') {
+        header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found');
+        $this->tpl->assign('error_message', $message);
+        $this->renderPage('404');
+    }
 }
 
 // ------------------------------------------------------------------------------------------
@@ -728,23 +686,37 @@ function showRSS()
     $LINKSDB = new LinkDB(
         $GLOBALS['config']['DATASTORE'],
         isLoggedIn(),
-        $GLOBALS['config']['HIDE_PUBLIC_LINKS']
+        $GLOBALS['config']['HIDE_PUBLIC_LINKS'],
+        $GLOBALS['redirector']
     );
     // Read links from database (and filter private links if user it not logged in).
 
     // Optionally filter the results:
-    $linksToDisplay=array();
-    if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']);
-    else if (!empty($_GET['searchtags']))   $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags']));
-    else $linksToDisplay = $LINKSDB;
+    $searchtags = !empty($_GET['searchtags']) ? escape($_GET['searchtags']) : '';
+    $searchterm = !empty($_GET['searchterm']) ? escape($_GET['searchterm']) : '';
+    if (! empty($searchtags) && ! empty($searchterm)) {
+        $linksToDisplay = $LINKSDB->filter(
+            LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT,
+            array($searchtags, $searchterm)
+        );
+    }
+    elseif ($searchtags) {
+        $linksToDisplay = $LINKSDB->filter(LinkFilter::$FILTER_TAG, $searchtags);
+    }
+    elseif ($searchterm) {
+        $linksToDisplay = $LINKSDB->filter(LinkFilter::$FILTER_TEXT, $searchterm);
+    }
+    else {
+        $linksToDisplay = $LINKSDB;
+    }
 
     $nblinksToDisplay = 50;  // Number of links to display.
-    if (!empty($_GET['nb']))  // In URL, you can specificy the number of links. Example: nb=200 or nb=all for all links.
-    {
-        $nblinksToDisplay = $_GET['nb']=='all' ? count($linksToDisplay) : max($_GET['nb']+0,1) ;
+    // In URL, you can specificy the number of links. Example: nb=200 or nb=all for all links.
+    if (!empty($_GET['nb'])) {
+        $nblinksToDisplay = $_GET['nb'] == 'all' ? count($linksToDisplay) : max(intval($_GET['nb']), 1);
     }
 
-    $pageaddr=escape(index_url($_SERVER));
+    $pageaddr = escape(index_url($_SERVER));
     echo '';
     echo ''.$GLOBALS['title'].''.$pageaddr.'';
     echo 'Shared linksen-en'.$pageaddr.''."\n\n";
@@ -761,14 +733,16 @@ function showRSS()
     {
         $link = $linksToDisplay[$keys[$i]];
         $guid = $pageaddr.'?'.smallHash($link['linkdate']);
-        $rfc822date = linkdate2rfc822($link['linkdate']);
+        $date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']);
         $absurl = $link['url'];
         if (startsWith($absurl,'?')) $absurl=$pageaddr.$absurl;  // make permalink URL absolute
         if ($usepermalinks===true)
             echo ''.$link['title'].''.$guid.''.$guid.'';
         else
             echo ''.$link['title'].''.$guid.''.$absurl.'';
-        if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) echo ''.escape($rfc822date)."\n";
+        if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) {
+            echo ''.escape($date->format(DateTime::RSS))."\n";
+        }
         if ($link['tags']!='') // Adding tags to each RSS entry (as mentioned in RSS specification)
         {
             foreach(explode(' ',$link['tags']) as $tag) { echo ''.$tag.''."\n"; }
@@ -779,7 +753,9 @@ function showRSS()
         // If user wants permalinks first, put the final link in description
         if ($usepermalinks===true) $descriptionlink = '(Link)';
         if (strlen($link['description'])>0) $descriptionlink = '
'.$descriptionlink; - echo ''."\n
\n"; + echo '' . "\n
\n"; $i++; } echo '
'; @@ -817,19 +793,33 @@ function showATOM() $LINKSDB = new LinkDB( $GLOBALS['config']['DATASTORE'], isLoggedIn(), - $GLOBALS['config']['HIDE_PUBLIC_LINKS'] + $GLOBALS['config']['HIDE_PUBLIC_LINKS'], + $GLOBALS['redirector'] ); // Optionally filter the results: - $linksToDisplay=array(); - if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']); - else if (!empty($_GET['searchtags'])) $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags'])); - else $linksToDisplay = $LINKSDB; + $searchtags = !empty($_GET['searchtags']) ? escape($_GET['searchtags']) : ''; + $searchterm = !empty($_GET['searchterm']) ? escape($_GET['searchterm']) : ''; + if (! empty($searchtags) && ! empty($searchterm)) { + $linksToDisplay = $LINKSDB->filter( + LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT, + array($searchtags, $searchterm) + ); + } + elseif ($searchtags) { + $linksToDisplay = $LINKSDB->filter(LinkFilter::$FILTER_TAG, $searchtags); + } + elseif ($searchterm) { + $linksToDisplay = $LINKSDB->filter(LinkFilter::$FILTER_TEXT, $searchterm); + } + else { + $linksToDisplay = $LINKSDB; + } $nblinksToDisplay = 50; // Number of links to display. - if (!empty($_GET['nb'])) // In URL, you can specificy the number of links. Example: nb=200 or nb=all for all links. - { - $nblinksToDisplay = $_GET['nb']=='all' ? count($linksToDisplay) : max($_GET['nb']+0,1) ; + // In URL, you can specificy the number of links. Example: nb=200 or nb=all for all links. + if (!empty($_GET['nb'])) { + $nblinksToDisplay = $_GET['nb']=='all' ? count($linksToDisplay) : max(intval($_GET['nb']), 1); } $pageaddr=escape(index_url($_SERVER)); @@ -841,8 +831,9 @@ function showATOM() { $link = $linksToDisplay[$keys[$i]]; $guid = $pageaddr.'?'.smallHash($link['linkdate']); - $iso8601date = linkdate2iso8601($link['linkdate']); - $latestDate = max($latestDate,$iso8601date); + $date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']); + $iso8601date = $date->format(DateTime::ISO8601); + $latestDate = max($latestDate, $iso8601date); $absurl = $link['url']; if (startsWith($absurl,'?')) $absurl=$pageaddr.$absurl; // make permalink URL absolute $entries.=''.$link['title'].''; @@ -850,7 +841,10 @@ function showATOM() $entries.=''.$guid.''; else $entries.=''.$guid.''; - if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) $entries.=''.escape($iso8601date).''; + + if (!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()) { + $entries.=''.escape($iso8601date).''; + } // Add permalink in description $descriptionlink = '(Permalink)'; @@ -858,7 +852,9 @@ function showATOM() if ($usepermalinks===true) $descriptionlink = '(Link)'; if (strlen($link['description'])>0) $descriptionlink = '
'.$descriptionlink; - $entries.='\n"; + $entries .= '\n"; if ($link['tags']!='') // Adding tags to each ATOM entry (as mentioned in ATOM specification) { foreach(explode(' ',$link['tags']) as $tag) @@ -911,7 +907,8 @@ function showDailyRSS() { $LINKSDB = new LinkDB( $GLOBALS['config']['DATASTORE'], isLoggedIn(), - $GLOBALS['config']['HIDE_PUBLIC_LINKS'] + $GLOBALS['config']['HIDE_PUBLIC_LINKS'], + $GLOBALS['redirector'] ); /* Some Shaarlies may have very few links, so we need to look @@ -953,8 +950,7 @@ function showDailyRSS() { // For each day. foreach ($days as $day => $linkdates) { - $daydate = linkdate2timestamp($day.'_000000'); // Full text date - $rfc822date = linkdate2rfc822($day.'_000000'); + $dayDate = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $day.'_000000'); $absurl = escape(index_url($_SERVER).'?do=daily&day='.$day); // Absolute URL of the corresponding "Daily" page. // Build the HTML body of this RSS entry. @@ -965,9 +961,10 @@ 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']); + $l_date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $l['linkdate']); + $l['timestamp'] = $l_date->getTimestamp(); if (startsWith($l['url'], '?')) { $l['url'] = index_url($_SERVER) . $l['url']; // make permalink URL absolute } @@ -977,10 +974,10 @@ function showDailyRSS() { // Then build the HTML for this day: $tpl = new RainTPL; $tpl->assign('title', $GLOBALS['title']); - $tpl->assign('daydate', $daydate); + $tpl->assign('daydate', $dayDate->getTimestamp()); $tpl->assign('absurl', $absurl); $tpl->assign('links', $links); - $tpl->assign('rfc822date', escape($rfc822date)); + $tpl->assign('rssdate', escape($dayDate->format(DateTime::RSS))); $html = $tpl->draw('dailyrss', $return_string=true); echo $html . PHP_EOL; @@ -992,13 +989,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']['HIDE_PUBLIC_LINKS'] + $GLOBALS['config']['HIDE_PUBLIC_LINKS'], + $GLOBALS['redirector'] ); $day=Date('Ymd',strtotime('-1 day')); // Yesterday, in format YYYYMMDD. @@ -1016,7 +1018,7 @@ function showDaily() } try { - $linksToDisplay = $LINKSDB->filterDay($day); + $linksToDisplay = $LINKSDB->filter(LinkFilter::$FILTER_DAY, $day); } catch (Exception $exc) { error_log($exc); $linksToDisplay = array(); @@ -1029,9 +1031,10 @@ 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']); + $date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']); + $linksToDisplay[$key]['timestamp'] = $date->getTimestamp(); } /* We need to spread the articles on 3 columns. @@ -1055,12 +1058,13 @@ function showDaily() array_push($columns[$index],$link); // Put entry in this column. $fill[$index]+=$length; } - $PAGE = new pageBuilder; + + $dayDate = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $day.'_000000'); $data = array( 'linksToDisplay' => $linksToDisplay, 'linkcount' => count($LINKSDB), 'cols' => $columns, - 'day' => linkdate2timestamp($day.'_000000'), + 'day' => $dayDate->getTimestamp(), 'previousday' => $previousday, 'nextday' => $nextday, ); @@ -1068,10 +1072,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; } @@ -1089,9 +1093,29 @@ function renderPage() $LINKSDB = new LinkDB( $GLOBALS['config']['DATASTORE'], isLoggedIn(), - $GLOBALS['config']['HIDE_PUBLIC_LINKS'] + $GLOBALS['config']['HIDE_PUBLIC_LINKS'], + $GLOBALS['redirector'] ); + $updater = new Updater( + read_updates_file($GLOBALS['config']['UPDATES_FILE']), + $GLOBALS, + $LINKSDB, + isLoggedIn() + ); + try { + $newUpdates = $updater->update(); + if (! empty($newUpdates)) { + write_updates_file( + $GLOBALS['config']['UPDATES_FILE'], + $updater->getDoneUpdates() + ); + } + } + catch(Exception $e) { + die($e->getMessage()); + } + $PAGE = new pageBuilder; // Determine which page will be rendered. @@ -1101,9 +1125,9 @@ function renderPage() // Call plugin hooks for header, footer and includes, specifying which page will be rendered. // Then assign generated data to RainTPL. $common_hooks = array( + 'includes', 'header', 'footer', - 'includes', ); $pluginManager = PluginManager::getInstance(); foreach($common_hooks as $name) { @@ -1140,13 +1164,25 @@ function renderPage() if ($targetPage == Router::$PAGE_PICWALL) { // Optionally filter the results: - $links=array(); - if (!empty($_GET['searchterm'])) $links = $LINKSDB->filterFulltext($_GET['searchterm']); - elseif (!empty($_GET['searchtags'])) $links = $LINKSDB->filterTags(trim($_GET['searchtags'])); - else $links = $LINKSDB; + $searchtags = !empty($_GET['searchtags']) ? escape($_GET['searchtags']) : ''; + $searchterm = !empty($_GET['searchterm']) ? escape($_GET['searchterm']) : ''; + if (! empty($searchtags) && ! empty($searchterm)) { + $links = $LINKSDB->filter( + LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT, + array($searchtags, $searchterm) + ); + } + elseif ($searchtags) { + $links = $LINKSDB->filter(LinkFilter::$FILTER_TAG, $searchtags); + } + elseif ($searchterm) { + $links = $LINKSDB->filter(LinkFilter::$FILTER_TEXT, $searchterm); + } + else { + $links = $LINKSDB; + } - $body=''; - $linksToDisplay=array(); + $linksToDisplay = array(); // Get only links which have a thumbnail. foreach($links as $link) @@ -1181,11 +1217,26 @@ function renderPage() // We sort tags alphabetically, then choose a font size according to count. // First, find max value. - $maxcount=0; foreach($tags as $key=>$value) $maxcount=max($maxcount,$value); - ksort($tags); + $maxcount = 0; + foreach ($tags as $value) { + $maxcount = max($maxcount, $value); + } + + // Sort tags alphabetically: case insensitive, support locale if avalaible. + uksort($tags, function($a, $b) { + // Collator is part of PHP intl. + if (class_exists('Collator')) { + $c = new Collator(setlocale(LC_COLLATE, 0)); + if (!intl_is_failure(intl_get_error_code())) { + return $c->compare($a, $b); + } + } + return strcasecmp($a, $b); + }); + $tagList=array(); foreach($tags as $key=>$value) - // Tag font size scaling: default 15 and 30 logarithm bases affect scaling, 22 and 6 are arbitrary font sizes for max and min sizes. + // Tag font size scaling: default 15 and 30 logarithm bases affect scaling, 22 and 6 are arbitrary font sizes for max and min sizes. { $tagList[$key] = array('count'=>$value,'size'=>log($value, 15) / log($maxcount, 30) * (22-6) + 6); } @@ -1204,6 +1255,11 @@ 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'); @@ -1268,13 +1324,15 @@ function renderPage() } if (isset($params['searchtags'])) { - $tags = explode(' ',$params['searchtags']); - $tags=array_diff($tags, array($_GET['removetag'])); // Remove value from array $tags. - if (count($tags)==0) { + $tags = explode(' ', $params['searchtags']); + // Remove value from array $tags. + $tags = array_diff($tags, array($_GET['removetag'])); + $params['searchtags'] = implode(' ',$tags); + + if (empty($params['searchtags'])) { unset($params['searchtags']); - } else { - $params['searchtags'] = implode(' ',$tags); } + unset($params['page']); // We also remove page (keeping the same page has no sense, since the results are different) } header('Location: ?'.http_build_query($params)); @@ -1439,21 +1497,23 @@ function renderPage() // -------- User wants to rename a tag or delete it if ($targetPage == Router::$PAGE_CHANGETAG) { - if (empty($_POST['fromtag'])) - { - $PAGE->assign('linkcount',count($LINKSDB)); - $PAGE->assign('token',getToken()); + if (empty($_POST['fromtag']) || (empty($_POST['totag']) && isset($_POST['renametag']))) { + $PAGE->assign('linkcount', count($LINKSDB)); + $PAGE->assign('token', getToken()); $PAGE->assign('tags', $LINKSDB->allTags()); $PAGE->renderPage('changetag'); exit; } - if (!tokenOk($_POST['token'])) die('Wrong token.'); + + if (!tokenOk($_POST['token'])) { + die('Wrong token.'); + } // Delete a tag: - if (!empty($_POST['deletetag']) && !empty($_POST['fromtag'])) - { + if (isset($_POST['deletetag']) && !empty($_POST['fromtag'])) { $needle=trim($_POST['fromtag']); - $linksToAlter = $LINKSDB->filterTags($needle,true); // True for case-sensitive tag search. + // True for case-sensitive tag search. + $linksToAlter = $LINKSDB->filter(LinkFilter::$FILTER_TAG, $needle, true); foreach($linksToAlter as $key=>$value) { $tags = explode(' ',trim($value['tags'])); @@ -1461,16 +1521,16 @@ function renderPage() $value['tags']=trim(implode(' ',$tags)); $LINKSDB[$key]=$value; } - $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); // Save to disk. + $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); echo ''; exit; } // Rename a tag: - if (!empty($_POST['renametag']) && !empty($_POST['fromtag']) && !empty($_POST['totag'])) - { + if (isset($_POST['renametag']) && !empty($_POST['fromtag']) && !empty($_POST['totag'])) { $needle=trim($_POST['fromtag']); - $linksToAlter = $LINKSDB->filterTags($needle,true); // true for case-sensitive tag search. + // True for case-sensitive tag search. + $linksToAlter = $LINKSDB->filter(LinkFilter::$FILTER_TAG, $needle, true); foreach($linksToAlter as $key=>$value) { $tags = explode(' ',trim($value['tags'])); @@ -1495,21 +1555,42 @@ function renderPage() // -------- User clicked the "Save" button when editing a link: Save link to database. if (isset($_POST['save_edit'])) { - if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away! - $tags = trim(preg_replace('/\s\s+/',' ', $_POST['lf_tags'])); // Remove multiple spaces. - $tags = implode(' ', array_unique(explode(' ', $tags))); // Remove duplicates. - $linkdate=$_POST['lf_linkdate']; + // Go away! + if (! tokenOk($_POST['token'])) { + die('Wrong token.'); + } + // Remove multiple spaces. + $tags = trim(preg_replace('/\s\s+/', ' ', $_POST['lf_tags'])); + // Remove first '-' char in tags. + $tags = preg_replace('/(^| )\-/', '$1', $tags); + // Remove duplicates. + $tags = implode(' ', array_unique(explode(' ', $tags))); + $linkdate = $_POST['lf_linkdate']; $url = trim($_POST['lf_url']); - if (!startsWith($url,'http:') && !startsWith($url,'https:') && !startsWith($url,'ftp:') && !startsWith($url,'magnet:') && !startsWith($url,'?') && !startsWith($url,'javascript:')) - $url = 'http://'.$url; - $link = array('title'=>trim($_POST['lf_title']),'url'=>$url,'description'=>trim($_POST['lf_description']),'private'=>(isset($_POST['lf_private']) ? 1 : 0), - 'linkdate'=>$linkdate,'tags'=>str_replace(',',' ',$tags)); - if ($link['title']=='') $link['title']=$link['url']; // If title is empty, use the URL as title. + if (! startsWith($url, 'http:') && ! startsWith($url, 'https:') + && ! startsWith($url, 'ftp:') && ! startsWith($url, 'magnet:') + && ! startsWith($url, '?') && ! startsWith($url, 'javascript:') + ) { + $url = 'http://' . $url; + } + + $link = array( + 'title' => trim($_POST['lf_title']), + 'url' => $url, + 'description' => $_POST['lf_description'], + 'private' => (isset($_POST['lf_private']) ? 1 : 0), + 'linkdate' => $linkdate, + 'tags' => str_replace(',', ' ', $tags) + ); + // If title is empty, use the URL as title. + if ($link['title'] == '') { + $link['title'] = $link['url']; + } $pluginManager->executeHooks('save_link', $link); $LINKSDB[$linkdate] = $link; - $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); // Save to disk. + $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); pubsubhub(); // If we are called from the bookmarklet, we must close the popup: @@ -1518,10 +1599,12 @@ function renderPage() exit; } - $returnurl = !empty($_POST['returnurl']) ? escape($_POST['returnurl']): '?'; + $returnurl = !empty($_POST['returnurl']) ? $_POST['returnurl'] : '?'; $location = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link')); - $location .= '#'.smallHash($_POST['lf_linkdate']); // Scroll to the link which has been edited. - header('Location: '. $location); // After saving the link, redirect to the page the user was on. + // Scroll to the link which has been edited. + $location .= '#' . smallHash($_POST['lf_linkdate']); + // After saving the link, redirect to the page the user was on. + header('Location: '. $location); exit; } @@ -1609,7 +1692,7 @@ function renderPage() // -------- User want to post a new link: Display link edit form. if (isset($_GET['post'])) { - $url = cleanup_url($_GET['post']); + $url = cleanup_url(escape($_GET['post'])); $link_is_new = false; // Check if URL is not already in database (in this case, we will edit the existing link) @@ -1627,35 +1710,24 @@ function renderPage() // 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 - list($headers, $data) = get_http_url($url, 4); - // FIXME: Decode charset according to specified in either 1) HTTP response headers or 2) in html + list($headers, $content) = get_http_response($url, 4); if (strpos($headers[0], '200 OK') !== false) { - // Look for charset in html header. - preg_match('##Usi', $data, $meta); - - // If found, extract encoding. - if (!empty($meta[0])) { - // Get encoding specified in header. - preg_match('#charset="?(.*)"#si', $meta[0], $enc); - // If charset not found, use utf-8. - $html_charset = (!empty($enc[1])) ? strtolower($enc[1]) : 'utf-8'; - } - else { - $html_charset = 'utf-8'; - } - - // Extract title - $title = html_extract_title($data); - if (!empty($title)) { - // Re-encode title in utf-8 if necessary. - $title = ($html_charset == 'iso-8859-1') ? utf8_encode($title) : $title; + // Retrieve charset. + $charset = get_charset($headers, $content); + // Extract title. + $title = html_extract_title($content); + // Re-encode title in utf-8 if necessary. + if (! empty($title) && $charset != 'utf-8') { + $title = mb_convert_encoding($title, $charset, 'utf-8'); } } } + if ($url == '') { $url = '?' . smallHash($linkdate); $title = 'Note: '; } + $link = array( 'linkdate' => $linkdate, 'title' => $title, @@ -1716,7 +1788,8 @@ HTML; ($exportWhat=='private' && $link['private']!=0) || ($exportWhat=='public' && $link['private']==0)) { - echo '
'.$link['title']."\n"; if ($link['description']!='') echo '
'.$link['description']."\n"; @@ -1750,6 +1823,54 @@ HTML; exit; } + // Plugin administration page + if ($targetPage == Router::$PAGE_PLUGINSADMIN) { + $pluginMeta = $pluginManager->getPluginsMeta(); + + // Split plugins into 2 arrays: ordered enabled plugins and disabled. + $enabledPlugins = array_filter($pluginMeta, function($v) { return $v['order'] !== false; }); + // Load parameters. + $enabledPlugins = load_plugin_parameter_values($enabledPlugins, $GLOBALS['plugins']); + uasort( + $enabledPlugins, + function($a, $b) { return $a['order'] - $b['order']; } + ); + $disabledPlugins = array_filter($pluginMeta, function($v) { return $v['order'] === false; }); + + $PAGE->assign('enabledPlugins', $enabledPlugins); + $PAGE->assign('disabledPlugins', $disabledPlugins); + $PAGE->renderPage('pluginsadmin'); + exit; + } + + // Plugin administration form action + if ($targetPage == Router::$PAGE_SAVE_PLUGINSADMIN) { + try { + if (isset($_POST['parameters_form'])) { + unset($_POST['parameters_form']); + foreach ($_POST as $param => $value) { + $GLOBALS['plugins'][$param] = escape($value); + } + } + else { + $GLOBALS['config']['ENABLED_PLUGINS'] = save_plugin_config($_POST); + } + writeConfig($GLOBALS, isLoggedIn()); + } + catch (Exception $e) { + error_log( + 'ERROR while saving plugin configuration:.' . PHP_EOL . + $e->getMessage() + ); + + // TODO: do not handle exceptions/errors in JS. + echo ''; + exit; + } + header('Location: ?do='. Router::$PAGE_PLUGINSADMIN); + exit; + } + // -------- Otherwise, simply display search form and links: showLinkList($PAGE, $LINKSDB); exit; @@ -1763,7 +1884,8 @@ function importFile() $LINKSDB = new LinkDB( $GLOBALS['config']['DATASTORE'], isLoggedIn(), - $GLOBALS['config']['HIDE_PUBLIC_LINKS'] + $GLOBALS['config']['HIDE_PUBLIC_LINKS'], + $GLOBALS['redirector'] ); $filename=$_FILES['filetoupload']['name']; $filesize=$_FILES['filetoupload']['size']; @@ -1849,83 +1971,94 @@ function importFile() // This function fills all the necessary fields in the $PAGE for the template 'linklist.html' function buildLinkList($PAGE,$LINKSDB) { - // ---- Filter link database according to parameters - $linksToDisplay=array(); - $search_type=''; - $search_crits=''; - if (isset($_GET['searchterm'])) // Fulltext search - { - $linksToDisplay = $LINKSDB->filterFulltext(trim($_GET['searchterm'])); - $search_crits=escape(trim($_GET['searchterm'])); - $search_type='fulltext'; + // Filter link database according to parameters. + $searchtags = !empty($_GET['searchtags']) ? escape($_GET['searchtags']) : ''; + $searchterm = !empty($_GET['searchterm']) ? escape(trim($_GET['searchterm'])) : ''; + $privateonly = !empty($_SESSION['privateonly']) ? true : false; + + // Search tags + fullsearch. + if (! empty($searchtags) && ! empty($searchterm)) { + $linksToDisplay = $LINKSDB->filter( + LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT, + array($searchtags, $searchterm), + false, + $privateonly + ); } - elseif (isset($_GET['searchtags'])) // Search by tag - { - $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags'])); - $search_crits=explode(' ',escape(trim($_GET['searchtags']))); - $search_type='tags'; + // Search by tags. + elseif (! empty($searchtags)) { + $linksToDisplay = $LINKSDB->filter( + LinkFilter::$FILTER_TAG, + $searchtags, + false, + $privateonly + ); } - elseif (isset($_SERVER['QUERY_STRING']) && preg_match('/[a-zA-Z0-9-_@]{6}(&.+?)?/',$_SERVER['QUERY_STRING'])) // Detect smallHashes in URL - { - $linksToDisplay = $LINKSDB->filterSmallHash(substr(trim($_SERVER["QUERY_STRING"], '/'),0,6)); - if (count($linksToDisplay)==0) - { - header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found"); - echo '

404 Not found.

Oh crap. The link you are trying to reach does not exist or has been deleted.'; - echo '
Would you mind clicking here?'; - exit; - } - $search_type='permalink'; + // Fulltext search. + elseif (! empty($searchterm)) { + $linksToDisplay = $LINKSDB->filter( + LinkFilter::$FILTER_TEXT, + $searchterm, + false, + $privateonly + ); } - else - $linksToDisplay = $LINKSDB; // Otherwise, display without filtering. - + // Detect smallHashes in URL. + elseif (! empty($_SERVER['QUERY_STRING']) + && preg_match('/[a-zA-Z0-9-_@]{6}(&.+?)?/', $_SERVER['QUERY_STRING']) + ) { + $linksToDisplay = $LINKSDB->filter( + LinkFilter::$FILTER_HASH, + substr(trim($_SERVER["QUERY_STRING"], '/'), 0, 6) + ); - // Option: Show only private links - if (!empty($_SESSION['privateonly'])) - { - $tmp = array(); - foreach($linksToDisplay as $linkdate=>$link) - { - if ($link['private']!=0) $tmp[$linkdate]=$link; + if (count($linksToDisplay) == 0) { + $PAGE->render404('The link you are trying to reach does not exist or has been deleted.'); + exit; } - $linksToDisplay=$tmp; + } + // Otherwise, display without filtering. + else { + $linksToDisplay = $LINKSDB->filter('', '', false, $privateonly); } // ---- Handle paging. - /* Can someone explain to me why you get the following error when using array_keys() on an object which implements the interface ArrayAccess??? - "Warning: array_keys() expects parameter 1 to be array, object given in ... " - If my class implements ArrayAccess, why won't array_keys() accept it ? ( $keys=array_keys($linksToDisplay); ) - */ - $keys=array(); foreach($linksToDisplay as $key=>$value) { $keys[]=$key; } // Stupid and ugly. Thanks PHP. + $keys = array(); + foreach ($linksToDisplay as $key => $value) { + $keys[] = $key; + } // If there is only a single link, we change on-the-fly the title of the page. - if (count($linksToDisplay)==1) $GLOBALS['pagetitle'] = $linksToDisplay[$keys[0]]['title'].' - '.$GLOBALS['title']; + if (count($linksToDisplay) == 1) { + $GLOBALS['pagetitle'] = $linksToDisplay[$keys[0]]['title'].' - '.$GLOBALS['title']; + } // Select articles according to paging. - $pagecount = ceil(count($keys)/$_SESSION['LINKS_PER_PAGE']); - $pagecount = ($pagecount==0 ? 1 : $pagecount); - $page=( empty($_GET['page']) ? 1 : intval($_GET['page'])); - $page = ( $page<1 ? 1 : $page ); - $page = ( $page>$pagecount ? $pagecount : $page ); - $i = ($page-1)*$_SESSION['LINKS_PER_PAGE']; // Start index. - $end = $i+$_SESSION['LINKS_PER_PAGE']; - $linkDisp=array(); // Links to display + $pagecount = ceil(count($keys) / $_SESSION['LINKS_PER_PAGE']); + $pagecount = $pagecount == 0 ? 1 : $pagecount; + $page= empty($_GET['page']) ? 1 : intval($_GET['page']); + $page = $page < 1 ? 1 : $page; + $page = $page > $pagecount ? $pagecount : $page; + // Start index. + $i = ($page-1) * $_SESSION['LINKS_PER_PAGE']; + $end = $i + $_SESSION['LINKS_PER_PAGE']; + $linkDisp = array(); while ($i<$end && $igetTimestamp(); + $taglist = explode(' ', $link['tags']); uasort($taglist, 'strcasecmp'); - $link['taglist']=$taglist; + $link['taglist'] = $taglist; $link['shorturl'] = smallHash($link['linkdate']); - if ($link["url"][0] === '?' && // Check for both signs of a note: starting with ? and 7 chars long. I doubt that you'll post any links that look like this. - strlen($link["url"]) === 7) { - $link["url"] = index_url($_SERVER) . $link["url"]; + // Check for both signs of a note: starting with ? and 7 chars long. + if ($link['url'][0] === '?' && + strlen($link['url']) === 7) { + $link['url'] = index_url($_SERVER) . $link['url']; } $linkDisp[$keys[$i]] = $link; @@ -1933,13 +2066,18 @@ function buildLinkList($PAGE,$LINKSDB) } // Compute paging navigation - $searchterm= ( empty($_GET['searchterm']) ? '' : '&searchterm='.$_GET['searchterm'] ); - $searchtags= ( empty($_GET['searchtags']) ? '' : '&searchtags='.$_GET['searchtags'] ); - $paging=''; - $previous_page_url=''; if ($i!=count($keys)) $previous_page_url='?page='.($page+1).$searchterm.$searchtags; - $next_page_url='';if ($page>1) $next_page_url='?page='.($page-1).$searchterm.$searchtags; + $searchtagsUrl = empty($searchtags) ? '' : '&searchtags=' . urlencode($searchtags); + $searchtermUrl = empty($searchterm) ? '' : '&searchterm=' . urlencode($searchterm); + $previous_page_url = ''; + if ($i != count($keys)) { + $previous_page_url = '?page=' . ($page+1) . $searchtermUrl . $searchtagsUrl; + } + $next_page_url=''; + if ($page>1) { + $next_page_url = '?page=' . ($page-1) . $searchtermUrl . $searchtagsUrl; + } - $token = ''; if (isLoggedIn()) $token=getToken(); + $token = isLoggedIn() ? getToken() : ''; // Fill all template fields. $data = array( @@ -1949,13 +2087,17 @@ function buildLinkList($PAGE,$LINKSDB) 'page_current' => $page, 'page_max' => $pagecount, 'result_count' => count($linksToDisplay), - 'search_type' => $search_type, - 'search_crits' => $search_crits, + 'search_term' => $searchterm, + 'search_tags' => $searchtags, 'redirector' => empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'], // Optional redirector URL. 'token' => $token, '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())); @@ -2221,45 +2363,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. @@ -2311,11 +2414,11 @@ function genThumbnail() else // This is a flickr page (html) { // Get the flickr html page. - list($headers, $data) = get_http_url($url, 20); + list($headers, $content) = get_http_response($url, 20); if (strpos($headers[0], '200 OK') !== false) { // flickr now nicely provides the URL of the thumbnail in each flickr page. - preg_match('! if ($imageurl=='') { - preg_match('! tag on that page // http://www.ted.com/talks/mikko_hypponen_fighting_viruses_defending_the_net.html // - list($headers, $data) = get_http_url($url, 5); + list($headers, $content) = get_http_response($url, 5); if (strpos($headers[0], '200 OK') !== false) { // Extract the link to the thumbnail - preg_match('!link rel="image_src" href="(http://images.ted.com/images/ted/.+_\d+x\d+\.jpg)"!',$data,$matches); + preg_match('!link rel="image_src" href="(http://images.ted.com/images/ted/.+_\d+x\d+\.jpg)"!', $content, $matches); if (!empty($matches[1])) { // Let's download the image. $imageurl=$matches[1]; // No control on image size, so wait long enough - list($headers, $data) = get_http_url($imageurl, 20); + list($headers, $content) = get_http_response($imageurl, 20); if (strpos($headers[0], '200 OK') !== false) { $filepath=$GLOBALS['config']['CACHEDIR'].'/'.$thumbname; - file_put_contents($filepath,$data); // Save image to cache. + file_put_contents($filepath, $content); // Save image to cache. if (resizeImage($filepath)) { header('Content-Type: image/jpeg'); @@ -2395,18 +2500,19 @@ function genThumbnail() // There is no thumbnail available for xkcd comics, so download the whole image and resize it. // http://xkcd.com/327/ // <BLABLA> - list($headers, $data) = get_http_url($url, 5); + list($headers, $content) = get_http_response($url, 5); if (strpos($headers[0], '200 OK') !== false) { // Extract the link to the thumbnail - preg_match('!