/** * Process the 'upload' event * * Process and store the uploaded file in the designated location. * Images will be resized when possible and applicable. A thumbnail image will also * be preproduced when possible. * Return a JSON encoded status of success or failure. * * Expected parameters: * * $_POST['directory'] path relative to basedir a.k.a. options['URLpath4FileManagedDirTree'] root * * $_POST['resize'] nonzero value indicates any uploaded image should be resized to the configured * options['maxImageDimension'] width and height whenever possible * * $_POST['filter'] optional mimetype filter string, amy be the part up to and * including the slash '/' or the full mimetype. Only files * matching this (set of) mimetypes will be listed. * Examples: 'image/' or 'application/zip' * * $_FILES[] the metadata for the uploaded file * * $_POST['reportContentType'] * if you want a specific content type header set on our response, put it here. * This is needed for when we are posting an upload response to a hidden iframe, the * default application/json mimetype breaks down in that case at least for Firefox 3.X * as the browser will pop up a save/view dialog before JS can access the transmitted data. * * Errors will produce a JSON encoded error report, including at least two fields: * * status 0 for error; nonzero for success * * error error message */ protected function onUpload() { $emsg = null; $file_arg = null; $file = null; $legal_dir_url = null; $jserr = array('status' => 1); try { if (!$this->options['upload']) { throw new FileManagerException('disabled:upload'); } // MAY upload zero length files! if (!isset($_FILES) || empty($_FILES['Filedata']) || empty($_FILES['Filedata']['name'])) { throw new FileManagerException('nofile'); } $v_ex_code = 'nofile'; $file_size = empty($_FILES['Filedata']['size']) ? 0 : $_FILES['Filedata']['size']; $file_arg = $_FILES['Filedata']['name']; $dir_arg = $this->getPOSTparam('directory'); $legal_dir_url = $this->rel2abs_legal_url_path($dir_arg . '/'); // must transform here so alias/etc. expansions inside legal_url_path2file_path() get a chance: $dir = $this->legal_url_path2file_path($legal_dir_url); $mime_filter = $this->getPOSTparam('filter', $this->options['filter']); $mime_filters = $this->getAllowedMimeTypes($mime_filter); $tmppath = $_FILES['Filedata']['tmp_name']; $resize_imgs = $this->getPOSTparam('resize', 0); $filename = null; $fi = array('filename' => null, 'extension' => null); $mime = null; $meta = null; if (!empty($file_arg)) { if (!$this->IsHiddenNameAllowed($file_arg)) { $v_ex_code = 'fmt_not_allowed'; } else { $filename = $this->getUniqueName($file_arg, $dir); if ($filename !== null) { /* * Security: * * Upload::move() processes the unfiltered version of $_FILES[]['name'], at least to get the extension, * unless we ALWAYS override the filename and extension in the options array below. That's why we * calculate the extension at all times here. */ if ($this->options['safe']) { $fi = pathinfo($filename); $fi['extension'] = $this->getSafeExtension(isset($fi['extension']) ? $fi['extension'] : ''); $filename = $fi['filename'] . (isset($fi['extension']) && strlen($fi['extension']) > 0 ? '.' . $fi['extension'] : ''); } $legal_url = $legal_dir_url . $filename; // UPLOAD delivers files in temporary storage with extensions NOT matching the mime type, so we don't // filter on extension; we just let getID3 go ahead and content-sniff the mime type. // Since getID3::analyze() is a quite costly operation, we like to do it only ONCE per file, // so we cache the last entries. $meta = $this->getFileInfo($tmppath, $legal_url); $mime = $meta->getMimeType(); if (!$this->IsAllowedMimeType($mime, $mime_filters)) { $v_ex_code = 'extension'; } else { $v_ex_code = null; } } } } $fileinfo = array('legal_dir_url' => $legal_dir_url, 'dir' => $dir, 'raw_filename' => $file_arg, 'filename' => $filename, 'meta_data' => $meta, 'mime' => $mime, 'mime_filter' => $mime_filter, 'mime_filters' => $mime_filters, 'tmp_filepath' => $tmppath, 'size' => $file_size, 'maxsize' => $this->options['maxUploadSize'], 'overwrite' => false, 'resize' => $resize_imgs, 'chmod' => $this->options['chmod'] & 0666, 'preliminary_json' => $jserr, 'validation_failure' => $v_ex_code); if (!empty($this->options['UploadIsAuthorized_cb']) && function_exists($this->options['UploadIsAuthorized_cb']) && !$this->options['UploadIsAuthorized_cb']($this, 'upload', $fileinfo)) { $v_ex_code = $fileinfo['validation_failure']; if (empty($v_ex_code)) { $v_ex_code = 'authorized'; } } if (!empty($v_ex_code)) { throw new FileManagerException($v_ex_code); } $legal_dir_url = $fileinfo['legal_dir_url']; $dir = $fileinfo['dir']; $file_arg = $fileinfo['raw_filename']; $filename = $fileinfo['filename']; $meta = $fileinfo['meta_data']; $mime = $fileinfo['mime']; $mime_filter = $fileinfo['mime_filter']; $mime_filters = $fileinfo['mime_filters']; //$tmppath = $fileinfo['tmp_filepath']; $resize_imgs = $fileinfo['resize']; $jserr = $fileinfo['preliminary_json']; if ($fileinfo['maxsize'] && $fileinfo['size'] > $fileinfo['maxsize']) { throw new FileManagerException('size'); } //if (!isset($fileinfo['extension'])) // throw new FileManagerException('extension'); // Creates safe file names if ($this->options['cleanFileName']) { $filename = FileManagerUtility::cleanUrl($filename, array(), '_'); } // must transform here so alias/etc. expansions inside legal_url_path2file_path() get a chance: $legal_url = $legal_dir_url . $filename; $file = $this->legal_url_path2file_path($legal_url); if (!$fileinfo['overwrite'] && file_exists($file)) { throw new FileManagerException('exists'); } if (!@move_uploaded_file($_FILES['Filedata']['tmp_name'], $file)) { $emsg = 'path'; switch ($_FILES['Filedata']['error']) { case 1: case 2: $emsg = 'size'; break; case 3: $emsg = 'partial'; break; default: $dir = $this->legal_url_path2file_path($legal_dir_url); if (!is_dir($dir)) { $emsg = 'path'; } else { if (!is_writable($dir)) { $emsg = 'path_not_writable'; } else { $emsg = 'filename_maybe_too_large'; } } if (!empty($_FILES['Filedata']['error'])) { $emsg .= ': error code = ' . strtolower($_FILES['Filedata']['error']) . ', ' . $emsg_add; } break; } throw new FileManagerException($emsg); } @chmod($file, $fileinfo['chmod']); /* * NOTE: you /can/ (and should be able to, IMHO) upload 'overly large' image files to your site, but the resizing process step * happening here will fail; we have memory usage estimators in place to make the fatal crash a non-silent one, i,e, one * where we still have a very high probability of NOT fatally crashing the PHP iunterpreter but catching a suitable exception * instead. * Having uploaded such huge images, a developer/somebody can always go in later and up the memory limit if the site admins * feel it is deserved. Until then, no thumbnails of such images (though you /should/ be able to milkbox-view the real thing!) */ $thumb250 = false; $thumb250_e = false; $thumb48 = false; $thumb48_e = false; if (FileManagerUtility::startsWith($mime, 'image/')) { if (!empty($resize_imgs)) { $img = new Image($file); $size = $img->getSize(); // Image::resize() takes care to maintain the proper aspect ratio, so this is easy // (default quality is 100% for JPEG so we get the cleanest resized images here) $img->resize($this->options['maxImageDimension']['width'], $this->options['maxImageDimension']['height'])->save(); unset($img); // source image has changed: nuke the cached metadata and then refetch the metadata = forced refetch $meta = $this->getFileInfo($file, $legal_url, true); } } /* * 'abuse' the info extraction process to generate the thumbnails. Besides, doing it this way will also prime the metadata cache for this item, * so we'll have a very fast performance viewing this file's details and thumbnails both from this point forward! */ $jsbogus = array('status' => 1); $jsbogus = $this->extractDetailInfo($jsbogus, $legal_url, $meta, $mime_filter, $mime_filters, array('direct')); $this->sendHttpHeaders('Content-Type: ' . $this->getPOSTparam('reportContentType', 'application/json')); echo json_encode(array('status' => 1, 'name' => basename($file))); return; } catch (FileManagerException $e) { $emsg = $e->getMessage(); } catch (Exception $e) { // catching other severe failures; since this can be anything and should only happen in the direst of circumstances, we don't bother translating $emsg = $e->getMessage(); } $this->modify_json4exception($jserr, $emsg, 'file = ' . $this->mkSafe4Display($file_arg . ', destination path = ' . $file . ', target directory (URI path) = ' . $legal_dir_url)); $this->sendHttpHeaders('Content-Type: ' . $this->getPOSTparam('reportContentType', 'application/json')); // when we fail here, it's pretty darn bad and nothing to it. // just push the error JSON and go. echo json_encode(array_merge($jserr, $_FILES)); }