diff options
author | Seb Sauvage <sebsauvage@sebsauvage.net> | 2011-09-28 14:44:02 +0200 |
---|---|---|
committer | Emilien Klein <emilien@klein.st> | 2011-09-28 14:44:02 +0200 |
commit | 28770b26b1a33ed079f9d777bfc6346e7a291c19 (patch) | |
tree | 510d5cfd2ba22daec1b473f5165fbce3928e6364 | |
parent | 0adcceeea91382dedecc7dbc3531b2a754142f2a (diff) | |
download | Shaarli-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.php | 168 |
1 files changed, 147 insertions, 21 deletions
@@ -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 | |||
18 | define('OPEN_SHAARLI',false); // If true, anyone can add/edit/delete links without having to login | 18 | define('OPEN_SHAARLI',false); // If true, anyone can add/edit/delete links without having to login |
19 | define('HIDE_TIMESTAMPS',false); // If true, the moment when links were saved are not shown to users that are not logged in. | 19 | define('HIDE_TIMESTAMPS',false); // If true, the moment when links were saved are not shown to users that are not logged in. |
20 | define('ENABLE_THUMBNAILS',true); // Enable thumbnails in links. | 20 | define('ENABLE_THUMBNAILS',true); // Enable thumbnails in links. |
21 | 21 | define('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 !) |
24 | define('UPDATECHECK_FILENAME',DATADIR.'/lastupdatecheck.txt'); // For updates check of Shaarli. | 25 | define('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"); | |||
48 | header("Cache-Control: no-store, no-cache, must-revalidate"); | 49 | header("Cache-Control: no-store, no-cache, must-revalidate"); |
49 | header("Cache-Control: post-check=0, pre-check=0", false); | 50 | header("Cache-Control: post-check=0, pre-check=0", false); |
50 | header("Pragma: no-cache"); | 51 | header("Pragma: no-cache"); |
51 | define('shaarli_version','0.0.21 beta'); | 52 | define('shaarli_version','0.0.22 beta'); |
52 | if (!is_dir(DATADIR)) { mkdir(DATADIR,0705); chmod(DATADIR,0705); } | 53 | if (!is_dir(DATADIR)) { mkdir(DATADIR,0705); chmod(DATADIR,0705); } |
54 | if (!is_dir(CACHEDIR)) { mkdir(CACHEDIR,0705); chmod(CACHEDIR,0705); } | ||
53 | if (!is_file(DATADIR.'/.htaccess')) { file_put_contents(DATADIR.'/.htaccess',"Allow from none\nDeny from all\n"); } // Protect data files. | 55 | if (!is_file(DATADIR.'/.htaccess')) { file_put_contents(DATADIR.'/.htaccess',"Allow from none\nDeny from all\n"); } // Protect data files. |
56 | if (!is_file(CACHEDIR.'/.htaccess')) { file_put_contents(CACHEDIR.'/.htaccess',"Allow from none\nDeny from all\n"); } // Protect data files. | ||
54 | if (!is_file(CONFIG_FILE)) install(); | 57 | if (!is_file(CONFIG_FILE)) install(); |
55 | require CONFIG_FILE; // Read login/password hash into $GLOBALS. | 58 | require 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"> 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"> 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"> 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; | |||
1291 | function thumbnail($url) | 1309 | function 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 | */ | ||
1599 | function 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). |
1574 | function invalidateCaches() | 1699 | function invalidateCaches() |
@@ -1576,6 +1701,7 @@ function invalidateCaches() | |||
1576 | unset($_SESSION['tags']); | 1701 | unset($_SESSION['tags']); |
1577 | } | 1702 | } |
1578 | 1703 | ||
1704 | if (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). |
1580 | if (startswith($_SERVER["QUERY_STRING"],'ws=')) { processWS(); exit; } // Webservices (for jQuery/jQueryUI) | 1706 | if (startswith($_SERVER["QUERY_STRING"],'ws=')) { processWS(); exit; } // Webservices (for jQuery/jQueryUI) |
1581 | if (!isset($_SESSION['LINKS_PER_PAGE'])) $_SESSION['LINKS_PER_PAGE']=LINKS_PER_PAGE; | 1707 | if (!isset($_SESSION['LINKS_PER_PAGE'])) $_SESSION['LINKS_PER_PAGE']=LINKS_PER_PAGE; |