preparations for rewrite

This commit is contained in:
2025-01-11 23:14:09 +01:00
parent 637a781f24
commit cf07363a8d
39 changed files with 105 additions and 66 deletions

3
src/config.inc.php Normal file
View File

@@ -0,0 +1,3 @@
<?php
define('URL','http://localhost:8081');

16
src/helpers.php Normal file
View File

@@ -0,0 +1,16 @@
<?php
function renderTemplate($templatename,$variables=[],$basepath=ROOT.'/src')
{
ob_start();
if(is_array($variables))
extract($variables);
if(file_exists($basepath.DS.'templates'.DS.$templatename.'.php'))
include($basepath.DS.'templates'.DS.$templatename.'.php');
else if(file_exists($basepath.DS.'templates'.DS.$templatename))
include($basepath.DS.'templates'.DS.$templatename);
$rendered = ob_get_contents();
ob_end_clean();
return $rendered;
}

339
src/http2pic.class.php Normal file
View File

@@ -0,0 +1,339 @@
<?php
/**
* http2pic by Christian Haschek (https://haschek.solutions)
*
* For more info and up2date version of this file visit https://github.com/chrisiaut/http2pic
* -------------
*
* @category Website rendering API
* @author Christian Haschek <christian@haschek.at>
* @copyright 2015 by HASCHEK SOLUTIONS
* @link https://http2pic.haschek.at
*/
//
// Stuff you can edit
//
// if true, will save all cmd queries
define('DEBUG',false);
define('MAXTIMEOUT',30);
define('ONFAILIMAGE', __DIR__.'/img/pagefailed.jpg');
define('ONDOMAINFAILIMAGE', __DIR__.'/img/domainfailed.jpg');
//rendering engine: wkhtmltoimage or phantomjs
define('RENDERINGENGINE','wkhtmltoimage');
//location of wkhtmltoimage
define('WKHTMLTOIMAGEPATH','/usr/sbin/wkhtmltoimage');
//location of phantomJS
define('PHANTOMJSPATH',__DIR__.'/phantomjs');
//where shoud we store cached images
define('CACHEDIR',ROOT.DS.'cache'.DS);
//
// Only edit from here if you know what you are doing
//
class http2pic
{
private $params = array();
function __construct($params)
{
//try to create the cache folder if not exists
if (!is_dir(CACHEDIR)) {
mkdir(CACHEDIR);
}
$this->params = $params;
return $this->paramsPrepare();
}
/**
* Prepare and validate params
**/
function paramsPrepare()
{
//validate file type of rendered image
switch($this->params['type'])
{
case 'png': $this->params['type'] = 'png'; break;
case 'jpg': $this->params['type'] = 'jpg'; break;
default: $this->params['type'] = 'png';
}
//validate timeout
if (!$this->params['timeout'] || !is_numeric($this->params['timeout']) || ($this->params['timeout'] > MAXTIMEOUT || $this->params['timeout'] < 1))
$this->params['timeout'] = 10;
//validate viewport
if ($this->params['viewport'])
{
$a = explode('x', $this->params['viewport']);
$w = $a[0];
$h = $a[1];
if (is_numeric($w))
$this->params['vp_w'] = $w;
if (is_numeric($h))
$this->params['vp_h'] = $h;
}
//validate resize width
if($this->params['resizewidth'])
{
if(!is_numeric($this->params['resizewidth']) || $this->params['resizewidth']<1 || $this->params['resizewidth']>8000)
unset($this->params['resizewidth']);
}
if(!$this->params['onfail'])
$this->params['onfail'] = ONFAILIMAGE;
else
$this->params['onfail'] = rawurldecode($this->params['onfail']);
if(!$this->params['ondomainfail'])
$this->params['ondomainfail'] = ONDOMAINFAILIMAGE;
else
$this->params['ondomainfail'] = rawurldecode($this->params['ondomainfail']);
//validate URL and check if exists
if ($this->isBase64($this->params['url']))
$this->params['url'] = base64_decode($url);
else
$this->params['url'] = rawurldecode($_GET['url']);
//if the url is not valid or not responding, show onfail image and leave
if(!$this->isURLValid($this->params['url']) || !(($reachableResult = $this->isURLReachable($this->params['url'])) == 0))
{
header('Content-Type: image/jpeg');
header('Content-Disposition: inline; filename="http2png.jpg"');
switch ($reachableResult) {
case 1:
header('HTTP/1.0 404 File Not Found');
$result = imagecreatefromjpeg($this->params['onfail']);
break;
case 2:
header('HTTP/1.0 404 Server Not Found');
$result = imagecreatefromjpeg($this->params['ondomainfail']);
break;
}
imagejpeg($result, NULL, 100);
return false;
}
//prepare file name
$this->params['cache'] = $this->trimToAlphaNumeric($this->params['cache']);
$hash = $this->params['cache'].'-'.preg_replace("/[^A-Za-z0-9 ]/", '', $this->params['url']).'.'.$this->params['type'];
if (!$this->params['cache'])
$hash = md5(time().rand(1,2000)).$hash;
$this->params['file'] = CACHEDIR.$hash;
$this->render();
return true;
}
function render()
{
//if phantomjs is selected and installed
if(RENDERINGENGINE=='phantomjs' && file_exists(PHANTOMJSPATH))
return $this->renderPagePHANTOMJS();
//no? well ok how about WKHTMLToImage?
else if(RENDERINGENGINE=='wkhtmltoimage' && file_exists(WKHTMLTOIMAGEPATH))
return $this->renderPageWKHTMLTOIMAGE();
//you're fucked
else
throw new Exception('No valid rendering engine found');
}
/**
* Render using PhantomJS
**/
function renderPagePHANTOMJS()
{
$cmd = 'timeout '.$this->params['timeout'].' '.PHANTOMJSPATH;
$cmd.= ' --ignore-ssl-errors=yes --ssl-protocol=any '.__DIR__.'/phantom.js ';
$cmd.= ($this->params['url']);
$cmd.= ','.($this->params['file']);
$cmd.= ','.$this->params['vp_w'];
$cmd.= ','.$this->params['vp_h'];
$cmd.= ','.$this->params['js'];
$cmd = escapeshellcmd($cmd);
shell_exec($cmd);
$this->params['cmd'] = $cmd;
$this->postRender();
if(DEBUG)
{
$fp = fopen('debug.log', 'a');
fwrite($fp, $cmd."\n");
fclose($fp);
}
return $cmd;
}
/**
* Render using WKHTMLToImage
**/
function renderPageWKHTMLTOIMAGE()
{
//escapeshellarg
//timeout
$cmd = 'timeout '.$this->params['timeout'].' '.WKHTMLTOIMAGEPATH;
//viewport vp_w und vp_h
if($this->params['vp_w'])
$cmd.=' --width '.$this->params['vp_w'];
if($this->params['vp_h'])
$cmd.=' --height '.$this->params['vp_h'];
//js or no js
if($this->params['js']=='no')
$cmd.=' -n';
//png or jpg (default)
if($this->params['type']=='png')
$cmd.=' -f png';
//add url to cmd
$cmd.=' \''.addslashes($this->params['url']).'\'';
//add storage path to cmd
$cmd.=' '.escapeshellarg($this->params['file']);
$cmd = escapeshellcmd($cmd);
shell_exec($cmd);
$this->params['cmd'] = $cmd;
$this->postRender();
if(DEBUG)
{
$fp = fopen('debug.log', 'a');
fwrite($fp, $cmd."\n");
fclose($fp);
}
return $cmd;
}
/**
* Called after a render took place.
* This method will print the image to the user, then
* resizes or deletes it
*/
function postRender()
{
// resize if necessary
if($this->params['resizewidth'])
$this->resizeImage($this->params['file']);
//print image to user
if ($this->params['type'] === 'png') {
header('Content-Type: image/png');
header('Content-Disposition: inline; filename="'.$this->trimToAlphaNumeric($this->params['url']).'.png"');
$result = imagecreatefrompng($this->params['file']);
imagepng($result, NULL, 9);
}
else {
header('Content-Type: image/jpeg');
header('Content-Disposition: inline; filename="'.$this->trimToAlphaNumeric($this->params['url']).'.jpg"');
$result = imagecreatefromjpeg($this->params['file']);
imagejpeg($result, NULL, 100);
}
//if no cache value specified: delete the image
if(!$this->params['cache']) unlink($this->params['file']);
}
function resizeImage($file)
{
list($width_orig, $height_orig) = getimagesize($file);
if ($width_orig != $this->params['resizewidth'])
{
$ratio_orig = $width_orig/$height_orig;
$height = $this->params['resizewidth']/$ratio_orig;
// resample
$image_p = imagecreatetruecolor($this->params['resizewidth'], $height);
if ($this->params['type'] === 'png')
$image = imagecreatefrompng($file);
else
$image = imagecreatefromjpeg($file);
imagecopyresampled($image_p, $image, 0, 0, 0, 0, $this->params['resizewidth'], $height, $width_orig, $height_orig);
if ($this->params['type'] === 'png')
imagepng($image_p, $file, 9);
else
imagejpeg($image_p, $file, 100);
}
}
function isURLValid($url)
{
if(!$this->startsWith($url,'http://') && !$this->startsWith($url,'https://') && !$this->startsWith($url,'ftp://'))
return false;
return filter_var($url, FILTER_VALIDATE_URL);
}
function startsWith($haystack,$needle)
{
$length = strlen($needle);
return (substr($haystack,0,$length) === $needle);
}
/**
* https://stackoverflow.com/questions/7684771/how-check-if-file-exists-from-the-url
*/
function isURLReachable($url)
{
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
if(curl_exec($ch) != false){
//We were able to connect to a webserver, what did it return?
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($code < 400) //status code updated so redirects will also work
$status = 0;
else
$status = 1;
curl_close($ch);
return $status;
} else {
//We were not able to connect to any webserver so we didn't get a status code
//to compare against. There must be a problem with the domain that was supplied.
curl_close($ch);
return 2;
}
}
function trimToAlphaNumeric($string)
{
return preg_replace("/[^A-Za-z0-9 ]/", '', $string);
}
function isBase64($data)
{
if (base64_encode(base64_decode($data, true)) === $data)
return true;
return false;
}
}

View File

@@ -0,0 +1,206 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="Christian Haschek">
<!-- Open Graph data -->
<meta property="og:title" content="HTTP2PIC | Website screenshot API" />
<meta property="og:type" content="article" />
<meta property="og:url" content="<?=URL?>" />
<meta property="og:image" content="<?=URL?>/api.php?url=<?php echo rawurlencode(URL); ?>&cache=1&viewport=1200x630"/>
<meta property="og:description" content="An open source website renderer" />
<meta property="og:site_name" content="http2pic" />
<meta property="article:published_time" content="2015-09-28 00:14:58" />
<meta property="article:modified_time" content="2015-09-28 22:08:16" />
<meta property="article:tag" content="selfhosted,opensource,screenshot" />
<title>http2pic</title>
<!-- Bootstrap Core CSS -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!-- Custom CSS -->
<link href="css/clean-blog.min.css" rel="stylesheet">
<link href="css/http2pic.css" rel="stylesheet">
<!-- Custom Fonts -->
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet" type="text/css">
<link href='//fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
<link href='//fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'>
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="//oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<a href="https://github.com/chrisiaut/http2pic"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/365986a132ccd6a44c23a9169022c0b5c890c387/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f7265645f6161303030302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png"></a>
<!-- Page Header -->
<!-- Set your background image for this header on the line below. -->
<header id="intro-header" class="intro-header" style="background-image: url('img/home-bg.jpg')">
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
<div class="site-heading">
<h1 style="text-shadow: 0px 0px 12px #000000;">http2pic</h1>
<h2 id="loading"><img src="img/loading.gif" /><br/>Loading..</h2>
<hr class="small">
<span style="text-shadow: 0px 0px 12px #000000;" class="subheading">Give it a try! <input id="showcase_url" type="url" placeholder="eg. http://xkcd.com" />
<input id="showcase_button" type="button" value="GO" /></div>
</div>
</div>
</div>
</div>
</header>
<img id="preloader" src="" />
<!-- Main Content -->
<div class="container">
<h2>How the API works</h2>
<div class="well"><h2 ><?=URL?>/api.php?<span style="color:#C73C49">[OPTIONS]</span>&amp;url=<span style="color:#1e90ff">[WEBSITE_URL]</span></h2></div><hr/><br/>
<div >
<div>
<section>
<h2>Options</h2>
<table class="table">
<tbody>
<tr>
<th>Option</th>
<th>Values</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td>url</td>
<td>http://..</td>
<td>The URL of the webpage you'd like to take a screenshot of. Make sure to encode the URL!</td>
<td>url=http://xkcd.com</td>
</tr>
<tr>
<td>width</td>
<td>WIDTH</td>
<td>Resizes the screenshot to a specified maximum width. Default value is the original size</td>
<td>width=400</td>
</tr>
<tr>
<td>viewport</td>
<td>WIDTHxHEIGHT</td>
<td>Sets the size of the virtual screen rendering the page. Default: smart width, full height</td>
<td>viewport=1980x1080</td>
</tr>
<tr>
<td>js</td>
<td>yes|no</td>
<td>Allows you to enable/disable JavaScript in the rendered Website. Default value: yes</td>
<td>js=yes</td>
</tr>
<tr>
<td>type</td>
<td>jpg|png</td>
<td>Sets the output file format of the rendered screenshot. Default value: jpg</td>
<td>type=png</td>
</tr>
<tr>
<td>onfail</td>
<td>[url of .jpg]</td>
<td>If the page can't be reached, this image will be displayed instead</td>
<td><?=URL?>/img/pagefailed.jpg</td>
</tr>
<tr>
<td>ondomainfail</td>
<td>[url of .jpg]</td>
<td>If the web server can't be reached, this image will be displayed instead</td>
<td><?=URL?>/img/domainfailed.jpg</td>
</tr>
<tr>
<td>cache</td>
<td>[any alphanumeric string]</td>
<td>If provided, caches the rendered image (based on the URL) so it loads faster on next request. The same cache id with the same url will return the cached image. Change cache id to re-render</td>
<td>f01d0</td>
</tr>
</tbody>
</table>
</section>
</div>
<div class="6u">
<section>
<h1>Examples</h1>
<h3>Simple link via img tag</h3>
<p class="margin-bottom"></p>
<p>
<pre><code class="php">
&lt;?php
$url = 'http://www.xkcd.com';
$query = 'type=jpg&viewport=1200x330&url='.rawurlencode($url);
$img="<?=URL?>/api.php?$query";
echo "&lt;img src='$img' /&gt;";
?&gt;
</code></pre>
</p>
<h3>Proxy script to download the image via curl</h3>
<p class="margin-bottom"></p>
<p>
<pre><code class="php">
&lt;?php
$targeturl = 'http://www.xkcd.com';
$url = '<?=URL?>/api.php?url='.rawurlencode($targeturl);
$ch = curl_init($url);
$fp = fopen('xkcd.jpg', 'wb');
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
curl_close($ch);
fclose($fp);
?&gt;
</code></pre>
</p>
</section>
</div>
</div>
</div>
<hr>
<!-- Footer -->
<footer>
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
<p class="copyright text-muted">Copyright &copy; Haschek Solutions <br/><a href="https://www.haschek-solutions.com"><img src="img/hs_logo.png" /></a></p>
</div>
</div>
</div>
</footer>
<!-- jQuery -->
<script src="js/jquery.js"></script>
<!-- Bootstrap Core JavaScript -->
<script src="js/bootstrap.min.js"></script>
<!-- Custom Theme JavaScript -->
<script src="js/clean-blog.min.js"></script>
<script src="js/http2pic.js"></script>
</body>
</html>