how to store and send $_FILES via javascript/php - php

I'm trying to set up a form that can upload to both YouTube and Vimeo simultaneously. I would prefer to use Posterous.com for something like this, but since they've been acquired by twitter, their help team has dropped off the face of the earth as my emails are now going unanswered (they've removed a bunch of services)...
So anyways here's how the youtube process is supposed to work:
Set a title and category for the video you want to upload via webform
submit form, get an access token back from youtube
another form is generated, allowing you to select the file to upload
submit form, access token and file are sent and youtube uploads the video
What I'm trying to do is turn this into a single step with a drag and drop uploader:
drag and drop file onto page
javascript grabs the file information, sets the filename as the video title and uses a default category
javascript calls php and sends filename & category to youtube, gets access token back and creates form with file input
after getting the token, send a POST (via PHP) request with file upload stored in a session variable (right now I have to select the file again and click submit)
shouldn't I be able to use the file information from the first step, store it in a session variable and programmatically submit the token and file information via php? I don't know how to send this data like it was sent as a form submission and I'm not always getting response codes back from youtube to fix my code.
this may be the answer I need: sending xml and headers via curl but I don't know how to set the $xmlString or $videoData
EDIT::
I think I need to do this via PHP, not javascript because I'm trying to modify the following code:
/**
* Create upload form by sending the incoming video meta-data to youtube and
* retrieving a new entry. Prints form HTML to page.
*
* #param string $VideoTitle The title for the video entry.
* #param string $VideoDescription The description for the video entry.
* #param string $VideoCategory The category for the video entry.
* #param string $nextUrl (optional) The URL to redirect back to after form upload has completed.
* #return void
*/
function createUploadForm($videoTitle, $videoCategory, $nextUrl = null) {
$httpClient = getAuthSubHttpClient();
$youTubeService = new Zend_Gdata_YouTube($httpClient);
$newVideoEntry = new Zend_Gdata_YouTube_VideoEntry();
$newVideoEntry->setVideoTitle($videoTitle);
//make sure first character in category is capitalized
$videoCategory = strtoupper(substr($videoCategory, 0, 1))
. substr($videoCategory, 1);
$newVideoEntry->setVideoCategory($videoCategory);
// convert videoTags from whitespace separated into comma separated
$tokenHandlerUrl = 'https://gdata.youtube.com/action/GetUploadToken';
try {
$tokenArray = $youTubeService->getFormUploadToken($newVideoEntry, $tokenHandlerUrl);
if (loggingEnabled()) {
logMessage($httpClient->getLastRequest(), 'request');
logMessage($httpClient->getLastResponse()->getBody(), 'response');
}
} catch (Zend_Gdata_App_HttpException $httpException) {
print 'ERROR ' . $httpException->getMessage()
. ' HTTP details<br /><textarea cols="100" rows="20">'
. $httpException->getRawResponseBody()
. '</textarea><br />'
. '<a href="session_details.php">'
. 'click here to view details of last request</a><br />';
return;
} catch (Zend_Gdata_App_Exception $e) {
print 'ERROR - Could not retrieve token for syndicated upload. '
. $e->getMessage()
. '<br /><a href="session_details.php">'
. 'click here to view details of last request</a><br />';
return;
}
$tokenValue = $tokenArray['token'];
$postUrl = $tokenArray['url'];
// place to redirect user after upload
if (!$nextUrl) {
$nextUrl = $_SESSION['homeUrl'];
}
//instead of echoing the form below, send $_FILES from previous form submit
print <<< END
<br />
<form id="uploadToYouTubeForm" action="${postUrl}?nexturl=${nextUrl}" method="post" enctype="multipart/form-data">
<input id="uploadToYouTube" name="file" type="file" />
<input name="token" type="hidden" value="${tokenValue}"/>
<input value="Upload Video File" type="submit" />
</form>
END;
}

I think I may have a solution. Below is the form I want to use:
<!--removing the action ensures form will be sent via javascript, then when that request comes back, I can add in the authenticated youtube URL needed-->
<form id="upload" action="" method="POST" enctype="multipart/form-data" class="form-horizontal"><!--upload.php-->
<fieldset>
<legend><h1>Video File Upload</h1></legend>
<input type="hidden" id="MAX_FILE_SIZE" name="MAX_FILE_SIZE" value="1000000000" /> <!--1GB-->
<p id="filedrag">Drag and drop a video file from your computer here. Or use the 'File upload' button below.</p><!--dragFileHere-->
<label class="control-label" for="fileselect">Files to upload:</label>
<input type="file" id="fileselect" name="fileselect[]" /> <!--multiple="multiple"-->
<button class="btn" id="submitbutton" type="submit">Upload Files</button> <!--hidden via js/css-->
<div class="progress progress-striped active">
<div class="bar" style="width: 0%;"></div>
</div>
<label class="hide" for="video-title">Title</label>
<input type="text" id="video-title" class="span4" placeholder="Video Title"/>
<label class="control-label" for="video-category">Category</label>
<select id="video-category" name="videoCategory" class="span4">
<option value="Autos">Autos & Vehicles</option>
<option value="Music">Music</option>
.......
<option value="Entertainment" selected>Entertainment</option>
</select>
<input id="token" type="text" placeholder="token"/> <!--will be hidden-->
</div>
</fieldset>
</form>
by leaving the action attribute blank, (and using script I already had in place to respond to user interaction) I can ensure the form is submitted programmatically via javascript:
<script src=http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js></script>
<script src=_js/video_app.js" type="text/javascript></script>
<script>
/*
filedrag.js - HTML5 File Drag & Drop demonstration
Featured on SitePoint.com
Developed by Craig Buckler (#craigbuckler) of OptimalWorks.net (without jQuery)
*/
// output information
function Output(msg) {
$('#messages').html(msg + $('#messages').html());
}
// file drag hover
function FileDragHover(e) {
e.stopPropagation();
e.preventDefault();
$(this).addClass("hover");
}
function FileDragOut(e) {
$(this).removeClass("hover");
}
// file selection
function FileSelectHandler(e) {
// cancel event and hover styling
FileDragHover(e);
// fetch FileList object
var files = e.target.files || e.dataTransfer.files;
// process all File objects
for (var i = 0, f; f = files[i]; i++) {
ParseFile(f); //prints file data to div and optionally displays content of selected file
UploadFile(f); //uploads file to server
}
}
// output file information
function ParseFile(file) {
videoName = file.name;
videoType = file.type;
videoURL = "http://localhost/"+videoName;
videoCategory = $('#video-category').val();
Output(
"</strong> type: <strong>" + file.type +
"</strong> size: <strong>" + file.size +
"</strong> bytes</p>"
);
// sets a default value because a title is needed for youtube to send response
if( $('#video-title').val() == $('#video-title').attr('placeholder') ) {
$('#video-title').val(videoName);
}
var reader = new FileReader();
reader.onload = function(e) {
var fileContents = e.target.result;
Output(
'<img src="'+e.target.result+'"/>'
);
}
reader.readAsDataURL(file);
//get upload token
ytVideoApp.prepareSyndicatedUpload(videoName, videoCategory);
}
// upload video files
function UploadFile(file) {
var xhr = new XMLHttpRequest();
if (xhr.upload && file.size <= $('#MAX_FILE_SIZE').val()) { //&& file.type == "video/mp4" or video/*
xhr.upload.addEventListener("progress", function(e) {
var pc = Math.ceil(e.loaded / e.total * 100);
}, false);
// file received/failed
xhr.onreadystatechange = function(e) {
if (xhr.readyState == 4) {
if(xhr.status == 200) { //success
} else { //fail
}
}
};
// start upload
xhr.open("POST", 'upload.php', true); //$("#upload").attr('action')
xhr.setRequestHeader("X_FILENAME", file.name);
xhr.send(file);
}
}
// initialize
function Init() {
// file select
$('#fileselect').change(FileSelectHandler);
// is XHR2 available?
var xhr = new XMLHttpRequest();
if (xhr.upload) {
// file drop
$('#filedrag').bind('dragover', FileDragHover);
$('#filedrag').bind('dragleave', FileDragOut);
//I can't get the below line to work, so I've used the ugly fallback
//$('#filedrag').bind('drop', FileSelectHandler);
document.getElementById('filedrag').addEventListener("drop", FileSelectHandler, false);
filedrag.style.display = "block";
// remove submit button
submitbutton.style.display = "none";
}
}
// call initialization file
if (window.File && window.FileList && window.FileReader) {
Init();
}
</script>
_js/video_app.js:
/**
* Zend Framework
* #package Zend_Gdata
....
/**
* provides namespacing for the YouTube Video Application PHP version (ytVideoApp)
**/
var ytVideoApp = {};
/**
* Sends an AJAX request to the server to retrieve a list of videos or
* the video player/metadata. Sends the request to the specified filePath
* on the same host, passing the specified params, and filling the specified
* resultDivName with the resutls upon success.
* #param {String} filePath The path to which the request should be sent
* #param {String} params The URL encoded POST params
* #param {String} resultDivName The name of the DIV used to hold the results
*/
ytVideoApp.sendRequest = function(filePath, params, resultDivName) {
if (window.XMLHttpRequest) {
var xmlhr = new XMLHttpRequest();
} else {
var xmlhr = new ActiveXObject('MSXML2.XMLHTTP.3.0');
}
xmlhr.open('POST', filePath);
xmlhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xmlhr.onreadystatechange = function() {
var resultDiv = document.getElementById(resultDivName);
if (xmlhr.readyState == 1) {
resultDiv.innerHTML = '<b>Loading...</b>';
} else if (xmlhr.readyState == 4 && xmlhr.status == 200) {
if (xmlhr.responseText) {
resultDiv.innerHTML = xmlhr.responseText;
}
} else if (xmlhr.readyState == 4) {
alert('Invalid response received - Status: ' + xmlhr.status);
}
}
xmlhr.send(params);
}
ytVideoApp.prepareSyndicatedUpload = function(videoTitle, videoCategory, fileContents) {
var filePath = '_scripts/operations.php';
var params = 'operation=create_upload_form' +
'&videoTitle=' + videoTitle +
'&videoCategory=' + videoCategory;
ytVideoApp.sendRequest(filePath, params, ytVideoApp.SYNDICATED_UPLOAD_DIV);
}
_scripts/operations.php:
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata_YouTube');
Zend_Loader::loadClass('Zend_Gdata_AuthSub');
Zend_Loader::loadClass('Zend_Gdata_App_Exception');
/*
* The main controller logic.
*
* POST used for all authenticated requests
* otherwise use GET for retrieve and supplementary values
*/
session_start();
setLogging('on');
generateUrlInformation();
if (!isset($_POST['operation'])) {
// if a GET variable is set then process the token upgrade
if (isset($_GET['token'])) {
updateAuthSubToken($_GET['token']);
} else {
if (loggingEnabled()) {
logMessage('reached operations.php without $_POST or $_GET variables set', 'error');
header('Location: add-content.php');
}
}
}
$operation = $_POST['operation'];
switch ($operation) {
....
case 'create_upload_form':
createUploadForm($_POST['videoTitle'],
$_POST['videoCategory'],
$_POST['videoContents']);
break;
....
default:
unsupportedOperation($_POST);
break;
}
function createUploadForm($videoTitle, $videoCategory, $nextUrl = null) {
$httpClient = getAuthSubHttpClient();
$youTubeService = new Zend_Gdata_YouTube($httpClient);
$newVideoEntry = new Zend_Gdata_YouTube_VideoEntry();
$newVideoEntry->setVideoTitle($videoTitle);
//make sure first character in category is capitalized
$videoCategory = strtoupper(substr($videoCategory, 0, 1))
. substr($videoCategory, 1);
$newVideoEntry->setVideoCategory($videoCategory);
// convert videoTags from whitespace separated into comma separated
$tokenHandlerUrl = 'https://gdata.youtube.com/action/GetUploadToken';
try {
$tokenArray = $youTubeService->getFormUploadToken($newVideoEntry, $tokenHandlerUrl);
if (loggingEnabled()) {
logMessage($httpClient->getLastRequest(), 'request');
logMessage($httpClient->getLastResponse()->getBody(), 'response');
}
} catch (Zend_Gdata_App_HttpException $httpException) {
print 'ERROR ' . $httpException->getMessage()
. ' HTTP details<br /><textarea cols="100" rows="20">'
. $httpException->getRawResponseBody()
. '</textarea><br />'
. '<a href="session_details.php">'
. 'click here to view details of last request</a><br />';
return;
} catch (Zend_Gdata_App_Exception $e) {
print 'ERROR - Could not retrieve token for syndicated upload. '
. $e->getMessage()
. '<br /><a href="session_details.php">'
. 'click here to view details of last request</a><br />';
return;
}
$tokenValue = $tokenArray['token'];
$postUrl = $tokenArray['url'];
// place to redirect user after upload
if (!$nextUrl) {
$nextUrl = $_SESSION['homeUrl'];
}
//instead of outputting the form below, send variables (json???) to be interpreted by xmlhr in _js/video_app.js
//print <<< END
//<br />
//<p>url: ${postUrl}?nexturl=${nextUrl}</p>
//<form id="uploadToYouTubeForm" action="temp.php" method="post" enctype="multipart/form-data">
//<input id="uploadToYouTube" name="file" type="file" onchange="autoUploadToYouTube();" /><br/>
//token: <input id="token" name="token" type="text" value="${tokenValue}"/><br/>
//<input value="Manual upload" type="submit" />
//</form>
//END;
//}
So the first javascript listens for the drag & drop (on the div) or change() event for the input type="file". The form is not actually submitted, but data is gathered and and sent via ajax to the php script which returns the token and url needed for upload. The original PHP script then outputs a form to select the file (and a hidden field containing the token). INSTEAD of that, I want to pass the url and token as variables and use the XMLHttpRequest to put those variables in the action attribute of the form as well set the key to a hidden input field's value. Then the field can be submitted via $('#uploadToYouTubeForm').submit();
The only problem that may arise is sending additional information to the youtube app, I'm hoping it will simply ignore it or i may need to programmatically remove the fields that youtube won't accept (title, category, max file size)....

Related

Loop through $_FILES causes JSON unexpected Token

***** Update:** The issue I believe is with the dSubmit function, it might not be handling multipart form data therefore causes PHP to throw an error when the foreach is executed? I've submitted over arrays through the function without issue, is there something different with the files[]
I have a AJAX form submission on a website, the JSON array that is returned is used to show message or direct to another page. Everything works well until I loop through file uploads sent through the form via the PHP script. I have removed all possible extra code so it is now bare bones and I still can't resolve it:
upload.php
<?php
foreach($_FILES['files']['name'] as $f => $name){};
$returnData = array(
"infoMsg" => "message for user",
"color" => "orange"
);
echo json_encode($returnData);
?>
html snippet:
<form id="fileUpload" name="fileUpload" enctype="multipart/form-data">
<input type="file" id="file" name="files[]" multiple="multiple"/>
</form>
<button onclick="dSubmit('fileUpload' , 'inc/upload.php')">test</button>
dSubmit is the function to submit the form, everything is working as expected until I introduce the looping of the uploaded files. The error I receive is:
Uncaught SyntaxError: Unexpected token < in JSON at position 0
I can see no extra characters/whitespace or anything else that might cause the JSON issue.
Here's the dSubmit code found in the head:
function dSubmit(formName, formAction){
// formName = id of Form to process
// formAction = php file to parse data to
var url = formAction;
event.preventDefault()
for ( instance in CKEDITOR.instances )
CKEDITOR.instances[instance].updateElement();
$.ajax({
type: "POST",
url: url,
data: $("#" + formName).serialize(),
success: function(returnData)
{
var data = JSON.parse(returnData); // Return php array from formAction
var passedInfoMsg = (data['infoMsg']); // infoMsg (if set)
var color = (data['color']); // infoMsg color (if set)
var resetForm = (data['reset']); // boolean reset submitted form upon submission
var fileToLoad = (data['fileToLoad']); // fileToLoad (if set)
var divToLoad = (data['divToLoad']); // divToLoad fileToLoad in (if set)
console.log("Form Return: passedInfoMsg: " + passedInfoMsg + " | color: " + color + " | Reset: " + resetForm + " | fileToLoad: " + fileToLoad + " | divToLoad: " + divToLoad);
if(passedInfoMsg!==undefined) {
// If message defined, display message
infoMsg(passedInfoMsg,color);
}
if(resetForm===true) {
// clear submitted form upon request from formAction
clearForm($("#"+formName))
}
if(fileToLoad!==undefined) {
// if fileToLoad is set, run dLoader
dLoader(fileToLoad,divToLoad);
}
}
});
return false; // avoid to execute the actual submit of the form.
}
The full code as requested:
storage.php ( where uploads take place ):
<?php
require $_SERVER['DOCUMENT_ROOT'] . '/../vendor/bucketScripts/start.php'; // include AWS email parser
$folderTitle = "Client Portal";
$projectId = "36";
$folder = "client";
$objects = $s3->getIterator('ListObjects', array(
"Bucket" => "openplanman",
"Prefix" => "Projects/$projectId/$folder/" //must have the trailing forward slash "/"
));
$passedPrefix = "Projects/$projectId/$folder/";
?>
<div class="row">
<div class="12u 12u$(medium)">
<h3><?php echo $folderTitle;?></h3>
</div>
<form id="fileUpload" name="fileUpload" enctype="multipart/form-data" method="POST" action="inc/uploadFiles.php" target="_blank">
<input type="hidden" name="projectId" value="<?php echo $projectId;?>">
<input type="hidden" name="uploadFolder" value="<?php echo $folder;?>">
<input type="hidden" name="folderTitle" value="<?php echo $folderTitle;?>">
<input type="file" id="file" name="files[]" multiple="multiple"/>
<input type="submit" value="Upload">
</form>
<button onclick="dSubmit('fileUpload' , 'inc/uploadFiles.php')">test</button>
<div class="12u 12u$(medium)">
<table>
<?php
$fileArray = array();
foreach ($objects as $object) {
// Load into a new array
array_push($fileArray, $object['Key']);
}
foreach ($fileArray as $file) {
$noPrefixFileName = str_replace("$passedPrefix","",$file);
if($noPrefixFileName!=null)
{
$urlFilename = urlencode($noPrefixFileName);
echo "<tr><td>" . $noPrefixFileName . "</td><td><a onclick=\"fileRequest.php?id=$projectId&folder=$folder&req=$urlFilename\" target='_blank'><i class='fa fa-cloud-download' aria-hidden='true'></a></td><td><i class='fa fa-eye' aria-hidden='true'></td></tr>";
}
}
?>
</table>
</div>
</div>
uploadFiles.php (PHP script that handles the upload, works when POST not through AJAX)
<?php
require $_SERVER['DOCUMENT_ROOT'] . '/../vendor/bucketScripts/start.php';
$err = 0;
$projectId = $_POST['projectId'];
$uploadFolder = $_POST['uploadFolder'];
$path = $_SERVER['DOCUMENT_ROOT'] . '/../tmp/'; // include AWS email parser
$bucketPath = "../../../tmp/";
if(isset($_POST) and $_SERVER['REQUEST_METHOD'] == "POST"){
// Loop $_FILES to exeicute all files
foreach ($_FILES['files']['name'] as $f => $name) {
if ($_FILES['files']['error'][$f] == 4) {
continue; // Skip file if any error found
}
if ($_FILES['files']['error'][$f] == 0) {
// No error found! Move uploaded files
if(move_uploaded_file($_FILES["files"]["tmp_name"][$f], $path.$name))
$fileName = $_FILES["files"]["name"][$f];
// echo "<br>Key: " . "Projects/$projectId/$uploadFolder/$fileName";
// echo "<br>Source: " . "$bucketPath$fileName";
try {
$s3->putObject([
'Bucket' => $config['s3']['bucket'],
'Key' => "Projects/$projectId/$uploadFolder/$fileName",
'SourceFile' => "$bucketPath$fileName",
'ACL' => 'public-read',
]);
} catch (S3Exception $e) {
$err = 1;
}
unlink($path.$name); // Housekeeping
}
}
}
$returnData = array(
"infoMsg" => "user message",
"color" => "orange",
);
echo json_encode($returnData);
?>
you got this error because there is a PHP error response that treated by the browser as json content and it's not.
you can find out that error response through the chrome developer tools
press F12 to open the developer tools
select Network tab
select XHR filter
select the request that you make
on the right side you can find the response tab which will content the raw response
by the way i think your problem is your code try to iterate through $_FILES['files']['name'] which is string :)
Hope It's helpful..!
Your files array becomes like:-
$_FILES['files'][0]['name']
$_FILES['files'][1]['name']
When you do multiple file upload.

PHP Upload Security- prevent user from uploading unlimited files- form with ajax upload

Edit 2 : I notices user can upload unlimited files and can take all disk space, how to prevent that?
Edit: since no one answered this question, is there a source I could read to get my answer???
I have a contact form. There are three inputs. I used a jQuery plugin for uploading files. This plugin adds another form element and uploads files by ajax.
I'm kind of beginner but this code is for a customer and a real job so I want to make sure it's safe!
in my view:
<form action="" method="post" enctype="multipart/form-data" >
<input type="text" name="name" />
<input type="number" name="phone" />
<textarea name="enquiry" rows="10" ></textarea>
<div id="upload-div">
<div id="extraupload">Upload</div>
<input type="hidden" name="count" value="0" id="count"/>
<input type="submit" />
$(document).ready(function()
{
var uploadObj = $("#extraupload").uploadFile({
url:"/uplod_url",
fileName:"file",
onSuccess:function(files,data,xhr,pd)
{
data = jQuery.parseJSON(data);
if(data.status == 'success') {
var count = $('#count').val() * 1 + 1;
for(var i=0; i<data.files.length; i++) {
$('<input type="hidden" name="file_'+count+'" value="'+data.files[i]+'">').insertBefore('#extraupload');
$('#count').val(count);
count++;
}
}
},
});
});
</script>
each successful upload,will add one to input count value
and will append an hidden input with the value of uploaded file name.
In php I check for file type and change file name:
upload_url.php:
if ($_FILES['file']['type']=='image/jpeg' || $_FILES['file']['type']=='image/pjpeg') {
$ext = '.jpg';
}
elseif ($_FILES['file']['type']=='image/png') {
$ext = '.png';
}
elseif ($_FILES['file']['type']=='application/pdf') {
$ext = '.pdf';
}
else {
echo json_encode('Only images and pdf files are allowed!');
die();
}
$fileName = md5(uniqid());
$fileName = $fileName.$ext;
move_uploaded_file($_FILES["file"]["tmp_name"], 'image/tmp'.$fileName);
$result = array('status'=> 'success','files' => $fileName);
echo json_encode($result);
After changing the file's name to a unique hash, I save that in a tmp folder.
then when the main form is submitted this is what happens:
//validation method: if that file exists in tmp folder
if(isset($this->request->post['count']) && is_numeric($this->request->post['count'])) {
for($i=1; $i<=$this->request->post['count']; $i++ ) {
if(isset($this->request->post['file_'.$i])){
if(!file_exists('image/tmp/'.$this->request->post['file_'.$i])){
//throw error
}
} else{
//throw error
}
}
}
// hidden input count can only be integer
if(isset($this->request->post['count']) && !is_numeric($this->request->post['count'])) {
//throw error
}
and then mailing the file and saving file name in database(I did not include database part because I'm kind of sure it's ok)
//by every submition delete files in tmp folder older than 1 day
$oldFiles = glob($tmp_dir."*");
$now = time();
foreach ($oldFiles as $oldFile) {
if (is_file($oldFile)) {
if ($now - filemtime($oldFile) >= 60 * 60 * 24) {
unlink($oldFile);
}
}
}
$mail = new Mail();
//Mail Setting and details deleted
//if there's any file uploaded
if($this->request->post['count'] != 0) {
//unique directory for every form submition
$dir_path = 'image/submitted/'.uniqid();
mkdir($dir_path, 0764, true);
//for all hidden inputs move file from tmp folder to $dir_path
for ($i=1; $i <= $this->request->post['count']; $i++) {
$file = $this->request->post['file_'.$i];
rename('image/tmp'.$file, $dir_path.'/'.$file);
$mail->AddAttachment($dir_path.'/'.$file);
}
}
$mail->send();
now my question is: Is it safe this way? especially when I append hidden inputs with file's name and get the number of uploaded files from hidden input count??
This code already works, but I think this might be a security issue.
Thanks a lot for your patience and sorry for my poor english!
ps: I use opencart
There is the general misconception that in AJAX applications are more secure because it is thought that a user cannot access the server-side script without the rendered user interface (the AJAX based webpage). XML HTTP Request based web applications obscure server-side scripts, and this obscurity gives website developers and owners a false sense of security – obscurity is not security. Since XML HTTP requests function by using the same protocol as all else on the web (HTTP), technically speaking, AJAX-based web applications are vulnerable to the same hacking methodologies as ‘normal’ applications.

I am getting an undefined variable

I am trying to retrieve a file name from one page where the php script uploads the file (imageupload.php), and I want to display it in another page within a javascript function (QandATable.php). But I don't know how to do this
I will show you all of the relevant code so you can follow it and so you are able to understand what is happening.
UPDATE: BELOW I WILL SHOW YOU THE STEPS ON HOW THE FILE IS UPLOADED. THE CODE BELOW SUCCESSFULLY UPLOADS THE FILE.
Below is the form (QandATable.php);
var $fileImage = $("<form action='imageupload.php' method='post' enctype='multipart/form-data' target='upload_target' onsubmit='return imageClickHandler(this);' class='imageuploadform' >" +
<label>Image File: <input name='fileImage' type='file' class='fileImage' /></label><br/><label class='imagelbl'>" +
"<input type='submit' name='submitImageBtn' class='sbtnimage' value='Upload' /></label>" +
"</p><ul class='listImage' align='left'></ul>" +
"<iframe class='upload_target' name='upload_target' src='#' style='width:0;height:0;border:0px;solid;#fff;'></iframe></form>");
On the same page when the user submits the form, it will go onto the function below, it will check for validation and then when validation is clear, it will go onto the startImageUpload() function:
function imageClickHandler(imageuploadform){
if(imageValidation(imageuploadform)){
return startImageUpload(imageuploadform);
}
return false;
}
If there is no validation then it will go onto the JS function (QandATable.php) below where it hides the file input and it will submit the form to the imageupload.php where the file uploading occurs. When the file is uploaded it then calls back to the stopImageUpload() function (QandAtable.php) where it will display the message on whether the file is uploaded or not and this is where I want the name of the file from the server to be appended.
Below is startImageUpload() function:
var sourceImageForm;
function startImageUpload(imageuploadform){
$(imageuploadform).find('.fileImage').css('visibility','hidden');
sourceImageForm = imageuploadform;
return true;
}
Below is the php script where it uploads the file (imageupload.php):
<?php
session_start();
$result = 0;
if( file_exists("ImageFiles/".$_FILES['fileImage']['name'])) {
$parts = explode(".",$_FILES['fileImage']['name']);
$ext = array_pop($parts);
$base = implode(".",$parts);
$n = 2;
while( file_exists("ImageFiles/".$base."_".$n.".".$ext)) $n++;
$_FILES['fileImage']['name'] = $base."_".$n.".".$ext;
move_uploaded_file($_FILES["fileImage"]["tmp_name"],
"ImageFiles/" . $_FILES["fileImage"]["name"]);
$result = 1;
}
else
{
move_uploaded_file($_FILES["fileImage"]["tmp_name"],
"ImageFiles/" . $_FILES["fileImage"]["name"]);
$result = 1;
}
?>
<script language="javascript" type="text/javascript">
window.top.window.stopImageUpload(<?php echo $result;?>);
</script>
Finally when upload is finished it goes back to the stopUploadImage() function (QandATable.php) to display the message on whether file is successfully uploaded or not. This is also where I want the uploaded file name from the server to be appended.
function stopImageUpload(success){
var result = '';
if (success == 1){
result = '<span class="msg">The file was uploaded successfully!</span><br/><br/>';
$('.listImage').append('<br/>');
}
else {
result = '<span class="emsg">There was an error during file upload!</span><br/><br/>';
}
return true;
}
Your $_POST won't contain fileimagename. Instead, your form input was called fileImage. Use that instead:
// Check $_POST for fileImage, which was the form input name
if (isset($_POST['fileImage'])) {
$_SESSION['fileimagename'] = $_FILES['fileImage']['name'];
// Proceed with the file upload and save.
}
else {
// oops, can't proceed
}
On the JavaScript page, do some error checking when accessing the value:
<?php
session_start();
if (isset($_SESSION['fileimagename'])) {
$fileimagename = $_SESSION['fileimagename'];
// output JS code...
?>
<script type="text/javascript">Your JS code here...</script>
<?php
}
else {
// No filename - can't proceed with JavaScript code
// Display an eror or a message with instructions for user...
}
Note: Don't use the user-supplied filename to store the image! It opens you up to a directory traversal attack, and makes it possible for the user to write a file anywhere on your filesystem the web server has write-access to.
// This is unsafe!
move_uploaded_file($_FILES["fileImage"]["tmp_name"], "ImageFiles/" . $_FILES["fileImage"]["name"]);
Instead, it's common to store the value from $_FILES['fileImage']['name'] in your database, along with an identifier value for the actual file, and use the identifier to store it on disk.
$info = pathinfo($_FILES['fileImage']['name']);
// Get the original extension
$filext = $info['extension'];
// Make a unique filename and add the extension
$stored_filename = uniqid() . $filext;
// Use that to store the file on disk
move_uploaded_file($_FILES["fileImage"]["tmp_name"], $stored_filename);
// Now store BOTH $_FILES['fileImage']['name'] and $stored_filename in your database together
// The original user-supplied filename can be used for display, but isn't used on disk

Javascript & Multi File Upload Fields - Manipulating POST (File Input)

I am developing a php ajax-based application, and one of it's components is an image upload.
I know ajax is incapable of posting files, since it's Request based, but i did find a nifty iframe work around.
My goal is to send multiple files, but post them individually so i can process the upload and validate serversided, send the result, and then move on to the next file.
I would like to achieve this with a:
<input name="files[]" type="file" multiple=""/>
It all works all fine and dandy, but when it comes to sending them individually.. there doesn't teem to be a possible way.
My original idea was to treat the (element.files) as an array.. So i create duplicate form, and attempted to copy over the input to the other form (that was successfull)
but then when i tried to splice, or remove the unwanted elements so i can submit the form, i couldn't find a way to do it. Nothing worked...
Trying to alter the element.files.length to 1, didn't work
Trying to splice it didn't work..
Trying to just clear the indexes didn't work either...
If anyone could shed a green light, or just blast the ultra red light letting me know that this is not possible w/ an html input, it'll be greatly appreciated.
p.s: Flash isn't an option.
Here's some examples of my failed attempts:
<form name="image_upload" id="image_upload_form" action="image_uploader" method="POST" enctype="multipart/form-data">
<input name="userfile[]" id="filesToUpload" type="file" title="Select Your Files" multiple=""/> <br /><br />
<input type="button" id="imageUploadButton" value="Upload" onclick="valif('image_upload'); return false;" />
</form>
<!-- Hidden Form and File Input -->
<form name="single_image_upload" id="single_image_upload_form" action="image_uploader" method="POST" enctype="multipart/form-data">
<input name="userfile[]" id="fileToUpload" type="file" style="visibility: none; position: absolute; top: 0; left: 0;/>
</form>
<script type="text/javascript">
//This happens on submit
var multi_input = getElementById("image_upload");
var single_input = getElementById("single_image_upload");
single_input = multi_input;
//Assuming there are 2+ files chosen for upload, attempt to only upload the first one.
//Attempt 1
multi_input.files.length = 1;
//Attempt 2
multi_input.files.splice(1);
//Attempt 3
for (x = 1; x < multi_input.files.length; x++) { //skip 0, we wanna keep that one
multi_input.files[x] = null; // or ''.. same difference in this scneario.
}
</script>
Below is the actual code right now, working w/ 1 iframe.... This code works great, however it actually unfortunately sends every single file, every time.. And i just validate the right one on the serverside, but it's not practical, as i shouldn't have to send every single file all the time.
function ajaxUpload(form,url_action,id_element){
var detectWebKit = isWebKit();
form = typeof(form)=="string"?$m(form):form;
//Error Validator
var error="";
if (form==null || typeof(form)=="undefined"){
error += "Invalid Form.\n";
} else if(form.nodeName.toLowerCase()!="form"){
error += "Element Exists but it is not a form.\n";
}
if (id_element==null){
error += "The element of 3rd parameter does not exists.\n";
}
if (error.length>0){
alert("Error in image upload attempt:\n" + error);
return;
}
//Create Frame On The Fly
var iframe = document.createElement("iframe");
iframe.setAttribute("id","ajax-temp-"+rt_img);
iframe.setAttribute("name","ajax-temp-"+rt_img);
iframe.setAttribute("width","0");
iframe.setAttribute("height","0");
iframe.setAttribute("border","0");
iframe.setAttribute("style","width: 0; height: 0; border: none;");
form.parentNode.appendChild(iframe);
window.frames['ajax-temp-'+rt_img].name="ajax-temp-"+rt_img;
//Upload Image
var doUpload = function(){
removeEvent($m('ajax-temp-'+rt_img),"load", doUpload);
var cross = "javascript: ";
cross += "window.parent.upload_result(document.body.innerHTML); void(0);";
$m('ajax-temp-'+rt_img).src = cross;
if(detectWebKit){
remove($m('ajax-temp-'+rt_img));
}else{
setTimeout(function(){ remove($m('ajax-temp-'+rt_img))}, 250);
}
}
//Post Form
addEvent($m('ajax-temp-'+rt_img),"load", doUpload);
form.setAttribute("target","ajax-temp-"+rt_img);
form.setAttribute("action",url_action);
form.setAttribute("method","post");
form.setAttribute("enctype","multipart/form-data");
form.setAttribute("encoding","multipart/form-data");
form.submit();
}
function upload_run() {
var send_index = rt_img;
rt_img += 1;
var li = $('#fileList li:nth-child('+rt_img+')');
var filetype = li.html().substr(-3).toLowerCase();
var skip_mod = '';
//validate file type
if ((filetype != 'jpg') && (filetype != 'png') && (filetype != 'gif')) {
li.append(' - <b>invalid image file</b>');
} else {
li.append(' <img src="'+base_url+'sds_ajax/upload-ani.gif">');
//Initiate File Upload
ajaxUpload($m('image_upload_form'),base_url+'includes/core.php?post_action=image_upload&si='+send_index,li);
}
}
//Get the input and UL list
var button = document.getElementById('imageUploadButton');
var input = document.getElementById('filesToUpload');
var list = document.getElementById('fileList');
//Empty list for now
while (list.hasChildNodes()) {
list.removeChild(ul.firstChild);
}
//Populate List w/ files
rt_max = input.files.length;
for (var x = 0; x < input.files.length; x++) {
//add to list
var li = document.createElement('li');
li.innerHTML = 'Image ' + (x + 1) + ': ' + input.files[x].name;
list.appendChild(li);
}
//Run through created list
rt_img = 0;
upload_run();
//Disable Submit Button
button.disabled=true;
bump
It sounds like you need an iframe per file upload. So what I would do is have hidden fields for every other field in your normal form (in each iframe). Then when you click the submit button, you take all the visible fields and place the values in the corresponding hidden fields for each image. Then call post on each iframe now that they all have the data they need.

Send/receive data via jQuery to/from PHP

I am using this code to make the user input a name to create a folder. I have modified the code to try and send the form data via jQuery and receive the success/failure message from PHP through jQuery.
However, when I enter the name of the folder, nothing happens. No folder is created nor any error displayed. Firebug does not show any error either.
This is the code I have till now:
create.php:
<html>
<head><title>Make Directory</title></head>
<body>
<div id="albumform">
<form id="album_form" method="post" action="createAlbum.php" enctype="multipart/form-data">
<p id="message" style="display:none;">
<?php echo (isset($success)?"<h3>$success</h3>":""); ?>
<?php echo (isset($error)?'<span style="color:red;">' . $error . '</span>':''); ?>
</p>
<input type="text" id="create_album" name="create_album" value="" />
<input type="button" onclick="return checkForm('album_form');" id="btn_album" name="btn_album" value="Create" />
</form>
</div>
</body>
</html>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
<script type="text/javascript">
/* $("#btn_album").click(function() { */
function checkForm(form) {
//create post data
var postData = {
"create_album" : $("#create_album").val()
};
//make the call
$.ajax({
type: "POST",
url: "createAlbum.php",
data: postData, //send it along with your call
success: function(response){
$('#message').fadeIn();
}
});
/* }); */
}
</script>
createAlbum.php:
<?php
/**********************
File: createDir.php
Author: Frost
Website: http://www.slunked.com
***********************/
// set our absolute path to the directories will be created in:
$path = $_SERVER['DOCUMENT_ROOT'] . '/web/photos/images/';
if (isset($_POST['btn_album'])) {
// Grab our form Data
$dirName = isset($_POST['create_album'])?$_POST['create_album']:false;
// first validate the value:
if ($dirName !== false && preg_match('~([^A-Z0-9]+)~i', $dirName, $matches) === 0) {
// We have a valid directory:
if (!is_dir($path . $dirName)) {
// We are good to create this directory:
if (mkdir($path . $dirName, 0775)) {
$success = "Your directory has been created succesfully!<br /><br />";
}else {
$error = "Unable to create dir {$dirName}.";
}
}else {
$error = "Directory {$dirName} already exists.";
}
}else {
// Invalid data, htmlenttie them incase < > were used.
$dirName = htmlentities($dirName);
$error = "You have invalid values in {$dirName}.";
}
}
?>
There are at least two seperate problems with your code:
In the php-file, you check if $_POST['btn_album'] is set. This field is not sent as it is not part of your ajax-request (You're only sending "create_album" : $("#create_album").val()). So the code that creates the folder is never executed.
Another problem is the part
<?php echo (isset($success)?"<h3>$success</h3>":""); ?>
<?php echo (isset($error)?'<span style="color:red;">' . $error . '</span>':''); ?>
in your response-message. This code is evaluated when the page loads, not during your ajax-request, so the php-variables $success and $error will always be undefined. You have to return those response-messages as response to the actual request and then use javascript to display them.
The ajax request has a bad habit of failing silently.
You should use jQuery post and take advantage of .success(), .complete(), and .error() functions to track your code.
Also use the console.log() to check if the parameters are sent corectly. I'll try out the code myself to see the problem.
http://api.jquery.com/jQuery.post/
Due to the nature of the $.ajax request, $_POST['btn_album'] is not sent. So your php file gets here
if (isset($_POST['btn_album'])) {
and returns false.
also you need to echo $error to get a response.

Categories