html2canvas custom proxy in php - php

This is my first question on here so be nice....
I am trying to use html2canvas to ultimately get a screenshot of a remote site (user submitted url).
The problem is the cross domain security features. I cannot read from the canvas as it's locked by off site assets.
The solution is to use the proxy feature built into the library.
There are some github projects with python and node.js versions, but I'm needing to do it in php.
There are many topics on HOW to implement the feature and how to get it working, but none really explain how to make your own proxy.
My question is two fold, are there any existing solutions in PHP? and if not, I have a few questions on making my own:
1.) What is the output format of the proxy? json object? the rendered image? the base64 encoded data string?
2.) Do these files need to persist on the server or can they just be rendered then disappear (overwritten)?
This is roughly what i'm thinking:
$img_url = urldecode($_GET['url']);
$img_data = base64_encode(file_get_contents($img_url));
//shouldn't need it since it's not cross domain now, but a CORS header could be inserted
header('content-type: application/json; charset=utf-8');
json_encode("{$_GET['callback']}($img_data)");

I've found the answers to my question.
The proxy feature accepts a jsonp element with the url to the proxied image.
And they do need to be saved on the server while
This is raw, I will update it later, but here is a working PHP proxy script for html2canvas
session_start();
//parse the url sent by the proxy function
//TODO: scrub the input
$img_url = urldecode($_GET['url']);
//test file type
//TODO: test for other cases that don't have a '.'
$pos = strrpos($img_url, '.', -1);
$ext = substr($img_url, $pos);
//set a dir for this request
function randomNumber()
{
return substr(sha1(rand()), 0, 15);
}
if (!isset($_COOKIE["img_path"]))
{
do{
$random = randomNumber();
}while (is_dir('images/' . $random));
setcookie("img_path", $random, time()+3600);
} else {
$random = $_COOKIE["img_path"];
}
is_dir('images/' . $random) ? '' : mkdir('images/' . $random, 0755);
//TODO:catch cases where a filename isn't the last element
$basename = basename($img_url);
$file = 'images/' . $random . '/' . $basename;
//save the image
copy($img_url, $file);
//TODO: don't hardcode the url
$test_location = "http://osc.test/html2canvas2/" . $file;
header('Content-Type: application/javascript');
echo "{$_GET['callback']}(" . json_encode($test_location) . ")";

In case anyone else is looking for a simple PHP proxy, here is a link to a nice one by "Cowboy" Ben Alman:
Simple PHP proxy

Related

open file on client stored on server

I want to open a server stored html report file on a client machine.
I want to bring back a list of all the saved reports in that folder (scandir).
This way the user can click on any of the crated reports to open them.
So id you click on a report to open it, you will need the location where the report can be opend from
This is my dilemma. Im not sure how to get a decent ip, port and folder location that the client can understand
Here bellow is what Ive been experimenting with.
Using this wont work obviously:
$path = $_SERVER['DOCUMENT_ROOT']."/reports/saved_reports/";
So I though I might try this instead.
$host= gethostname();
$ip = gethostbyname($host);
$ip = $ip.':'.$_SERVER['SERVER_PORT'];
$path = $ip."/reports/saved_reports/";
$files = scandir($path);
after the above code I loop through each file and generate a array with the name, date created and path. This is sent back to generate a list of reports in a table that the user can interact with. ( open, delete, edit)
But this fails aswell.
So im officially clueless on how to approach this.
PS. Im adding react.js as a tag, because that is my front-end and might be useful to know.
Your question may be partially answered here: https://stackoverflow.com/a/11970479/2781096
Get the file names from the specified path and hit curl or get_text() function again to save the files.
function get_text($filename) {
$fp_load = fopen("$filename", "rb");
if ( $fp_load ) {
while ( !feof($fp_load) ) {
$content .= fgets($fp_load, 8192);
}
fclose($fp_load);
return $content;
}
}
$matches = array();
// This will give you names of all the files available on the specified path.
preg_match_all("/(a href\=\")([^\?\"]*)(\")/i", get_text($ip."/reports/saved_reports/"), $matches);
foreach($matches[2] as $match) {
echo $match . '<br>';
// Again hit a cURL to download each of the reports.
}
Get list of reports:
<?php
$path = $_SERVER['DOCUMENT_ROOT']."/reports/saved_reports/";
$files = scandir($path);
foreach($files as $file){
if($file !== '.' && $file != '..'){
echo "<a href='show-report.php?name=".$file. "'>$file</a><br/>";
}
}
?>
and write second php file for showing html reports, which receives file name as GET param and echoes content of given html report.
show-report.php
<?php
$path = $_SERVER['DOCUMENT_ROOT']."/reports/saved_reports/";
if(isset($_GET['name'])){
$name = $_GET['name'];
echo file_get_contents($path.$name);
}

file_get_contents doesn't update the data

I'm trying to fetch gamerscore data from gamercard.xbox.com with my little script:
test.php
<?php
error_reporting(E_ALL); ini_set('display_errors', 1);
$regex = '/<div id=\"Gamerscore\">(.+?)<\/div>/';
$gamertag = 'Stallion83';
try {
$URL = file_get_contents('http://gamercard.xbox.com/en-US/' . $gamertag . '.card');
if ($URL == false) {
echo 'Error!';
}
} catch (Exception $e) {
echo $e;
}
preg_match($regex, $URL, $gs);
// Extract integer value from string
$gamerscore = filter_var($gs[1], FILTER_SANITIZE_NUMBER_INT);
// Force gs_int to be integer so it can be used with number_format later
$gs_int = (int)$gamerscore;
$textFile = 'data/gamerscore_' . $gamertag . '.txt';
// Save gamerscore value into everyone's own txt file
file_put_contents($textFile, $gs_int);
?>
Now this works and it creates a .txt file in the data folder which has only the gamerscore number inside. But my problem is if I run the script again after the gamerscore value has increased the script doesn't give me any errors and it seems to execute just fine but the gamerscore value it saves into the .txt file is the old value.
I can go to the URL http://gamercard.xbox.com/en-US/Stallion83.card and see the number is different than my script shows.
I thought it might be a caching issue but I think file_get_contents doesn't use caching.
Is there anything else I could set for file_get_contents to force it to get the most recent content of the URL specified? I tried using timeout but it didn't make any difference.
This is most likelly caused by cache. In this case, the server seems to be returning a cached version of the page.
Often, adding a random value to the URL can be a workaround, such as ?foo.
So, in your case, something like:
[...] . $gamertag . 'card?' . mt_rand());

FileDrop.js & PHP resulting in empty $_FILES

JSFIDDLE
I'm using filedrop.js to create a file repository structure within my app. The above noted JSFIDDLE has all of the Javascript / jQuery / HTML and CSS code for this small module. While everything on the client end seems to be functioning properly (files can be DnD'd, progress bar acts correctly, console shows proper event triggers), the result on the server-side is always an empty $_FILES variable. My PHP (ajax.receiveFile.php) is as follows:
var_dump($_FILES);
ob_start();
$callback = &$_REQUEST['fd-callback'];
$job_id = &$_REQUEST['job_id'];
$subdir = &$_REQUEST['subdir'];
$j = loadJob($job_id);
$save_path = "D:\\JobFiles\\" . $j->gOrderNumber() . "\\" . $subdir . "\\";
if ( ($_FILES['fd-file']['size'] > 0) && is_uploaded_file($_FILES['fd-file']['tmp_name']) ) {
$name = $_FILES['fd-file']['name'];
if (move_uploaded_file($_FILES['fd-file']['tmp_name'], $save_path.$name)) {
$j->addAttachment($subdir,$name);
echo 'true';
} else {
echo 'false';
}
}
ob_end_flush();
FileDrop.js seems to be doing what it is supposed to do, as shown here:
I read here on SO that using the same element name over multiple input types of "file" can cause errors but I'm not sure that is the case here. I have double- and triple-checked the permissions on both the TEMP and TARGET upload folders, I have confirmed that all PHP variables are set as needed via visual inspection and PHPINFO(). The server config is PHP 5.4 on IIS7.
If anyone has any ideas on what else to look for, please contribute. Thanks!
This works for me:
file_put_contents('uploads/person/7.jpeg', fopen('php://input', 'r'));

How to debug a PHP file that is being called by AJAX?

I have a php file for uploading a picture (it uploads the picture, creates its thumbnail and also adds watermark to the picture). The file is being called by AJAX.
It was working on on my localhost on Windows XP with WAMPSERVER 2.0. Now I have installed Windows Vista and suddenly it doesn't work properly (the picture gets uploaded but the thumbnail and watermark parts don't work).
I'm using exactly the same software to test the application on my local machine (WAMPSERVER 2.0) yet it doesn't work.
How to debug this file? Here's how it looks:
<?php
define('BASE_PATH', substr(dirname(dirname(__FILE__)), 0, -22));
// set the include path
set_include_path(BASE_PATH
. '/../../library'
. PATH_SEPARATOR
. BASE_PATH
. '/library'
. PATH_SEPARATOR
. get_include_path());
// autoload classes from the library
function __autoload($class) {
include str_replace('_', '/', $class) . '.php';
}
$configuration = new Zend_Config_Ini(BASE_PATH
. '/application'
. '/configs/application.ini',
'development');
$dbAdapter = Zend_Db::factory($configuration->database);
Zend_Db_Table_Abstract::setDefaultAdapter($dbAdapter);
function _getTable($table)
{
include BASE_PATH
. '/application/modules/default/models/'
. $table
. '.php';
return new $table();
}
$albums = _getTable('Albums');
$media = _getTable('Media');
if (false === empty($_FILES)) {
$tempFile = $_FILES['Filedata']['tmp_name'];
$extension = end(explode('.', $_FILES['Filedata']['name']));
// insert temporary row into the database
$data = array();
$data['type'] = 'photo';
$data['type2'] = 'public';
$data['status'] = 'temporary';
$data['user_id'] = $_REQUEST['user_id'];
$paths = $media->add($data, $extension, $dbAdapter);
// save the photo
move_uploaded_file($tempFile,
BASE_PATH . '/public/' . $paths[0]);
// create a thumbnail
include BASE_PATH . '/library/My/PHPThumbnailer/ThumbLib.inc.php';
$thumb = PhpThumbFactory::create('/' . $paths[0]);
$thumb->adaptiveResize(85, 85);
$thumb->save(BASE_PATH . '/public/' . $paths[1]);
// add watermark to the bottom right corner
$pathToFullImage = BASE_PATH . '/public/' . $paths[0];
$size = getimagesize($pathToFullImage);
switch ($extension) {
case 'gif':
$im = imagecreatefromgif($pathToFullImage);
break;
case 'jpg':
$im = imagecreatefromjpeg($pathToFullImage);
break;
case 'png':
$im = imagecreatefrompng($pathToFullImage);
break;
}
if (false !== $im) {
$white = imagecolorallocate($im, 255, 255, 255);
$font = BASE_PATH . '/public/fonts/arial.ttf';
imagefttext($im,
13, // font size
0, // angle
$size[0] - 132, // x axis (top left is [0, 0])
$size[1] - 13, // y axis
$white,
$font,
'HunnyHive.com');
switch ($extension) {
case 'gif':
imagegif($im, $pathToFullImage);
break;
case 'jpg':
imagejpeg($im, $pathToFullImage, 100);
break;
case 'png':
imagepng($im, $pathToFullImage, 0);
break;
}
imagedestroy($im);
}
echo "1";
}
For generally debugging this, you should break the steps down into functions and test each part in turn.
With regard to Ajax debugging, you might like to try out Zend_Log_Writer_FirePHP. It's an extension for Firebug which reads extra data in headers sent by PHP, which means that the data in your response body doesn't contain debug output, and for things like images, can still be rendered.
Once you've installed FirePHP for Firebug, you can just write the following few lines:
$wFirebug = new Zend_Log_Writer_Firebug();
$firebug = new Zend_Log($wFirebug);
$firebug->info($myArray);
$firebug->info('Got to line 10');
When debugging anything that is related to AJAX I would recommend the following:
Ensure that the file is returning the correct data without any AJAX wrapper around it. Call the file directly with some sample data. Does it return what you require? Does it have syntax errors? This is the first thing you want to check and it will save you a ton of headache.
Ensure your Javascript is parsing the data correctly. Your JS may be expecting JSON but you're returning XML, or your returned data is not formatted the way you think, or you may need to evaluate your returned data in Javascript so it can actually be used. Try some console.log()'s to test what your data looks like.
Try something like Postbin which lets you send POST data. Ensure your POST data is correct and you're sending the right data to your script.
You could just create a simple form with a file input and just use that for easy testing.
That is the simplest way that I see, someone else may have something better in mind.
I find that when working with an action being called via AJAX in ZF, it's always a good idea to make sure that the action works without AJAX first. That is, make your first iteration of developing the feature result in going to a new page to represent the action you're making.
Once you know that your PHP code works, you can then begin to worry about making the AJAX stuff work. In my opinion, at least, being able to output Zend_Debug::dump() on variables when you view another page is a lot easier for initial development.
AJAX by design creates a layer of opacity which can makes it difficult to do this. It gets even harder if you're interfacing with, for instance, a jQuery plugin that requires formatted data that you're just not getting for some reason. So again, PHP first, AJAX second. It takes roughly two seconds in ZF to go from a PHP to an AJAX call, and vice versa.
The easiest solution would be to use FirePHP; install firebug + firephp addon for firefox and include the classes in your project.
(I keep the FirePHP library in /usr/share/php/ so I can include it easily in any project)
Then just do this:
require_once('/path/to/FirePHPCore/FirePHP.class.php');
$fp = FirePHP::getInstance(true);
$fp->log('you can put anything here, vars, objects, arrays, etc');
It will output the response in the FireBug console and is much better than polluting your code with echos and var_dumps when debugging ajax!
Make the AJAX request with a callback function, which checks the data returned (echo'd) from the PHP function. If the data echo'd is some pre-determined success string ("success"?) then all is well, if it's not that, have the callback function output whatever is output by the function in an alert or something.

Why do images served from my web server not cache on the client?

I store all of my images behind the webroot (before /var/www/), which means that the web server is unable to send cache headers back for my pictures. What do I need to add to this to make the user's web cache work? Currently, this is getting hit every time by the same browser.
My <img> path on my pages look something like this:
<img src="pic.php?u=1134&i=13513&s=0">
Edit: Could it be that it is because "pic.php?u=1134&i=13513&s=0" is not a valid file name or something?
// pic.php
<?php
// open the file in a binary mode
$user = $_GET['u'];
$id = $_GET['i'];
$s = $_GET['s'];
if (!isset($user) && !isset($s) && $isset($id))
{
// display a lock!
exit(0);
}
require_once("bootstrap_minimal.php"); //setup db connection, etc
// does this image_id belong to the user?
$stmt = $db->query('SELECT image_id, user_id, file_name, private FROM images WHERE image_id = ?', $id);
$obj = $stmt->fetchObject();
if (is_object($obj))
{
// is the picture is the users?
if ($obj->user_id != $_SESSION['user_id'])
{
// is this a private picture?
if ($obj->private == 1)
{
// check permissions...
// display a lock in needed!
}
}
}
else
{
// display a error pic?!
exit(0);
}
if ($s == 0)
{
$picture = $common->getImagePathThumb($obj->file_name);
}
else
{
$picture = $common->getImagePath($obj->file_name);
}
// send the right headers
header("Content-Type: image/png");
header("Content-Length: " . filesize($picture));
$fp = fopen($picture, 'rb');
// dump the picture and stop the script
fpassthru($fp);
exit;
?>
You need to add something like:
$expiry = 3600*24*7; // A week
header('Expires: ' . gmdate('D, d M Y H:i:s' time() + $expiry) . ' GMT');
header('Cache-control: private, max-age=' . $expiry);
Apache only caches static files by default. You need to send a cache control header via the header() function. This article has a lot of information on the topic.
Alternatively, you could use the PHP file to redirect to the actual location of the image. (This is probably the easiest way if you don't know anything about headers.)
You might try:
header("Cache-Control: max-age=3600");
That should send a cache timeout of one hour on the file.
What I would do in your situation is to stream the bytes of the image using a .php file. Don't link to images directly; instead, link to a php file that:
- outputs the cache headers
- reads the file off of disk, from behind the webroot
- sends the image bits down the wire
Simple answer: you aren't telling your users' browser to cache it

Categories