[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Fmsystem-commits] [6643] property: Fileuploader
From: |
Sigurd Nes |
Subject: |
[Fmsystem-commits] [6643] property: Fileuploader |
Date: |
Thu, 25 Nov 2010 22:50:42 +0000 |
Revision: 6643
http://svn.sv.gnu.org/viewvc/?view=rev&root=fmsystem&revision=6643
Author: sigurdne
Date: 2010-11-25 22:50:42 +0000 (Thu, 25 Nov 2010)
Log Message:
-----------
property: Fileuploader
Modified Paths:
--------------
trunk/property/inc/class.fileuploader.inc.php
trunk/property/inc/class.uientity.inc.php
trunk/property/js/yahoo/entity.edit.js
Modified: trunk/property/inc/class.fileuploader.inc.php
===================================================================
--- trunk/property/inc/class.fileuploader.inc.php 2010-11-25 17:12:15 UTC
(rev 6642)
+++ trunk/property/inc/class.fileuploader.inc.php 2010-11-25 22:50:42 UTC
(rev 6643)
@@ -24,7 +24,7 @@
* @internal Development of this application was funded by
http://www.bergen.kommune.no/bbb_/ekstern/
* @package property
* @subpackage location
- * @version $Id: class.uilocation.inc.php 5083 2010-03-19 14:29:26Z
sigurd $
+ * @version $Id: class.fileuploader.inc.php 5083 2010-03-19 14:29:26Z
sigurd $
*/
/**
@@ -43,7 +43,7 @@
function __construct()
{
- $GLOBALS['phpgw_info']['flags']['xslt_app']
= true;
+ $GLOBALS['phpgw_info']['flags']['xslt_app']
= false;
$GLOBALS['phpgw_info']['flags']['noframework']
= true;
$GLOBALS['phpgw_info']['flags']['no_reset_fonts']
= true;
}
@@ -63,6 +63,15 @@
'domain' =>
phpgw::get_var('domain')
);
+ $oArgs = "{menuaction:'$upload_target',"
+ ."id:'$id',"
+ ."last_loginid:'".
phpgw::get_var('last_loginid')."',"
+ ."last_domain:'" .
phpgw::get_var('last_domain')."',"
+ ."sessionphpgwsessid:'" .
phpgw::get_var('sessionphpgwsessid')."',"
+ ."domain:'" . phpgw::get_var('domain')."'}";
+
+
+
foreach ($_GET as $varname => $value)
{
if(strpos($varname, '_')===0)
@@ -73,8 +82,48 @@
$upload_url = $GLOBALS['phpgw']->link('/index.php',
$link_data);
- $js_code = self::get_js($upload_url);
-
+ $js_code = self::get_js($oArgs);
+
+ $title = lang('fileuploader');
+ $html = <<<HTML
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <title>{$title}</title>
+ <link
href="{$GLOBALS['phpgw_info']['server']['webserver_url']}/phpgwapi/js/swfupload/default.css"
rel="stylesheet" type="text/css" />
+ <script type="text/javascript"
src="{$GLOBALS['phpgw_info']['server']['webserver_url']}/phpgwapi/js/core/base.js"></script>
+ <script type="text/javascript"
src="{$GLOBALS['phpgw_info']['server']['webserver_url']}/phpgwapi/js/swfupload/swfupload.js"></script>
+ <script type="text/javascript"
src="{$GLOBALS['phpgw_info']['server']['webserver_url']}/phpgwapi/js/swfupload/swfupload.queue.js"></script>
+ <script type="text/javascript"
src="{$GLOBALS['phpgw_info']['server']['webserver_url']}/phpgwapi/js/swfupload/fileprogress.js"></script>
+ <script type="text/javascript"
src="{$GLOBALS['phpgw_info']['server']['webserver_url']}/phpgwapi/js/swfupload/handlers.js"></script>
+ $js_code
+ </head>
+ <body>
+
+ <div id="content">
+ <h2>{$title}</h2>
+ <form id="form1" action="index.php"
method="post" enctype="multipart/form-data">
+
+ <div class="fieldset
flash" id="fsUploadProgress">
+ <span
class="legend">Upload Queue</span>
+ </div>
+ <div id="divStatus">0 Files
Uploaded</div>
+ <div>
+ <span
id="spanButtonPlaceHolder"></span>
+ <input
id="btnCancel" type="button" value="Cancel All Uploads"
onclick="swfu.cancelQueue();" disabled="disabled" style="margin-left: 2px;
font-size: 8pt; height: 29px;" />
+ </div>
+ </form>
+ </div>
+ </body>
+ </html>
+HTML;
+
+
+
+
+ echo $html;
+
+/*
$GLOBALS['phpgw']->css->add_external_file('phpgwapi/js/yahoo/datatable/assets/skins/sam/datatable.css');
$GLOBALS['phpgw']->css->add_external_file('phpgwapi/js/yahoo/fonts/fonts-min.css');
phpgwapi_yui::load_widget('uploader');
@@ -85,175 +134,250 @@
'js_code' => $js_code,
);
$GLOBALS['phpgw']->xslttpl->set_var('phpgw',
array('fileuploader' => $data));
+*/
}
- static function get_js($upload_url = '')
+ static function get_js($oArgs = '')
{
+ $button_text = lang('Select Files');
+ $str_base_url = $GLOBALS['phpgw']->link('/', array(),
true);
$js_code = <<<JS
- YAHOO.util.Event.onDOMReady(function () {
- var uiLayer = YAHOO.util.Dom.getRegion('selectLink');
- var overlay = YAHOO.util.Dom.get('uploaderOverlay');
- YAHOO.util.Dom.setStyle(overlay, 'width',
uiLayer.right-uiLayer.left + "px");
- YAHOO.util.Dom.setStyle(overlay, 'height',
uiLayer.bottom-uiLayer.top + "px");
- });
+<script type="text/javascript">
+ var swfu;
+ var strBaseURL = '$str_base_url';
- // Custom URL for the uploader swf file (same folder).
+ var sUrl = phpGWLink('index.php', $oArgs);
- YAHOO.widget.Uploader.SWFURL =
"{$GLOBALS['phpgw_info']['server']['webserver_url']}/phpgwapi/js/yahoo/uploader/assets/uploader.swf";
- // Instantiate the uploader and write it to its
placeholder div.
- var uploader = new YAHOO.widget.Uploader(
"uploaderOverlay" );
-
- // Add event listeners to various events on the
uploader.
- // Methods on the uploader should only be called once
the
- // contentReady event has fired.
+ window.onload = function() {
+ var settings = {
+ flash_url :
"{$GLOBALS['phpgw_info']['server']['webserver_url']}/phpgwapi/js/swfupload/swfupload.swf",
+ flash9_url :
"{$GLOBALS['phpgw_info']['server']['webserver_url']}/phpgwapi/js/swfupload/swfupload_fp9.swf",
+ upload_url: sUrl,
+// post_params: {"PHPSESSID" : "<?php echo
session_id(); ?>"},
+ file_size_limit : "100 MB",
+ file_types : "*.*",
+ file_types_description : "All Files",
+ file_upload_limit : 100,
+ file_queue_limit : 0,
+ custom_settings : {
+ progressTarget : "fsUploadProgress",
+ cancelButtonId : "btnCancel"
+ },
+ debug: false,
+
+ // Button settings
+ button_image_url:
"images/TestImageNoText_65x29.png",
+ button_width: "65",
+ button_height: "29",
+ button_placeholder_id: "spanButtonPlaceHolder",
+ button_text: '<span
class="theFont">{$button_text}</span>',
+ button_text_style: ".theFont { font-size: 16;
}",
+ button_text_left_padding: 12,
+ button_text_top_padding: 3,
+
+ // The event handler functions are defined in
handlers.js
+ swfupload_preload_handler : preLoad,
+ swfupload_load_failed_handler : loadFailed,
+ file_queued_handler : fileQueued,
+ file_queue_error_handler : fileQueueError,
+ file_dialog_complete_handler :
fileDialogComplete,
+ upload_start_handler : uploadStart,
+ upload_progress_handler : uploadProgress,
+ upload_error_handler : uploadError,
+ upload_success_handler : uploadSuccess,
+ upload_complete_handler : uploadComplete,
+ queue_complete_handler : queueComplete //
Queue plugin event
+ };
+
+ swfu = new SWFUpload(settings);
+ };
+ </script>
+JS;
+ return $js_code;
+ }
+
+
+/*
+This is an upload script for SWFUpload that attempts to properly handle
uploaded files
+in a secure way.
+
+Notes:
- uploader.addListener('contentReady',
handleContentReady);
- uploader.addListener('fileSelect', onFileSelect)
- uploader.addListener('uploadStart', onUploadStart);
- uploader.addListener('uploadProgress',
onUploadProgress);
- uploader.addListener('uploadCancel', onUploadCancel);
- uploader.addListener('uploadComplete',
onUploadComplete);
- uploader.addListener('uploadCompleteData',
onUploadResponse);
- uploader.addListener('uploadError', onUploadError);
- uploader.addListener('rollOver', handleRollOver);
- uploader.addListener('rollOut', handleRollOut);
- uploader.addListener('click', handleClick);
-
- // Variable for holding the filelist.
- var fileList;
+ SWFUpload doesn't send a MIME-TYPE. In my opinion this is ok since
MIME-TYPE is no better than
+ file extension and is probably worse because it can vary from OS to OS
and browser to browser (for the same file).
+ The best thing to do is content sniff the file but this can be
resource intensive, is difficult, and can still be fooled or inaccurate.
+ Accepting uploads can never be 100% secure.
+
+ You can't guarantee that SWFUpload is really the source of the upload.
A malicious user
+ will probably be uploading from a tool that sends invalid or false
metadata about the file.
+ The script should properly handle this.
+
+ The script should not over-write existing files.
- // When the mouse rolls over the uploader, this function
- // is called in response to the rollOver event.
- // It changes the appearance of the UI element below
the Flash overlay.
- function handleRollOver () {
-
YAHOO.util.Dom.setStyle(YAHOO.util.Dom.get('selectLink'), 'color', "#FFFFFF");
-
YAHOO.util.Dom.setStyle(YAHOO.util.Dom.get('selectLink'), 'background-color',
"#000000");
- }
+ The script should strip away invalid characters from the file name or
reject the file.
+
+ The script should not allow files to be saved that could then be
executed on the webserver (such as .php files).
+ To keep things simple we will use an extension whitelist for allowed
file extensions. Which files should be allowed
+ depends on your server configuration. The extension white-list is
_not_ tied your SWFUpload file_types setting
+
+ For better security uploaded files should be stored outside the
webserver's document root. Downloaded files
+ should be accessed via a download script that proxies from the file
system to the webserver. This prevents
+ users from executing malicious uploaded files. It also gives the
developer control over the outgoing mime-type,
+ access restrictions, etc. This, however, is outside the scope of this
script.
+
+ SWFUpload sends each file as a separate POST rather than several files
in a single post. This is a better
+ method in my opinion since it better handles file size limits, e.g.,
if post_max_size is 100 MB and I post two 60 MB files then
+ the post would fail (2x60MB = 120MB). In SWFupload each 60 MB is
posted as separate post and we stay within the limits. This
+ also simplifies the upload script since we only have to handle a
single file.
+
+ The script should properly handle situations where the post was too
large or the posted file is larger than
+ our defined max. These values are not tied to your SWFUpload
file_size_limit setting.
+
+*/
- // On rollOut event, this function is called, which
changes the appearance of the
- // UI element below the Flash layer back to its
original state.
- function handleRollOut () {
-
YAHOO.util.Dom.setStyle(YAHOO.util.Dom.get('selectLink'), 'color', "#0000CC");
-
YAHOO.util.Dom.setStyle(YAHOO.util.Dom.get('selectLink'), 'background-color',
"#FFFFFF");
- }
+ function upload()
+ {
+ // Check post_max_size
(http://us3.php.net/manual/en/features.file-upload.php#73762)
+ $POST_MAX_SIZE = ini_get('post_max_size');
+ $unit = strtoupper(substr($POST_MAX_SIZE, -1));
+ $multiplier = ($unit == 'M' ? 1048576 : ($unit == 'K' ?
1024 : ($unit == 'G' ? 1073741824 : 1)));
- // When the Flash layer is clicked, the "Browse" dialog
is invoked.
- // The click event handler allows you to do something
else if you need to.
- function handleClick () {
+ if ((int)$_SERVER['CONTENT_LENGTH'] >
$multiplier*(int)$POST_MAX_SIZE && $POST_MAX_SIZE)
+ {
+ header("HTTP/1.1 500 Internal Server Error");
// This will trigger an uploadError event in SWFUpload
+ echo "POST exceeded maximum allowed size.";
+ exit(0);
}
- // When contentReady event is fired, you can call
methods on the uploader.
- function handleContentReady () {
- // Allows the uploader to send log messages to
trace, as well as to YAHOO.log
- uploader.setAllowLogging(true);
+ // Settings
+ $save_path = dirname(__FILENAME__) . "/uploads/";
// The path were we will save the file (getcwd() may not be reliable and should
be tested in your environment)
+ $save_path =
"{$GLOBALS['phpgw_info']['server']['temp_dir']}/";
+ $upload_name = "Filedata";
+ $max_file_size_in_bytes = 2147483647;
// 2GB in bytes
+ $extension_whitelist = array("jpg", "gif", "png");
// Allowed file extensions
+ $valid_chars_regex = '.A-Z0-9_
address@hidden&()+={}\[\]\',~`-'; // Characters
allowed in the file name (in a Regular Expression format)
+
+ // Other variables
+ $MAX_FILENAME_LENGTH = 260;
+ $file_name = "";
+ $file_extension = "";
+ $uploadErrors = array
+ (
+ 0=>"There is no error, the file uploaded successfully",
+ 1=>"The uploaded file exceeds the upload_max_filesize
directive in php.ini",
+ 2=>"The uploaded file exceeds the MAX_FILE_SIZE
directive that was specified in the HTML form",
+ 3=>"The uploaded file was only partially uploaded",
+ 4=>"No file was uploaded",
+ 6=>"Missing a temporary folder"
+ );
- // Allows multiple file selection in "Browse"
dialog.
- uploader.setAllowMultipleFiles(true);
- // New set of file filters.
- var ff = new Array({description:"Images",
extensions:"*.jpg;*.png;*.gif"},
- {description:"Videos",
extensions:"*.avi;*.mov;*.mpg"});
-
- // Apply new set of file filters to the
uploader.
-// uploader.setFileFilters(ff);
+ // Validate the upload
+ if (!isset($_FILES[$upload_name]))
+ {
+ $this->HandleError("No upload found in \$_FILES
for " . $upload_name);
+ exit(0);
}
-
- // Actually uploads the files. In this case,
- // uploadAll() is used for automated queueing and
upload
- // of all files on the list.
- // You can manage the queue on your own and use
"upload" instead,
- // if you need to modify the properties of the request
for each
- // individual file.
- function upload() {
- if (fileList != null) {
-//
uploader.setSimUploadLimit(parseInt(document.getElementById("simulUploads").value));
- uploader.setSimUploadLimit(1);
- uploader.uploadAll("{$upload_url}",
"POST", null, "Filedata");
- }
+ else if (isset($_FILES[$upload_name]["error"]) &&
$_FILES[$upload_name]["error"] != 0)
+ {
+
$this->HandleError($uploadErrors[$_FILES[$upload_name]["error"]]);
+ exit(0);
}
+ else if (!isset($_FILES[$upload_name]["tmp_name"]) ||
address@hidden($_FILES[$upload_name]["tmp_name"]))
+ {
+ $this->HandleError("Upload failed
is_uploaded_file test.");
+ exit(0);
+ }
+ else if (!isset($_FILES[$upload_name]['name']))
+ {
+ $this->HandleError("File has no name.");
+ exit(0);
+ }
+
+ // Validate the file size (Warning: the largest files supported
by this code is 2GB)
+ $file_size =
@filesize($_FILES[$upload_name]["tmp_name"]);
+ if (!$file_size || $file_size > $max_file_size_in_bytes)
+ {
+ $this->HandleError("File exceeds the maximum
allowed size");
+ exit(0);
+ }
+
+ if ($file_size <= 0)
+ {
+ $this->HandleError("File size outside allowed
lower bound");
+ exit(0);
+ }
- // Fired when the user selects files in the "Browse"
dialog
- // and clicks "Ok".
- function onFileSelect(event) {
- if('fileList' in event && event.fileList !=
null) {
- fileList = event.fileList;
- createDataTable(fileList);
- }
+ // Validate file name (for our purposes we'll just remove
invalid characters)
+ $file_name =
preg_replace('/[^'.$valid_chars_regex.']|\.+$/i', "",
basename($_FILES[$upload_name]['name']));
+ if (strlen($file_name) == 0 || strlen($file_name) >
$MAX_FILENAME_LENGTH)
+ {
+ $this->HandleError("Invalid file name");
+ exit(0);
}
- function createDataTable(entries) {
- rowCounter = 0;
- this.fileIdHash = {};
- this.dataArr = [];
- for(var i in entries) {
- var entry = entries[i];
- entry["progress"] = "<div
style='height:5px;width:100px;background-color:#CCC;'></div>";
- dataArr.unshift(entry);
- }
- for (var j = 0; j < dataArr.length; j++) {
- this.fileIdHash[dataArr[j].id] = j;
- }
-
- var myColumnDefs = [
- {key:"name", label: "File Name",
sortable:false},
- {key:"size", label: "Size", sortable:false},
- {key:"progress", label: "Upload progress",
sortable:false}
- ];
-
- this.myDataSource = new YAHOO.util.DataSource(dataArr);
- this.myDataSource.responseType =
YAHOO.util.DataSource.TYPE_JSARRAY;
- this.myDataSource.responseSchema = {
- fields: ["id","name","created","modified","type",
"size", "progress"]
- };
-
- this.singleSelectDataTable = new
YAHOO.widget.DataTable("dataTableContainer",
- myColumnDefs, this.myDataSource, {
- caption:"Files To Upload",
- selectionMode:"single"
- });
+ // Validate that we won't over-write an existing file
+ if (file_exists($save_path . $file_name))
+ {
+ $this->HandleError("File with this name already
exists");
+ exit(0);
}
- // Do something on each file's upload start.
- function onUploadStart(event) {
-
+ // Validate file extension
+ $path_info = pathinfo($_FILES[$upload_name]['name']);
+ $file_extension = $path_info["extension"];
+ $is_valid_extension = false;
+ foreach ($extension_whitelist as $extension)
+ {
+ if (strcasecmp($file_extension, $extension) ==
0)
+ {
+ $is_valid_extension = true;
+ break;
+ }
}
-
- // Do something on each file's upload progress event.
- function onUploadProgress(event) {
- rowNum = fileIdHash[event["id"]];
- prog =
Math.round(100*(event["bytesLoaded"]/event["bytesTotal"]));
- progbar = "<div
style='height:5px;width:100px;background-color:#CCC;'><div
style='height:5px;background-color:#F00;width:" + prog + "px;'></div></div>";
- singleSelectDataTable.updateRow(rowNum, {name:
dataArr[rowNum]["name"], size: dataArr[rowNum]["size"], progress: progbar});
+ if (!$is_valid_extension)
+ {
+ $this->HandleError("Invalid file extension");
+ exit(0);
}
- // Do something when each file's upload is complete.
- function onUploadComplete(event) {
- rowNum = fileIdHash[event["id"]];
- prog =
Math.round(100*(event["bytesLoaded"]/event["bytesTotal"]));
- progbar = "<div
style='height:5px;width:100px;background-color:#CCC;'><div
style='height:5px;background-color:#F00;width:100px;'></div></div>";
- singleSelectDataTable.updateRow(rowNum, {name:
dataArr[rowNum]["name"], size: dataArr[rowNum]["size"], progress: progbar});
- }
+ // Validate file contents (extension and mime-type can't be
trusted)
+ /*
+ Validating the file contents is OS and web
server configuration dependant. Also, it may not be reliable.
+ See the comments on this page:
http://us2.php.net/fileinfo
+
+ Also see
http://72.14.253.104/search?q=cache:3YGZfcnKDrYJ:www.scanit.be/uploads/php-file-upload.pdf+php+file+command&hl=en&ct=clnk&cd=8&gl=us&client=firefox-a
+ which describes how a PHP script can be
embedded within a GIF image file.
+
+ Therefore, no sample code will be provided
here. Research the issue, decide how much security is
+ needed, and implement a solution that meets the
need.
+ */
- // Do something if a file upload throws an error.
- // (When uploadAll() is used, the Uploader will
- // attempt to continue uploading.
- function onUploadError(event) {
- console.log(event);
- }
- // Do something if an upload is cancelled.
- function onUploadCancel(event) {
-
+ // Process the file
+ /*
+ At this point we are ready to process the valid
file. This sample code shows how to save the file. Other tasks
+ could be done such as creating an entry in a
database or generating a thumbnail.
+
+ Depending on your server OS and needs you may
need to set the Security Permissions on the file after it has
+ been saved.
+ */
+ if (address@hidden($_FILES[$upload_name]["tmp_name"],
$save_path.$file_name))
+ {
+ $this->HandleError("File could not be saved.");
+ exit(0);
}
- // Do something when data is received back from the
server.
- function onUploadResponse(event) {
+ exit(0);
+ }
- }
-JS;
- return $js_code;
+ /* Handles the error output. This error message will be sent to
the uploadSuccess event handler. The event handler
+ will have to check for any error messages and react as needed.
*/
+ function HandleError($message)
+ {
+ echo $message;
}
}
Modified: trunk/property/inc/class.uientity.inc.php
===================================================================
--- trunk/property/inc/class.uientity.inc.php 2010-11-25 17:12:15 UTC (rev
6642)
+++ trunk/property/inc/class.uientity.inc.php 2010-11-25 22:50:42 UTC (rev
6643)
@@ -158,6 +158,9 @@
$id = phpgw::get_var('id',
'int');
$jasperfile = phpgw::get_var('jasperfile',
'bool');
+ $fileuploader = CreateObject('property.fileuploader');
+ $fileuploader->upload();
+
if(!$this->acl_add && !$this->acl_edit)
{
$GLOBALS['phpgw']->common->phpgw_exit();
Modified: trunk/property/js/yahoo/entity.edit.js
===================================================================
--- trunk/property/js/yahoo/entity.edit.js 2010-11-25 17:12:15 UTC (rev
6642)
+++ trunk/property/js/yahoo/entity.edit.js 2010-11-25 22:50:42 UTC (rev
6643)
@@ -23,7 +23,7 @@
var frame = document.createElement('iframe');
frame.src = sUrl;
frame.width = "100%";
- frame.height = "350";
+ frame.height = "600";
o.setBody(frame);
};
lightbox.showEvent.subscribe(onDialogShow, lightbox);
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Fmsystem-commits] [6643] property: Fileuploader,
Sigurd Nes <=