aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorSeb Sauvage <sebsauvage@sebsauvage.net>2011-09-28 14:44:02 +0200
committerEmilien Klein <emilien@klein.st>2011-09-28 14:44:02 +0200
commit28770b26b1a33ed079f9d777bfc6346e7a291c19 (patch)
tree510d5cfd2ba22daec1b473f5165fbce3928e6364
parent0adcceeea91382dedecc7dbc3531b2a754142f2a (diff)
downloadShaarli-28770b26b1a33ed079f9d777bfc6346e7a291c19.tar.gz
Shaarli-28770b26b1a33ed079f9d777bfc6346e7a291c19.tar.zst
Shaarli-28770b26b1a33ed079f9d777bfc6346e7a291c19.zip
Version 0.0.22 beta:
- Corrected: Title was not properly passed if you had to login when using the bookmarklet (patch by shenshei). - New: Support for thumbnails for flickr.com. - Changed: Some hosts (flickr, vimeo) are slow as hell for the thumbnails, or require an extra HTTP request. For these hosts the thumbnail generation has been deported outside the generation of the page to keep Shaarli snappy. For those slow services the thumbnails are also cached. - New: As per request, you can now stay signed in (Your session will be kept open even if you close the browser). This is just a checkbox in login screen.
-rw-r--r--index.php168
1 files changed, 147 insertions, 21 deletions
diff --git a/index.php b/index.php
index 57a64004..6732083c 100644
--- a/index.php
+++ b/index.php
@@ -1,5 +1,5 @@
1<?php 1<?php
2// Shaarli 0.0.21 beta - Shaare your links... 2// Shaarli 0.0.22 beta - Shaare your links...
3// The personal, minimalist, super-fast, no-database delicious clone. By sebsauvage.net 3// The personal, minimalist, super-fast, no-database delicious clone. By sebsauvage.net
4// http://sebsauvage.net/wiki/doku.php?id=php:shaarli 4// http://sebsauvage.net/wiki/doku.php?id=php:shaarli
5// Licence: http://www.opensource.org/licenses/zlib-license.php 5// Licence: http://www.opensource.org/licenses/zlib-license.php
@@ -18,7 +18,8 @@ define('BAN_DURATION',1800); // Ban duration for IP address after login failures
18define('OPEN_SHAARLI',false); // If true, anyone can add/edit/delete links without having to login 18define('OPEN_SHAARLI',false); // If true, anyone can add/edit/delete links without having to login
19define('HIDE_TIMESTAMPS',false); // If true, the moment when links were saved are not shown to users that are not logged in. 19define('HIDE_TIMESTAMPS',false); // If true, the moment when links were saved are not shown to users that are not logged in.
20define('ENABLE_THUMBNAILS',true); // Enable thumbnails in links. 20define('ENABLE_THUMBNAILS',true); // Enable thumbnails in links.
21 21define('CACHEDIR','cache'); // Cache directory for thumbnails for SLOW services (like flickr)
22
22// ----------------------------------------------------------------------------------------------- 23// -----------------------------------------------------------------------------------------------
23// Program config (touch at your own risks !) 24// Program config (touch at your own risks !)
24define('UPDATECHECK_FILENAME',DATADIR.'/lastupdatecheck.txt'); // For updates check of Shaarli. 25define('UPDATECHECK_FILENAME',DATADIR.'/lastupdatecheck.txt'); // For updates check of Shaarli.
@@ -48,9 +49,11 @@ header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
48header("Cache-Control: no-store, no-cache, must-revalidate"); 49header("Cache-Control: no-store, no-cache, must-revalidate");
49header("Cache-Control: post-check=0, pre-check=0", false); 50header("Cache-Control: post-check=0, pre-check=0", false);
50header("Pragma: no-cache"); 51header("Pragma: no-cache");
51define('shaarli_version','0.0.21 beta'); 52define('shaarli_version','0.0.22 beta');
52if (!is_dir(DATADIR)) { mkdir(DATADIR,0705); chmod(DATADIR,0705); } 53if (!is_dir(DATADIR)) { mkdir(DATADIR,0705); chmod(DATADIR,0705); }
54if (!is_dir(CACHEDIR)) { mkdir(CACHEDIR,0705); chmod(CACHEDIR,0705); }
53if (!is_file(DATADIR.'/.htaccess')) { file_put_contents(DATADIR.'/.htaccess',"Allow from none\nDeny from all\n"); } // Protect data files. 55if (!is_file(DATADIR.'/.htaccess')) { file_put_contents(DATADIR.'/.htaccess',"Allow from none\nDeny from all\n"); } // Protect data files.
56if (!is_file(CACHEDIR.'/.htaccess')) { file_put_contents(CACHEDIR.'/.htaccess',"Allow from none\nDeny from all\n"); } // Protect data files.
54if (!is_file(CONFIG_FILE)) install(); 57if (!is_file(CONFIG_FILE)) install();
55require CONFIG_FILE; // Read login/password hash into $GLOBALS. 58require CONFIG_FILE; // Read login/password hash into $GLOBALS.
56// Small protection against dodgy config files: 59// Small protection against dodgy config files:
@@ -162,7 +165,9 @@ function isLoggedIn()
162 logout(); 165 logout();
163 return false; 166 return false;
164 } 167 }
165 $_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT; // User accessed a page : Update his/her session expiration date. 168 if (!empty($_SESSION['longlastingsession'])) $_SESSION['expires_on']=time()+$_SESSION['longlastingsession']; // In case of "Stay signed in" checked.
169 else $_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT; // Standard session expiration date.
170
166 return true; 171 return true;
167} 172}
168 173
@@ -225,9 +230,22 @@ if (isset($_POST['login']))
225 if (!ban_canLogin()) die('I said: NO. You are banned for the moment. Go away.'); 230 if (!ban_canLogin()) die('I said: NO. You are banned for the moment. Go away.');
226 if (isset($_POST['password']) && tokenOk($_POST['token']) && (check_auth($_POST['login'], $_POST['password']))) 231 if (isset($_POST['password']) && tokenOk($_POST['token']) && (check_auth($_POST['login'], $_POST['password'])))
227 { // Login/password is ok. 232 { // Login/password is ok.
228 ban_loginOk(); 233 ban_loginOk();
234 // If user wants to keep the session cookie even after the browser closes:
235 if (!empty($_POST['longlastingsession']))
236 {
237 $_SESSION['longlastingsession']=31536000; // (31536000 seconds = 1 year)
238 $_SESSION['expires_on']=time()+$_SESSION['longlastingsession']; // Set session expiration on server-side.
239 session_set_cookie_params($_SESSION['longlastingsession']); // Set session cookie expiration on client side
240 session_regenerate_id(true); // Send cookie with new expiration date to browser.
241 }
242 else // Standard session expiration (=when browser closes)
243 {
244 session_set_cookie_params(0); // 0 means "When browser closes"
245 session_regenerate_id(true);
246 }
229 // Optional redirect after login: 247 // Optional redirect after login:
230 if (isset($_GET['post'])) { header('Location: ?post='.urlencode($_GET['post']).(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):'')); exit; } 248 if (isset($_GET['post'])) { header('Location: ?post='.urlencode($_GET['post']).(!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):'')); exit; }
231 if (isset($_POST['returnurl'])) 249 if (isset($_POST['returnurl']))
232 { 250 {
233 if (endsWith($_POST['returnurl'],'?do=login')) { header('Location: ?'); exit; } // Prevent loops over login screen. 251 if (endsWith($_POST['returnurl'],'?do=login')) { header('Location: ?'); exit; } // Prevent loops over login screen.
@@ -657,13 +675,13 @@ function renderPage()
657 exit; 675 exit;
658 } 676 }
659 $returnurl_html = (isset($_SERVER['HTTP_REFERER']) ? '<input type="hidden" name="returnurl" value="'.htmlspecialchars($_SERVER['HTTP_REFERER']).'">' : ''); 677 $returnurl_html = (isset($_SERVER['HTTP_REFERER']) ? '<input type="hidden" name="returnurl" value="'.htmlspecialchars($_SERVER['HTTP_REFERER']).'">' : '');
660 $loginform='<div id="headerform"><form method="post" name="loginform">Login: <input type="text" name="login">&nbsp;&nbsp;&nbsp;Password : <input type="password" name="password"> <input type="submit" value="Login" class="bigbutton"><input type="hidden" name="token" value="'.getToken().'">'.$returnurl_html.'</form></div>'; 678 $loginform='<div id="headerform"><form method="post" name="loginform">Login: <input type="text" name="login">&nbsp;&nbsp;&nbsp;Password : <input type="password" name="password"> <input type="submit" value="Login" class="bigbutton"><br>';
679 $loginform.='<input style="margin:10 0 0 40;" type="checkbox" name="longlastingsession">&nbsp;Stay signed in (Do not check on public computers)<input type="hidden" name="token" value="'.getToken().'">'.$returnurl_html.'</form></div>';
661 $onload = 'onload="document.loginform.login.focus();"'; 680 $onload = 'onload="document.loginform.login.focus();"';
662 $data = array('pageheader'=>$loginform,'body'=>'','onload'=>$onload); 681 $data = array('pageheader'=>$loginform,'body'=>'','onload'=>$onload);
663 templatePage($data); 682 templatePage($data);
664 exit; 683 exit;
665 } 684 }
666
667 // -------- User wants to logout. 685 // -------- User wants to logout.
668 if (startswith($_SERVER["QUERY_STRING"],'do=logout')) 686 if (startswith($_SERVER["QUERY_STRING"],'do=logout'))
669 { 687 {
@@ -740,7 +758,7 @@ function renderPage()
740 // Show login screen, then redirect to ?post=... 758 // Show login screen, then redirect to ?post=...
741 if (isset($_GET['post'])) 759 if (isset($_GET['post']))
742 { 760 {
743 header('Location: ?do=login&post='.urlencode($_GET['post']).(isset($_GET['source'])?'&source='.urlencode($_GET['source']):'')); // Redirect to login page, then back to post link. 761 header('Location: ?do=login&post='.urlencode($_GET['post']).(isset($_GET['title'])?'&title='.urlencode($_GET['title']):'').(isset($_GET['source'])?'&source='.urlencode($_GET['source']):'')); // Redirect to login page, then back to post link.
744 exit; 762 exit;
745 } 763 }
746 764
@@ -1291,6 +1309,10 @@ HTML;
1291function thumbnail($url) 1309function thumbnail($url)
1292{ 1310{
1293 if (!ENABLE_THUMBNAILS) return ''; 1311 if (!ENABLE_THUMBNAILS) return '';
1312
1313 // For most hosts, the URL of the thumbnail can be easily deduced from the URL of the link.
1314 // (eg. http://www.youtube.com/watch?v=spVypYk4kto ---> http://img.youtube.com/vi/spVypYk4kto/2.jpg )
1315 // ^^^^^^^^^^^ ^^^^^^^^^^^
1294 $domain = parse_url($url,PHP_URL_HOST); 1316 $domain = parse_url($url,PHP_URL_HOST);
1295 if ($domain=='youtube.com' || $domain=='www.youtube.com') 1317 if ($domain=='youtube.com' || $domain=='www.youtube.com')
1296 { 1318 {
@@ -1315,20 +1337,16 @@ function thumbnail($url)
1315 return '<div class="thumbnail"><a href="'.htmlspecialchars($url).'"><img src="'.htmlspecialchars($thumburl).'" width="120" style="height:auto;"></a></div>'; 1337 return '<div class="thumbnail"><a href="'.htmlspecialchars($url).'"><img src="'.htmlspecialchars($thumburl).'" width="120" style="height:auto;"></a></div>';
1316 } 1338 }
1317 } 1339 }
1318 if ($domain=='vimeo.com') 1340
1341 // Some other hosts are SLOW AS HELL and usually require an extra HTTP request to get the thumbnail URL.
1342 // So we deport the thumbnail generation in order not to slow down page generation
1343 // (and we also cache the thumbnail)
1344 if ($domain=='flickr.com' || endsWith($domain,'.flickr.com') || $domain=='vimeo.com')
1319 { 1345 {
1320 // This is more complex: we have to perform a HTTP request, then parse the result. 1346 $sign = hash_hmac('sha256', $url, $GLOBALS['salt']); // We use the salt to sign data (it's random, secret, and specific to each installation)
1321 // This slows down page generation :-( 1347 return '<div class="thumbnail"><a href="'.htmlspecialchars($url).'"><img src="?do=genthumbnail&hmac='.htmlspecialchars($sign).'&url='.urlencode($url).'" width="120" style="height:auto;"></a></div>';
1322 // Maybe we should deport this to javascript ? Example: http://stackoverflow.com/questions/1361149/get-img-thumbnails-from-vimeo/4285098#4285098
1323 $vid = substr(parse_url($url,PHP_URL_PATH),1);
1324 // We allow 2 seconds for Vimeo servers to respond.
1325 list($httpstatus,$headers,$data) = getHTTP('http://vimeo.com/api/v2/video/'.htmlspecialchars($vid).'.php',2);
1326 if (strpos($httpstatus,'200 OK'))
1327 {
1328 $t = unserialize($data);
1329 if (!empty($t[0]['thumbnail_medium'])) return '<div class="thumbnail"><a href="'.htmlspecialchars($url).'"><img src="'.htmlspecialchars($t[0]['thumbnail_medium']).'" width="120" style="height:auto;"></a></div>';
1330 }
1331 } 1348 }
1349
1332 return ''; // No thumbnail. 1350 return ''; // No thumbnail.
1333 1351
1334} 1352}
@@ -1569,6 +1587,113 @@ function writeConfig()
1569 } 1587 }
1570} 1588}
1571 1589
1590/* Because some f*cking services like Flickr require an extra HTTP request to get the thumbnail URL,
1591 I have deported the thumbnail URL code generation here, otherwise this would slow down page generation.
1592 The following function takes the URL a link (eg. a flickr page) and return the proper thumbnail.
1593 This function is called by passing the url:
1594 http://mywebsite.com/shaarli/?do=genthumbnail&hmac=[HMAC]&url=[URL]
1595 [URL] is the URL of the link (eg. a flickr page)
1596 [HMAC] is the signature for the [URL] (so that these URL cannot be forged).
1597 The function below will fetch the image from the webservice and store it in the cache.
1598*/
1599function genThumbnail()
1600{
1601 // Make sure the parameters in the URL were generated by us.
1602 $sign = hash_hmac('sha256', $_GET['url'], $GLOBALS['salt']);
1603 if ($sign!=$_GET['hmac']) die('Naughty boy !');
1604
1605 // Let's see if we don't already have the image for this URL in the cache.
1606 $thumbname=hash('sha1',$_GET['url']).'.jpg';
1607 if (is_file(CACHEDIR.'/'.$thumbname))
1608 { // We have the thumbnail, just serve it:
1609 header('Content-Type: image/jpeg');
1610 echo file_get_contents(CACHEDIR.'/'.$thumbname);
1611 return;
1612 }
1613 // We may also serve a blank image (if service did not respond)
1614 $blankname=hash('sha1',$_GET['url']).'.gif';
1615 if (is_file(CACHEDIR.'/'.$blankname))
1616 {
1617 header('Content-Type: image/gif');
1618 echo file_get_contents(CACHEDIR.'/'.$blankname);
1619 return;
1620 }
1621
1622 // Otherwise, generate the thumbnail.
1623 $url = $_GET['url'];
1624 $domain = parse_url($url,PHP_URL_HOST);
1625
1626 if ($domain=='flickr.com' || endsWith($domain,'.flickr.com'))
1627 {
1628 // WTF ? I need a flickr API key to get a fucking thumbnail ? No way.
1629 // I'll extract the thumbnail URL myself. First, we have to get the flickr HTML page.
1630 // All images in Flickr are in the form:
1631 // http://farm[farm].static.flickr.com/[server]/[id]_[secret]_[size].jpg
1632 // Example: http://farm7.static.flickr.com/6205/6088513739_fc158467fe_z.jpg
1633 // We want the 240x120 format, which is _m.jpg.
1634 // We search for the first image in the page which does not have the _s size,
1635 // when use the _m to get the thumbnail.
1636
1637 // Is this a link to an image, or to a flickr page ?
1638 $imageurl='';
1639 logm('url: '.$url);
1640 if (endswith(parse_url($url,PHP_URL_PATH),'.jpg'))
1641 { // This is a direct link to an image. eg. http://farm1.static.flickr.com/5/5921913_ac83ed27bd_o.jpg
1642 preg_match('!(http://farm\d+.static.flickr.com/\d+/\d+_\w+_)\w.jpg!',$url,$matches);
1643 if (!empty($matches[1])) $imageurl=$matches[1].'m.jpg';
1644 }
1645 else // this is a flickr page (html)
1646 {
1647 list($httpstatus,$headers,$data) = getHTTP($url,20); // Get the flickr html page.
1648 if (strpos($httpstatus,'200 OK'))
1649 {
1650 preg_match('!(http://farm\d+.static.flickr.com/\d+/\d+_\w+_)[^s].jpg!',$data,$matches);
1651 if (!empty($matches[1])) $imageurl=$matches[1].'m.jpg';
1652 }
1653 }
1654 if ($imageurl!='')
1655 { // Let's download the image.
1656 list($httpstatus,$headers,$data) = getHTTP($imageurl,10); // Image is 240x120, so 10 seconds to download should be enough.
1657 if (strpos($httpstatus,'200 OK'))
1658 {
1659 file_put_contents(CACHEDIR.'/'.$thumbname,$data); // Save image to cache.
1660 header('Content-Type: image/jpeg');
1661 echo $data;
1662 return;
1663 }
1664 }
1665 else logm('unkown flickr url: '.$url);
1666 }
1667
1668 if ($domain=='vimeo.com' )
1669 {
1670 // This is more complex: we have to perform a HTTP request, then parse the result.
1671 // Maybe we should deport this to javascript ? Example: http://stackoverflow.com/questions/1361149/get-img-thumbnails-from-vimeo/4285098#4285098
1672 $vid = substr(parse_url($url,PHP_URL_PATH),1);
1673 list($httpstatus,$headers,$data) = getHTTP('http://vimeo.com/api/v2/video/'.htmlspecialchars($vid).'.php',5);
1674 if (strpos($httpstatus,'200 OK'))
1675 {
1676 $t = unserialize($data);
1677 $imageurl = $t[0]['thumbnail_medium'];
1678 // Then we download the image and serve it to our client.
1679 list($httpstatus,$headers,$data) = getHTTP($imageurl,10);
1680 if (strpos($httpstatus,'200 OK'))
1681 {
1682 file_put_contents(CACHEDIR.'/'.$thumbname,$data); // Save image to cache.
1683 header('Content-Type: image/jpeg');
1684 echo $data;
1685 return;
1686 }
1687 }
1688 }
1689
1690 // Otherwise, return an empty image (8x8 transparent gif)
1691 $blankgif = base64_decode('R0lGODlhCAAIAIAAAP///////yH5BAEKAAEALAAAAAAIAAgAAAIHjI+py+1dAAA7');
1692 file_put_contents(CACHEDIR.'/'.$blankname,$blankgif); // Also put something in cache so that this URL is not requested twice.
1693 header('Content-Type: image/gif');
1694 echo $blankgif;
1695}
1696
1572// Invalidate caches when the database is changed or the user logs out. 1697// Invalidate caches when the database is changed or the user logs out.
1573// (eg. tags cache). 1698// (eg. tags cache).
1574function invalidateCaches() 1699function invalidateCaches()
@@ -1576,6 +1701,7 @@ function invalidateCaches()
1576 unset($_SESSION['tags']); 1701 unset($_SESSION['tags']);
1577} 1702}
1578 1703
1704if (startswith($_SERVER["QUERY_STRING"],'do=genthumbnail')) { genThumbnail(); exit; } // Thumbnail generation/cache does not need the link database.
1579$LINKSDB=new linkdb(isLoggedIn() || OPEN_SHAARLI); // Read links from database (and filter private links if used it not logged in). 1705$LINKSDB=new linkdb(isLoggedIn() || OPEN_SHAARLI); // Read links from database (and filter private links if used it not logged in).
1580if (startswith($_SERVER["QUERY_STRING"],'ws=')) { processWS(); exit; } // Webservices (for jQuery/jQueryUI) 1706if (startswith($_SERVER["QUERY_STRING"],'ws=')) { processWS(); exit; } // Webservices (for jQuery/jQueryUI)
1581if (!isset($_SESSION['LINKS_PER_PAGE'])) $_SESSION['LINKS_PER_PAGE']=LINKS_PER_PAGE; 1707if (!isset($_SESSION['LINKS_PER_PAGE'])) $_SESSION['LINKS_PER_PAGE']=LINKS_PER_PAGE;