aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-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;