How to access files in data folder in zend framework? - php

currently i am working on zf2. Right now i have to give download option to download pdf files.i have stored all the pdf files in data directory.How can i specify link to that pdf files from .phtml file?
thanks in advance.

A user will never gain direct access to your /data directory. This would be just not that good. But you can easily write yourself a download-script.php or the likes that will hand out the content of this directory to your users.
If you take a look at the first six lines of public/index.php you'll see the following:
<?php
/**
* This makes our life easier when dealing with paths. Everything is relative
* to the application root now.
*/
chdir(dirname(__DIR__));
With this in mind, you know that from PHP's side of things the access to anything inside the data directory is as simple as data/file.pdf
You'd always want to write yourself some sort of download-logger. Write yourself a controller. Have an action inside of that controller probably called something like download or anything like that. That action should have one parameter filename.
All that this action does is to check if filename exists file_exists('data/'.$filename) and if it exists, you simply deliver this file to your users. An example mix or zf2 and native php could be:
public function downloadAction()
{
$filename = str_replace('..', '', $this->params('filename'));
$file = 'data/' . $filename;
if (false === file_exists($file)) {
return $this->redirect('routename-file-does-not-exist');
}
$filetype = finfo_file($file);
header("Content-Type: {$filetype}");
header("Content-Disposition: attachment; filename=\"{$filename}\"");
readfile($file);
// Do some DB or File-Increment on filename download counter
exit();
}
This is not clean ZF2 but i'm lazy right now. It may be much much more ideal to use a proper Response Object and do the File-Handling there!
Important Update this thing was actually quite insecure, too. You need to disallow parent-folders. You wouldn't wanna have this guy do something outside of the data/download directory like
`http://domain.com/download/../config/autoload/db.local.php`
If I'm not totally mistaken, simply replacing all occurences of double-dots should be enough...

I would create a symbolic link in public directory for PDF files in data folder.
For example:
ln -s /your/project/data/pdfdir /your/project/public/pdf
and create links something like
File.pdf

Borrowing Sam's code, here's what it looks like in ZF2 syntax.
public function downloadAction()
{
$filename = str_replace('..', '', $this->params('filename'));
$file = 'data/' . $filename;
if (false === file_exists($file)) {
return $this->redirect('routename-file-does-not-exist');
}
$filetype = finfo_file($file);
$response = new \Zend\Http\Response\Stream();
$response->getHeaders()->addHeaders(array(
'Content-Type' => $filetype,
'Content-Disposition' => "attachement; filename=\"$filename\""
));
$response->setStream(fopen($wpFilePath, 'r'));
return $response;
}

Related

Laravel 6 Storage results in a 404 error when trying to fetch files

I have tried to setup an upload script in Laravel and have followed the instructions in the docs.
I created a Symlink using the Laravel script and it looks like the following
storage -> /Users/username/Sites/switch/storage/app/public
The problem arrives when I go to upload the image and then get result of the image url in return. As you can see to match the symlink I set the folder to be public below.
$path = $request->file('manufacturer_image_name')->store('public');
echo asset($path);
and this returns
http://127.0.0.1:8000/public/XxIX7L75cLZ7cf2xzejc3E6STrcjfeeu3AQcSKz1.png
the problem is this doesn't work and throws a 404 but if I manually change the url from "public" to "storage" it will find the image.
http://127.0.0.1:8000/storage/XxIX7L75cLZ7cf2xzejc3E6STrcjfeeu3AQcSKz1.png
Shouldn't
echo asset($path);
be returning a url containing storage instead of public?
assett($path) is for generating a URL for assets that are just in the public folder, things like the Mix generated CSS and JS files. If you user Laravel Storage to save the file, you also have to use Laravel storage to generate the file URL.
Storage::url('file.jpg');
Well, there are a lot of ways to do that, pick anyone which fits you best.
// using storage_path helper
storage_path('public/' . $filename);
// you could make a double-check with File::exist() method
$path = storage_path('public/' . $filename);
if (!File::exists($path)) {
abort(404);
}
// using asset helper
asset('storage/your_folder/image.png');
// using url helper
url('storage/your_folder/image.png');
// using Storage facade
Storage::url($photoLink)
Here is the simplest and exact thing for your issue
if(!empty($request->file('manufacturer_image_name'))){
$path = storage_path('public/image/');
$image_path = Storage::disk('public')->put('manufacturer_image_name', $request->file('manufacturer_image_name'));
//Assuming you have a model called Manufacturer and created $manufacturer = new Manufacturer()
$manufacturer->manufacturer_image_name = isset($image_path) ? "storage/".$image_path : "";
}
Thanks for the help, I discovered this answer the fits nearly perfectly what I am after. Laravel: Storage not putting file inside public folder
This was what I ended up with.
if($request->file('manufacturer_image_name')){
$path = Storage::disk('public')->put('logo', $request->file('manufacturer_image_name'));
echo $path;
}
$path now returns "logo/filename.ext" instead of "public/ or storage/" so I can store this directly in the db.

What is the best way to get the parameters in PHP?

I will completely clarify my question, sorry to everybody.
I have code writed in files from a website that now is not working, the html code is on pages with php extension, in a folder of a Virtual Host in my PC using Wampserever. C:\wamp\1VH\PRU1, when the site was online there was a folder where was a file called image.php. This file was called from other pages inside the site like this: (a little code of a file, C:\wamp\1VH\PRU1\example.php)
"<div><img src="https://www.example.com/img/image.php?f=images&folder=foods&type=salads&desc=green&dim=50&id=23" alt="green salad 23"></div>"
And the result was that the images was showed correctly.
Now, like i have this proyect in local, and i HAVE NOT the code of that image.php file i must to write it myself, this way the images will be showed the same way that when the site was online.
When the site was online and i open a image returned by that image.php file the URL was, following the example, https://example.com/images/foods/salads/green_50/23.png.
Now how the site is only local and i have not that image.php file writed because i'm bot sure how to write it, the images obviously are not showed.
In C:\wamp\1VH\PRU1\example.php the code of files was changed deleting "https://www.example.com/img/image.php?" for a local path "img/image.php?".
And in the same folder there is anothers: "img" folder (here must be allocated the image.php file), and "images" folder, inside it /foods/salads/green_50/23.png, 24.png.25.png..............
So i have exactly the same folder architecture that the online site and i changed the code that i could only, for example replacing with Jquery "https://www.example.com/img/image.php?" for "img/image.php?" but wich i can not do is replace all the code after the image.php file to obtain a image file.
So i think that the easiest way to can obtain the images normally is creating that IMAGE.PHP file that i have not here in my virtual host.
I'd like to know how to obtain the parameters and return the correct URL in the image,php file.
The image of the DIV EXAMPLE must be C:/wamp/1VH/PRU1/images/foods/salads/green_50/23.png
I have in my PC the correct folders and the images, i only need to write the image.php file.
Note that there are "&" and i must to unite the values of "desc=green&dim=50&" being the result: green_50 (a folder in my PC).
TVM.
You probably want something like this.
image.php
$id = intval($_GET['id']);
echo '<div><img src="images/foods/salads/green_50/'.$id.'.png" alt="green salad '.$id.'"></div>';
Then you would call this page
www.example.com/image.php?id=23
So you can see here in the url we have id=23 in the query part of the url. And we access this in PHP using $_GET['id']. Pretty simple. In this case it equals 23 if it was id=52 it would be that number instead.
Now the intval part is very important for security reasons you should never put user input directly into file paths. I won't get into the details of Directory Transversal attacks. But if you just allow anything in there that's what you would be vulnerable to. It's often overlooked, so you wouldn't be the first.
https://en.wikipedia.org/wiki/Directory_traversal_attack
Now granted the Server should have user permissions setup properly, but I say why gamble when we can be safe with 1 line of code.
This should get you started. For the rest of them I would setup a white list like this:
For
folder=foods
You would make an array with the permissible values,
$allowedFolders = [
'food',
'clothes'
'kids'
];
etc...
Then you would check it like this
///set a default
$folder = '';
if(!empty($_GET['folder'])){
if(in_array($_GET['folder'], $allowedFolders)){
$folder = $_GET['folder'].'/';
}else{
throw new Exception('Invalid value for "folder"');
}
}
etc...
Then at the end you would stitch all the "cleaned" values together. As I said before a lot of people simply neglect this and just put the stuff right in the path. But, it's not the right way to do it.
Anyway hope that helps.
You essentially just need to parse the $_GET parameters, then do a few checks that the file is found, a real image and then just serve the file by setting the appropriate content type header and then outputting the files contents.
This should do the trick:
<?php
// define expected GET parameters
$params = ['f', 'folder', 'type', 'desc', 'dim', 'id'];
// loop over parameters in order to build path: /imagenes/foods/salads/green_50/23.png
$path = null;
foreach ($params as $key => $param) {
if (isset($_GET[$param])) {
$path .= ($param == 'dim' ? '_' : '/').basename($_GET[$param]);
unset($params[$key]);
}
}
$path .= '.png';
// check all params were passed
if (!empty($params)) {
die('Invalid request');
}
// check file exists
if (!file_exists($path)) {
die('File does not exist');
}
// check file is image
if (!getimagesize($path)) {
die('Invalid image');
}
// all good serve file
header("Content-Type: image/png");
header('Content-Length: '.filesize($path));
readfile($path);
https://3v4l.org/tTALQ
use $_GET[];
<?php
$yourParam = $_GET['param_name'];
?>
I can obtain the values of parameters in the image.php file tis way:
<?php
$f = $_GET['f'];
$folder = $_GET['folder'];
$type = $_GET['type'];
$desc = $_GET['desc'];
$dim = $_GET['dim'];
$id = $_GET['id'];
?>
But what must i do for the image:
C:/wamp/1VH/PRU1/images/foods/salads/green_50/23.png
can be showed correctly in the DIV with IMG SRC atribute?

is_file always returns false

The Problem
I'm having an issue with the PHP function is_file().
Some preliminaries: I'm developing on Ubuntu 12.04, 32-bit, using PHP 5.5.10 and Apache 2.4.9.
I'm currently rewriting some working code to convert it to a library in Laravel (completed with a Facade and a ServiceProvider). I'm doing this mainly to clean up some code I wrote when I was young and foolish (about 6 months ago) and to implement unit testing. The library I'm writing provides methods for taking a contract (of which there are two distinct types, with more to come) and finding the path to a PDF document (the scanned paper contract). My methods for finding the path work fine and the tests are all passing.
In my old code, I used to do this:
/**
* Get a scanned contract and return it to the client
*
* #param string $type
* The contract type. Must be either static::CONTRACT1 or static::CONTRACT2.
*
* #param string $contract_id
* The contract ID
*
* #return Response
*/
public static function get($type, $contract_id)
{
// get the file name
//
$results = static::getFileName($type, $contract_id);
// did we find a file? if not, throw a ScannedContractNotFoundException
//
if(!$results)
throw new \MyApplication\Exceptions\ScannedContractNotFoundException("No file found for $type contract $contract_id");
// get the path and file name
//
$path = $results['path'];
$name = $results['name'];
// get the full path
//
$file = $path.$name;
// get the file size
//
$contents = file_get_contents($file);
$fsize = strlen($contents);
// push the file to the client
//
header("Content-type: application/pdf");
header("Content-Disposition: inline; filename=\"".$name."\"");
header("Content-length: $fsize");
header("Cache-control: private");
echo $contents;
exit;
}
And it worked just fine.
Now I'm trying to rewrite it to get rid of the echo and move the code that actually does the work of sending the file to a controller. That code will look like this:
$x = \MyApplication\Models\Contract1::find($id);
$file = \ScannedContracts::getFileName($x);
$path = $file["path"].$file["name"];
return \Response::download($path, $file["name"]);
However, this code is throwing a FileNotFoundException. The code where the exception is being thrown looks like this:
public function __construct($path, $checkPath = true)
{
if ($checkPath && !is_file($path)) {
throw new FileNotFoundException($path);
}
...
Clearly the problem is with the if statement, and in particular the call to is_file().
I have written a little script to test this with a path which is known to be good and is_file() returns false.
When I copy a file to my "public" folder, it works fine.
In the documentation for the is_file() function there is a comment stating that the permissions for the parent folder must be +x. I've examined the permissions, and the folder is world-executable, as is the parent, and the grand-parent, and the great-grand-parent, etc.
There are two possible confounding factors: first, the files I'm working with are located on a CIFS/Samba share. I should mention that the paths in question are absolute paths to the mounted share.
The closest issue to this I've found on SO is PHP is_file returns false (incorrectly) for Windows share on Ubuntu, but that doesn't have a resolution. I've also searched for PHP bug reports, but there's nothing.
Second, some of the paths contain spaces. I've tried escaping them every way I can think of, but it doesn't help.
In the absence of a solution, I'll have to do it the old fashioned way, but I really wanted to do it using the functions Laravel provides.
Questions
Do I need to escape spaces in a path passed to is_file()?
Does anyone know of a fix or a workaround that doesn't a) require changing code in a 3rd party library, or b) require wholesale changes to permissions or other configuration on the CIFS/Samba server?
Thanks in advance!
I think you need to sanitize filenames when upload to directory
function sanitize_file_name( $str ) {
return preg_replace("/[^a-z0-9\.]/", "", strtolower($str));
}

Protect PDF docs from being directly accessed in URL

I want to protect a pdf file from being directly linked but instead have my logged in users be able to access it. I have a link which currently goes to a javascript function which posts a form:
$('nameofdoc').setProperty('value',doc);
document.getElementById('sendme').submit();
where sendme is the name of the form and nameof doc the index of the document I want to display.
This then goes to a php file:
$docpath = $holdingArray[0].$holdingArray[1];
$file = $holdingArray[0]; //file name
$filename = $holdingArray[1]; //path to the file]
header( 'Location:'.$docpath ) ;
header('Content-type: application/pdf');
header('Content-Disposition: attachment; filename="'.$filename . '"');
readfile($filename)
This all works fine it loads up the file and outputs the pdf. What I can't do is protect the directory from direct linking - ie www.mydomain.com/pathToPdf/pdfname.pdf
I've thought of using .htaccess to protect the directory but it's on a shared host so I'm not sure about the security and anyway when I've tried I can't get it to work.
Any help would be great since this is my fourth day of trying to fix this.
thanks
Update
I've had a lot of help thank you but I'm not quite there yet.
I've got an .htaccess file that now launches another php file when a pdf is requested from the directory:
RewriteEngine on
RewriteRule ^(.*).(pdf)$ fileopen.php
When the fileopen.php file lauches it fails to open the pdf
$path = $_SERVER['REQUEST_URI'];
$paths = explode('/', $path);
$lastIndex = count($paths) - 1;
$fileName = $paths[$lastIndex];
$file = basename($path);
$filepath = $path;
if (file_exists($file)) {
header( 'Location: http://www.mydomain.com'.$path ) ;
header("Content-type: application/pdf");
header("Content-Disposition: attachment; filename=".$file);
readfile($filepath);
}else{
echo "file not found using path ".$path." and file is ".$file;
}
The output is
file not found using path /documents/6/Doc1.pdf and file is Doc1.pdf
but the file does exist and is in that direcotry - any ideas??
OKAY I'm happy to report that Jaroslav really helped me sort out the issue. His method works well but it is tricky to get all the directory stuff lined up. In the end I spent a few hours playing about with combinations to get it working but the principle he gave works well. Thanks
The best way would be to protect that folder with htaccess, as you have mentioned. So you put all PDFs in pdf/ folder, and in the same pdf folder you out .htaccess file:
RewriteEngine on
RewriteRule .* your-php-script.php
Now no files can be accessed by url in this folder. Every request to every file in this folder will return what your-php-script.php script returns. In your-php-script.php you do something like this:
//Check if user has right to access the file. If no, show access denied and exit the script.
$path = $_SERVER['REQUEST_URI'];
$paths = explode('/', path);
$lastIndex = count($paths) - 1;
$fileName = $paths[$lastIndex]; // Maybe add some code to detect subfolder if you have them
// Check if that file exists, if no show some error message
// Output headers here
readfile($filename);
Now if user opens domain.com/pdf/nsa-secrets.pdf Apache will run your-php-script.php. Script will have variable $_SERVER['REQUEST_URI'] set to "domain.com/pdf/nsa-secrets.pdf". You take the last part (filename) and output it to a user (or not).
This will stop anyone from accessing files directly from the internet by knowing URL. If someone has direct access to files on your server, that will not stop them. On the other hand, I think any shared hosting stops users from getting files of other clients. Only way to do it is to hack the server in some way. But then we are getting very paranoid and if that may be a case for you, you shouldn't use shared hosting in the first place.
If you cannot make htaccess work, you can try to obfuscate files, so it would be difficult to spot them for someone outside. For example change file from mySecretData.pdf to djjsdmdkjeksm.pdf. This may help a little bit.
I want to protect a pdf file from being directly linked but instead have my logged in users be able to access it.
Check to ensure there is an authenticated user before streaming the PDF's content.
This is kinda sloppy but it could work assuming you can setup a MYSQL DB. It lets you pass the "password" in the URL as an MD5 string or as a clear text if you want to. Trying to setup some kind of security without using htaccess or an existing frame work is kinda clunky. This however won't even attach the file to the stream until it knows you've been "Authenticated" I think you could maybe make this a little better if you setup a login page that saved a cookie locally then you wouldn't need to pass the "passphrase" in the URL.
$file = $_GET['file'];
$pass = $_GET['pass'];
$download_folder = '../Protected';
$file = basename($file);
$filepath = "$download_folder/$file";
if (file_exists($filepath)) {
if(CheckUser($pass)){
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=$file");
session_write_close();
readfile($filepath);
} else {
echo 'Not Authenticated!';
}
} else {
echo 'No File!';
}
function CheckUser($value){
$con = mysqli_connect("test.com","test","123456","my_db");
// Check connection
if (mysqli_connect_errno()){
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
$result = mysqli_query($con,"SELECT user FROM pass_table WHERE password =".md5($value).";");
while($row = mysqli_fetch_array($result)){
mysqli_close($con);
//return $row['user'];
if($row['user']){
return true;
}
}
mysqli_close($con);
return false;
}

php directory content list above root

how do i use php to access the directory above my site root, i need to specifically go up one directory and show contents to the user so they can pick from a couple different directories on the same level as public_html, navigate into them, and when clicking on a file serve it up? server is unix/apache
zipsanimspublic_htmlThank you ahead.
David
i found that if they know the file name it can be served to them by this... named image.php
then image.php?file=imagename.jpg
Thank you!
<?php
$file = $_GET['file'];
$fileDir = '/path/to/files/';
if (file_exists($fileDir . $file))
{
// Note: You should probably do some more checks
// on the filetype, size, etc.
$contents = file_get_contents($fileDir . $file);
// Note: You should probably implement some kind
// of check on filetype
header('Content-type: image/jpeg');
echo $contents;
}
You go up one directory using the .. link. Example:
<?php include("../foo.bar"); ?>
Note that if you're on shared hosting, there's a good chance that the server won't let you do this.

Categories