views:

159

answers:

4

I have a script that allows users to 'save as' a pdf, this is the script -

<?php
header("Content-Type: application/octet-stream");

$file = $_GET["file"] .".pdf";
header("Content-Disposition: attachment; filename=" . urlencode($file));   
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header("Content-Description: File Transfer");            
header("Content-Length: " . filesize($file));
flush(); // this doesn't really matter.
$fp = fopen($file, "r");
while (!feof($fp))
{
    echo fread($fp, 65536);
    flush(); // this is essential for large downloads
} 
fclose($fp);
?>

An error log is being created and gets to be more than a gig in a few days the errors I receive are-

[10-May-2010 12:38:50] PHP Warning:  filesize() [<a href='function.filesize'>function.filesize</a>]: stat failed for BYJ-Timetable.pdf in /home/byj/public_html/pdf_server.php on line 10
[10-May-2010 12:38:50] PHP Warning:  Cannot modify header information - headers already sent by (output started at /home/byj/public_html/pdf_server.php:10) in /home/byj/public_html/pdf_server.php on line 10
[10-May-2010 12:38:50] PHP Warning:  fopen(BYJ-Timetable.pdf) [<a href='function.fopen'>function.fopen</a>]: failed to open stream: No such file or directory in /home/byj/public_html/pdf_server.php on line 12
[10-May-2010 12:38:50] PHP Warning:  feof(): supplied argument is not a valid stream resource in /home/byj/public_html/pdf_server.php on line 13
[10-May-2010 12:38:50] PHP Warning:  fread(): supplied argument is not a valid stream resource in /home/byj/public_html/pdf_server.php on line 15
[10-May-2010 12:38:50] PHP Warning:  feof(): supplied argument is not a valid stream resource in /home/byj/public_html/pdf_server.php on line 13
[10-May-2010 12:38:50] PHP Warning:  fread(): supplied argument is not a valid stream resource in /home/byj/public_html/pdf_server.php on line 15

The line 13 and 15 just continue on and on... I'm a bit of a newbie with php so any help is great. Thanks guys Nik

+4  A: 

What's happening is:

  • Your file could not be found, so the filesize function errorred out
  • Because it errored out, it produced output.
  • Once you have output, you CANNOT send anymore headers
  • Because you are sending more headers, you are getting more errors

Because your file was not found, your while loop will error out forever, because you are trying to read from a file that does not exist.

ALSO: A file has ONE content-type, not 4. You need to pick one of the content-types and use that one, you can't pass them all.

And finally: Your script is horribly insecure. If someone passed ?file=/home/apache/secret_db_passwords.txt then they would immediately have access to your stuff.

Edit: PHP has a function called readfile that is designed to handle exactly what you are trying to do. You do not need to have a while loop run thru the file sending bit by bit. Just do readfile($file); and the web server will handle all that crap for you

webdestroya
+1 for the security catch!
Anthony
Great... but sh#t now I'm a bit worried as I have used this script on numerous websites that have sensitive info on them... oh dear! I understand what you are saying I'm just not sure how to implement the fixes? I cut and paste that script as thats all my skills allow when it comes to php =\
Nik
Well you should set the filename to be something similar (like all letters/numbers). That way you can strip out all other characters. You could also try stripping all `/`'s or something, but I'm not sure of an easy 'addon' hack to fix the problem. But as a rule of thumb, you should NEVER let the user specify parameters that are directly inserted into a table, or used to access a file (without properly sanitizing them)
webdestroya
Thanks I think its time to go through and sanitize my work ;)
Nik
+1  A: 

Couple of things:

  1. You can only set a header once, as far as I know, so setting your Content-Type four times is pointless.

  2. It might help if you set the content type to a pdf mime type, in this case I'd go with application/pdf.

  3. Force Download is not a content type, as far as I know. The browser will download if you set the Content-Disposition to attachment, which you already have.

  4. Finally, if there is no PDF version of the file (which it looks like there isn't) why are you trying to stream from it? You probably want to set your $file variable to the actual file, and make another variable like $filename for setting the PDF file name that is output to the user.

Anthony
Technically, you can send more than one of the same header. However, you have to do `header("Header",false)` the `false` tells it to append. (But, it is never used for a Content-Type header, as that makes no sense)
webdestroya
I was half-bluffing, but I'm glad I was right in this case. Thanks for the heads up.
Anthony
+1  A: 
[10-May-2010 12:38:50] PHP Warning:  fopen(BYJ-Timetable.pdf) [<a href='function.fopen'>function.fopen</a>]: failed to open stream: No such file or directory in /home/byj/public_html/pdf_server.php on line 12

This should tell you what you need to know: fopen and filesize both need to be passed the name, and path to, the file to be opened. In this case, you're just passing the filename, no path, so it's trying to find it in the current directory the script is running in. In this case, /home.byj/public_html/

To fix this, you need to apply a relative or absolute path to the PDF file to open to these functions, not just the name of the PDF file itself.

Slokun
+1  A: 

Instead of looping and reading pieces of the file can you use: http://php.net/manual/en/function.readfile.php?

Jonathan