I am having trouble debugging a php script that I use for downloading .pdf files.
The script works fine for one user but doesn't work for another giving blank page.
What I am pretty sure is:
the part responsible for downloading works fine for both users
The query works fine and gets correct data from the serwer
All of the files are in the same directory (and as I already wrote it works perfectly for the first user)
Please give me a hint on where the bug might be or how to find it.
Thanks so much in advance.
Here's my code:
.htacces :
<Directory /faktury/>
Order deny,allow
Deny from all
</Directory>
html :
<form action="downloadfv.php" method="post">
<input type="text" name="fv" id="fv" value="$rowvariable" hidden />
<button type="submit"">Download</button>
</form>
downloadfv.php :
<?php
session_start();
if(!isset($_SESSION['zalogowany']))
{
header('Location: logowanie.php');
exit();
}
require_once "connect.php";
mysqli_report(MYSQLI_REPORT_STRICT);
$polaczenie = new mysqli($host, $db_user, $db_password, $db_name);
mysqli_query($polaczenie, "SET CHARSET utf8");
mysqli_query($polaczenie, "SET NAMES `utf8` COLLATE `utf8_polish_ci`");
if (mysqli_connect_errno())
{
echo "Could not connect to server" . mysqli_connect_error();
}
$idogloszenia = htmlspecialchars($_POST['fv'], ENT_QUOTES,'UTF-8');
$sql = "SELECT * FROM faktury WHERE user='{$_SESSION['user']}' AND idogloszenia = '$idogloszenia' ORDER BY idogloszenia DESC LIMIT 1";
$result = $polaczenie->query($sql);
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
$file = "./faktury/".$row["nazwapdf"].".pdf";
if (file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="'.basename($file).'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
exit;
}
}
} else {
echo " <div class='itemsname'>
<span style='padding:10px; font-size:90%'><u>No invoice available.</u></span>
</div>";
}
$polaczenie->close();
?>
The script works fine. The bug was inside my test database. It ocurred that I've had duplicated rows with similar data in them and the query selected wrong rows.
So the script can be used although as #mehdi pointed out it is not secure.
Thank you #chris85 for your hints.
Related
I check the MD5sum of an .iso file before uploading to my website using Filezilla in active mode. The MD5sum is calculated on the website and matches what was calculated before upload. If I download the same file from the website using the following code, the MD5sum is different.
<?php
$php_scripts = '../../php/';
require $php_scripts . 'PDO_Connection_Select.php';
require $php_scripts . 'GetUserIpAddr.php';
function mydloader($l_filename=NULL)
{
$ip = GetUserIpAddr();
if (!$pdo = PDOConnect("foxclone"))
{
echo "Failed to connect to database" ;
exit;
}
if( isset( $l_filename ) ) {
// var_dump($ip,$l_filename);
$stmt = $pdo->prepare("INSERT INTO download (IP_ADDRESS, FILENAME) VALUES (?, ?)");
$stmt->execute([$ip, $l_filename]) ;
header('Content-Type: octet-stream');
header("Content-Disposition: attachment; filename={$l_filename}");
header('Pragma: no-cache');
header('Expires: 0');
readfile($l_filename);
echo "Made it here";
}
else {
echo "isset failed";
}
}
mydloader($_GET["f"]);
Why is this happening?
EDIT: I found the difference by opening both the original iso and the downloaded iso in atom editor. The downloaded version has the following added to the start of the file:
herestring(14) “xx.xxx.xxx.xxx” <— my ip
string(17) “foxclone35-02.iso”
herestring(14) “xx.xxx.xxx.xxx”
string(17) “foxclone35-02.iso”
Solved by removing some debugging code that was being added to the downloaded file.
I have recently made a website and have a members table in my sql. I have an admin page that has the ability to download that with column names; however I used to use INTO OUTFILE but that is not allowed on my host. I came up with this code to download the file:
if(file_exists('downloads/members_'.date("m-d-Y").'.csv') == true) {
header("Location: /downloads/members_".date("m-d-Y").".csv");
}
$output = fopen('downloads/members_'.date("m-d-Y").'.csv', 'w+');
fputcsv($output, array('memberID','username', 'password', 'email', 'active', 'resetToken', 'resetComplete', 'support', 'supportToken' ));
$rows = mysql_query("SELECT * FROM members;");
while ($row = mysql_fetch_assoc($rows)) fputcsv($output,$row);
header("Location: /downloads/members_".date("m-d-Y").".csv");
exit;
This just outputs the column names and I have never used fputcsv before and am a tad confused on what I am doing wrong. I get the file to download but not with the table in it.
[EDIT] I just tried it with a valid sql query and it does not work. updated code above. Also There is stuff in the members table.
Try this:
if (isset($_POST['download'])) {
$result = mysql_query("SELECT * FROM members", $connection);
header('Content-Type: text/csv');
header('Content-Disposition: attachment;filename=yourcsv.csv');
header('Cache-Control: no-cache, no-store, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
$csvoutput = fopen('php://output', 'w');
$row = getRow($result);
$headers = array_keys($row);//columns from db e.g, memberID','username',...
fputcsv($csvoutput, $headers);
fputcsv($csvoutput, $row);
while ($row = getRow($result)) {
fputcsv($csvoutput, $row);
}
fclose($csvoutput);
exit;
}
Here is your getRow() function:
function getRow($res){
return mysql_fetch_assoc($res);
}
And in your html:
<input type="submit" name="download" value="Download CSV">
Here is realtime demo with sqllite3 database. Uncomment the create database and insert statement in order to see the working demo. I have tested and it is working fine. I hope this will help you.
I have an export function on my website. It exports from a MySQL result query and lets it save the file as CSV locally on user's computer.
Export functionality is almost working, except for one thing. Inside the exported and saved CSV right after all the exported MySQL data at the end the PHP script also includes the entire form's code.
Not sure why.
Export form. Let's you pick what data to export using <select>. This whole code shows up in CSV file after all data:
<div class="btn-group-vertical btn-group-lg btn-block">
<span class="glyphicon glyphicon-step-backward"></span> Back
</div>
<hr />
<?php
// show potential errors / feedback (from registration object)
if (isset($obj1)) {
if ($obj1->errors) {
foreach ($obj1->errors as $error) {
echo $error;
}
}
if ($obj1->messages) {
foreach ($obj1->messages as $message) {
echo $message;
}
}
}
?>
<form name="exportcsvform" method="post" action="admin_export.php" role="form">
<div class="form-group">
<label for="export_data" class="sr-only">Choose:</label>
<select size="1" name="export_data" id="export_data" class="input-lg">
<option value="" selected="selected" disabled>Choose</option>
<?php $obj1->getData(); ?>
</select>
</div>
<div class="form-group">
<button type="submit" class="btn btn-default btn-lg" name="export" id="export"><span class="glyphicon glyphicon-save"></span>Export</button>
</div>
<hr />
</form>
The PHP function that generates the CSV. It's inside a class, let's call it Class1:
private function exportData()
{
if(isset($_POST['export_data'])){
$this->db_connection = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
if (!$this->db_connection->set_charset("utf8")) {
$this->errors[] = $this->db_connection->error;
}
if (!$this->db_connection->connect_errno) {
$export_data_id = $_POST['export_data'];
$sql =<<<EOF
SELECT col1, col2, col3
FROM table1
JOIN table2 ON table2.col0=table1.col0
WHERE table1.col0 = $export_data_id;
EOF;
// output headers so that the file is downloaded rather than displayed
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=data.csv');
// create a file pointer connected to the output stream
$output = fopen('php://output', 'w');
// output the column headings
fputcsv($output, array('col1','col2','col3'));
$query_export_data = $this->db_connection->query($sql);
$rows = array();
while($r = mysqli_fetch_assoc($query_export_data)){
$rows[] = $r;
}
// loop over the rows, outputting them
foreach ($rows as $r) fputcsv($output, $r);
}else{
// show error
}
}
}
Main PHP file that loads headers, content, footer and is also the action for the export form(admin_export.php):
<?php
// include the configs / constants for the database connection
require_once("config/db.php");
// load the login class
require_once("classes/Login.php");
// create a login object. when this object is created, it will do all login/logout stuff automatically
// so this single line handles the entire login process. in consequence, you can simply ...
$login = new Login();
// only administrators can see
if (login stuff) {
// require once classes here
require_once("classes/User.php");
$user = new User();
require_once("classes/Class1.php");
$class1 = new Class1();
include("includes/header.php");
// include views here
// include("views/admin_whatever_view.php");
include("views/admin_export_view.php");
include("includes/footer.php");
}else{
echo "<h1>Access denied</h1>";
include("views/go_back_view.php");
}
?>
Most likely it's because you need to flush the output buffer.
Here's a simple export CSV function based on an array taken from ExchangeCore blog. Looks like the only difference is the ob_* functions
public function outputCsv($fileName, $assocDataArray)
{
ob_clean();
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Cache-Control: private', false);
header('Content-Type: text/csv');
header('Content-Disposition: attachment;filename=' . $fileName);
if(isset($assocDataArray['0'])){
$fp = fopen('php://output', 'w');
fputcsv($fp, array_keys($assocDataArray['0']));
foreach($assocDataArray AS $values){
fputcsv($fp, $values);
}
fclose($fp);
}
ob_flush();
}
When I generate an export to a CSV file, I have a tab that is inserted at the beginning.
I don't understand why.
Here is my code :
public function hookExportAll($params){
header('Content-Type: application/csv');
header('Content-Disposition: inline; filename="clients.csv"');
header('Cache-Control: private, max-age=0, must-revalidate');
//mysql request
$sql = "..."
$clientlist = Db::getInstance()->ExecuteS($sql);
//separator recovered the post of the form
$filter['sep']=Tools::getValue('sep');
//; or , or \t
$entete = array('email','nom','prenom','anniversaire','newsletter','adresse','pays','téléphone');
echo $this->getcsvline($entete, $sep);
foreach($clientlist AS $client){
echo $this->getcsvline($client, $sep);
}
}
private function getcsvline($list, $sep="\t"){
return implode($sep, $list)."\r\n";
}
A little help?
Finally, Ivar Bonsaksen must be right. A tab must have been generated in the CMS Prestashop before a . With no time to dwell on this case, I consider this issue as resolved.
Thank you all for your support, see you soon =)
Using PHP, I'm trying to download a blob file that has already been uploaded to an Oracle 10g database. I've seen and imitated numerous examples I've found. When I access the page a File Download dialog appears allowing me to Open or Save. If I click Open, media player comes up as it should but never retrieves the file. If I choose Save, I always get an error message stating "Internet Explorer was not able to open this Internet site. The requested site is either unavailable or cannot be found. Please try again later."
Below is my code which is pretty straight forward and pretty much like the examples I've found.
<?php
header('Content-Disposition: attachment; filename='.$_GET['fileName']);
header('Content-length: '.$_GET['fileSize']);
header('Content-type: '.$_GET['mimeType']);
require_once("Include/Application.php");
$connection = oci_connect ($userID, $password, $TNS);
$phpCur = oci_new_cursor($connection);
$stmt = oci_parse($connection, "BEGIN MOR.DOWNLOAD_ATTACHMENT (:morID, :attachmentType, :phpCur); END;");
oci_bind_by_name($stmt, ":morID", $_GET['morID'], -1);
oci_bind_by_name($stmt, ":attachmentType", $_GET['attachmentType'], -1);
oci_bind_by_name($stmt, "phpCur", $phpCur, -1, OCI_B_CURSOR);
oci_execute($stmt);
oci_free_statement($stmt);
$output = '';
oci_execute($phpCur);
while( $row = oci_fetch_array($phpCur) )
$output .= $row['ATTACHMENT_BL'];
oci_free_statement($phpCur);
oci_close($connection);
echo $output;
exit;
?>
Use your db query and excecute first here is the data field is blob data:
$sql="SELECT FILE_NAME,data,length(data) as filesize FROM branch_data where id='$id'";
$r = $db->execute($sql);
$filename=$r->data[0]['FILE_NAME'];
$d=$r->data[0]['DATA'];
$filesize = $r->data[0]['FILESIZE'];
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.$filename.'"');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' .$filesize);
echo $d->load();
Add more error handling to your script. Any of the oci* function can fail and then subsequent steps will also fail. The documentation tells you what happens if a function fails and what the return value will be. E.g.
Return Values
Returns a connection identifier or FALSE on error.
If you set the Content-type header as late as possible, i.e. directly before the first output, you can send plain text or html that contains some sort of error message instead.
<?php
// error_reporting/ini_set: for testing purposes only.
error_reporting(E_ALL); ini_set('display_errors', 1);
require_once("Include/Application.php");
$connection = oci_connect ($userID, $password, $TNS);
if ( !$connection) {
die('database connection failed');
}
$phpCur = oci_new_cursor($connection);
if ( !$phpCur) {
die('creating cursor failed');
}
$stmt = oci_parse($connection, "BEGIN MOR.DOWNLOAD_ATTACHMENT (:morID, :attachmentType, :phpCur); END;");
if ( !$stmt) {
die('creating statement failed');
}
// and so on and on. Test the return values of each oci* function.
oci_close($connection);
header('Content-Disposition: attachment; filename='.$_GET['fileName']); // at least at basename() here
header('Content-length: '.$_GET['fileSize']); // strange...
header('Content-type: '.$_GET['mimeType']); // possible but still strange...
echo $output;
exit;