X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=index.php;h=6e7e638c494da56076683d16698d7b76dae7b227;hb=99c9c9541b012eebdb84ee33a765bb18a62fb0d7;hp=a9491ed365366f15550ab0ef0b3976a57fb56f71;hpb=dcd653d10e168e655ce080da6886ef9fc2c1d3e1;p=github%2Fshaarli%2FShaarli.git
diff --git a/index.php b/index.php
index a9491ed3..6e7e638c 100644
--- a/index.php
+++ b/index.php
@@ -1,5 +1,5 @@
yZH23w
+ Small hashes:
+ - are unique (well, as unique as crc32, at last)
+ - are always 6 characters long.
+ - only use the following characters: a-z A-Z 0-9 - _ @
+ - are NOT cryptographically secure (they CAN be forged)
+ In Shaarli, they are used as a tinyurl-like link to individual entries.
+*/
+function smallHash($text)
+{
+ $t = rtrim(base64_encode(hash('crc32',$text,true)),'=');
+ $t = str_replace('+','-',$t); // Get rid of characters which need encoding in URLs.
+ $t = str_replace('/','_',$t);
+ $t = str_replace('=','@',$t);
+ return $t;
+}
+
+// Function inspired from http://www.php.net/manual/en/function.preg-replace.php#85722
+function text2clickable($url)
+{
+ return preg_replace('!((?:https?|ftp)://\S+[[:alnum:]]/?)!si','$1 ',$url);
+}
// ------------------------------------------------------------------------------------------
// 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.)
@@ -381,7 +404,7 @@ function getHTTP($url,$timeout=30)
{
$options = array('http'=>array('method'=>'GET','timeout' => $timeout)); // Force network timeout
$context = stream_context_create($options);
- $data=file_get_contents($url,false,$context,-1, 2000000); // We download at most 2 Mb from source.
+ $data=file_get_contents($url,false,$context,-1, 4000000); // We download at most 4 Mb from source.
if (!$data) { $lasterror=error_get_last(); return array($lasterror['message'],array(),''); }
$httpStatus=$http_response_header[0]; // eg. "HTTP/1.1 200 OK"
$responseHeaders=http_parse_headers($http_response_header);
@@ -567,6 +590,22 @@ class linkdb implements Iterator, Countable, ArrayAccess
krsort($filtered);
return $filtered;
}
+
+ // Filter by smallHash.
+ // Only 1 article is returned.
+ public function filterSmallHash($smallHash)
+ {
+ $filtered=array();
+ foreach($this->links as $l)
+ {
+ if ($smallHash==smallHash($l['linkdate'])) // Yes, this is ugly and slow
+ {
+ $filtered[$l['linkdate']] = $l;
+ return $filtered;
+ }
+ }
+ return $filtered;
+ }
// Returns the list of all tags
// Output: associative array key=tags, value=0
@@ -1011,7 +1050,7 @@ HTML;
$linkdate = strval(date('Ymd_His'));
$title = (empty($_GET['title']) ? '' : $_GET['title'] ); // Get title if it was provided in URL (by the bookmarklet).
$description=''; $tags=''; $private=0;
- if (parse_url($url,PHP_URL_SCHEME)=='') $url = 'http://'.$url;
+ if (($url!='') && parse_url($url,PHP_URL_SCHEME)=='') $url = 'http://'.$url;
// If this is an HTTP link, we try go get the page to extact the title (otherwise we will to straight to the edit form.)
if (empty($title) && parse_url($url,PHP_URL_SCHEME)=='http')
{
@@ -1019,6 +1058,7 @@ HTML;
// FIXME: Decode charset according to specified in either 1) HTTP response headers or 2)
in html
if (strpos($status,'200 OK')) $title=html_entity_decode(html_extract_title($data),ENT_QUOTES,'UTF-8');
}
+ if ($url=='') $url='?'.smallHash($linkdate); // In case of empty URL, this is just a text (with a link that point to itself)
$link = array('linkdate'=>$linkdate,'title'=>$title,'url'=>$url,'description'=>$description,'tags'=>$tags,'private'=>0);
}
list($editform,$onload)=templateEditForm($link,$link_is_new);
@@ -1236,7 +1276,7 @@ HTML;
function templateLinkList()
{
global $LINKSDB;
-
+
// Search according to entered search terms:
$linksToDisplay=array();
$searched='';
@@ -1251,6 +1291,10 @@ function templateLinkList()
$tagshtml=''; foreach(explode(' ',trim($_GET['searchtags'])) as $tag) $tagshtml.=''.htmlspecialchars($tag).' x ';
$searched=' '.count($linksToDisplay).' results for tags '.$tagshtml.':';
}
+ elseif (preg_match('/[a-zA-Z0-9-_@]{6}/',$_SERVER["QUERY_STRING"])) // Detect smallHashes in URL
+ {
+ $linksToDisplay = $LINKSDB->filterSmallHash($_SERVER["QUERY_STRING"]);
+ }
else
$linksToDisplay = $LINKSDB; // otherwise, display without filtering.
@@ -1273,7 +1317,7 @@ function templateLinkList()
while ($i<$end && $i';
@@ -1281,9 +1325,10 @@ function templateLinkList()
if ($link['tags']!='') foreach(explode(' ',$link['tags']) as $tag) { $tags.=''.htmlspecialchars($tag).' '; }
$linklist.='
';
}
if ($domain=='i.imgur.com')
{
@@ -1356,6 +1402,15 @@ function thumbnail($url)
return '
';
}
+ // For all other, we try to make a thumbnail of links ending with .jpg/jpeg/png/gif
+ // Technically speaking, we should download ALL links and check their Content-Type to see if they are images.
+ // But using the extension will do.
+ $ext=strtolower(pathinfo($url,PATHINFO_EXTENSION));
+ if ($ext=='jpg' || $ext=='jpeg' || $ext=='png' || $ext=='gif')
+ {
+ $sign = hash_hmac('sha256', $url, $GLOBALS['salt']); // We use the salt to sign data (it's random, secret, and specific to each installation)
+ return '
@@ -1645,7 +1702,6 @@ function genThumbnail()
// Is this a link to an image, or to a flickr page ?
$imageurl='';
- logm('url: '.$url);
if (endswith(parse_url($url,PHP_URL_PATH),'.jpg'))
{ // This is a direct link to an image. eg. http://farm1.static.flickr.com/5/5921913_ac83ed27bd_o.jpg
preg_match('!(http://farm\d+.static.flickr.com/\d+/\d+_\w+_)\w.jpg!',$url,$matches);
@@ -1671,7 +1727,6 @@ function genThumbnail()
return;
}
}
- else logm('unkown flickr url: '.$url);
}
if ($domain=='vimeo.com' )
@@ -1695,6 +1750,20 @@ function genThumbnail()
}
}
}
+
+ // For all other domains, we try to download the image and make a thumbnail.
+ list($httpstatus,$headers,$data) = getHTTP($url,30); // We allow 30 seconds max to download (and downloads are limited to 4 Mb)
+ if (strpos($httpstatus,'200 OK'))
+ {
+ $filepath=CACHEDIR.'/'.$thumbname;
+ file_put_contents($filepath,$data); // Save image to cache.
+ if (resizeImage($filepath))
+ {
+ header('Content-Type: image/jpeg');
+ echo file_get_contents($filepath);
+ return;
+ }
+ }
// Otherwise, return an empty image (8x8 transparent gif)
$blankgif = base64_decode('R0lGODlhCAAIAIAAAP///////yH5BAEKAAEALAAAAAAIAAgAAAIHjI+py+1dAAA7');
@@ -1703,6 +1772,36 @@ function genThumbnail()
echo $blankgif;
}
+// Make a thumbnail of the image (to width: 120 pixels)
+// Returns true if success, false otherwise.
+function resizeImage($filepath)
+{
+ if (!function_exists('imagecreatefromjpeg')) return false; // GD not present: no thumbnail possible.
+
+ // Trick: some stupid people rename GIF as JPEG... or else.
+ // So we really try to open each image type whatever the extension is.
+ $header=file_get_contents($filepath,false,NULL,0,256); // Read first 256 bytes and try to sniff file type.
+ $im=false;
+ if (strpos($header,'GIF8')==0) $im = imagecreatefromgif($filepath); // Well this is crude, but it should be enough.
+ if (strpos($header,'PNG')==1) $im = imagecreatefrompng($filepath);
+ if (strpos($header,'JFIF')) $im = imagecreatefromjpeg($filepath);
+ if (!$im) return false; // Unable to open image (corrupted or not an image)
+ $w = imagesx($im);
+ $h = imagesy($im);
+ $nw = 120; // Desired width
+ $nh = floor(($h*$nw)/$w); // Compute new width/height while keeping ratio
+ // Resize image:
+ $im2 = imagecreatetruecolor($nw,$nh);
+ imagecopyresampled($im2, $im, 0, 0, 0, 0, $nw, $nh, $w, $h);
+ imageinterlace($im2,true); // For progressive JPEG.
+ $tempname=$filepath.'_TEMP.jpg';
+ imagejpeg($im2, $tempname, 90);
+ imagedestroy($im);
+ imagedestroy($im2);
+ rename($tempname,$filepath); // Overwrite original picture with thumbnail.
+ return true;
+}
+
// Invalidate caches when the database is changed or the user logs out.
// (eg. tags cache).
function invalidateCaches()