/** * metaWeblog.newMediaObject image upload * wp.uploadFile * * Supplied image is encoded into the struct as bits * * @see http://www.xmlrpc.com/metaWeblogApi#metaweblognewmediaobject * @see http://codex.wordpress.org/XML-RPC_wp#wp.uploadFile * * @param xmlrpcmsg XML-RPC Message * 0 blogid (string): Unique identifier of the blog the post will be added to. * Currently ignored in b2evo, in favor of the category. * 1 username (string): Login for a Blogger user who has permission to edit the given * post (either the user who originally created it or an admin of the blog). * 2 password (string): Password for said username. * 3 struct (struct) * - name : filename * - type : mimetype * - bits : base64 encoded file * @return xmlrpcresp XML-RPC Response */ function _wp_mw_newmediaobject($m) { global $Settings, $Plugins, $force_upload_forbiddenext; // CHECK LOGIN: /** * @var User */ if (!($current_User =& xmlrpcs_login($m, 1, 2))) { // Login failed, return (last) error: return xmlrpcs_resperror(); } // GET BLOG: /** * @var Blog */ if (!($Blog =& xmlrpcs_get_Blog($m, 0))) { // Login failed, return (last) error: return xmlrpcs_resperror(); } // CHECK PERMISSION: if (!$current_User->check_perm('files', 'add', false, $Blog->ID)) { // Permission denied return xmlrpcs_resperror(3); // User error 3 } logIO('Permission granted.'); if (!$Settings->get('upload_enabled')) { return xmlrpcs_resperror(2, 'Object upload not allowed'); } $xcontent = $m->getParam(3); // Get the main data - and decode it properly for the image - sorry, binary object logIO('Decoding content...'); $contentstruct = xmlrpc_decode_recurse($xcontent); $data = $contentstruct['bits']; $file_mimetype = isset($contentstruct['type']) ? $contentstruct['type'] : '(none)'; logIO('Received MIME type: ' . $file_mimetype); $overwrite = false; if (isset($contentstruct['overwrite'])) { $overwrite = (bool) $contentstruct['overwrite']; } logIO('Overwrite if exists: ' . ($overwrite ? 'yes' : 'no')); load_funcs('files/model/_file.funcs.php'); $filesize = evo_bytes($data); if (($maxfilesize = $Settings->get('upload_maxkb') * 1024) && $filesize > $maxfilesize) { return xmlrpcs_resperror(4, sprintf(T_('The file is too large: %s but the maximum allowed is %s.'), bytesreadable($filesize, false), bytesreadable($maxfilesize, false))); } logIO('File size is OK: ' . bytesreadable($filesize, false)); $FileRootCache =& get_FileRootCache(); $fm_FileRoot =& $FileRootCache->get_by_type_and_ID('collection', $Blog->ID, true); if (!$fm_FileRoot) { // fileRoot not found: return xmlrpcs_resperror(14, 'File root not found'); } $rf_filepath = $contentstruct['name']; logIO('Received filepath: ' . $rf_filepath); // Split into path + name: $filepath_parts = explode('/', $rf_filepath); $filename = array_pop($filepath_parts); logIO('Original file name: ' . $filename); // Validate and sanitize filename if ($error_filename = process_filename($filename, true)) { return xmlrpcs_resperror(5, $error_filename); } logIO('Sanitized file name: ' . $filename); // Check valid path parts: $rds_subpath = ''; foreach ($filepath_parts as $filepath_part) { if (empty($filepath_part) || $filepath_part == '.') { // self ref not useful continue; } if ($error = validate_dirname($filepath_part)) { // invalid relative path: logIO($error); return xmlrpcs_resperror(6, $error); } $rds_subpath .= $filepath_part . '/'; } logIO('Subpath: ' . $rds_subpath); // Create temporary file and insert contents into it. $tmpfile_name = tempnam(sys_get_temp_dir(), 'fmupload'); if ($tmpfile_name) { if (save_to_file($data, $tmpfile_name, 'wb')) { $image_info = @getimagesize($tmpfile_name); } else { return xmlrpcs_resperror(13, 'Error while writing to temp file.'); } } if (!empty($image_info)) { // This is an image file, let's check mimetype and correct extension if ($image_info['mime'] != $file_mimetype) { // Invalid file type $FiletypeCache =& get_FiletypeCache(); // Get correct file type based on mime type $correct_Filetype = $FiletypeCache->get_by_mimetype($image_info['mime'], false, false); $file_mimetype = $image_info['mime']; // Check if file type is known by us, and if it is allowed for upload. // If we don't know this file type or if it isn't allowed we don't change the extension! The current extension is allowed for sure. if ($correct_Filetype && $correct_Filetype->is_allowed()) { // A FileType with the given mime type exists in database and it is an allowed file type for current User // The "correct" extension is a plausible one, proceed... $correct_extension = array_shift($correct_Filetype->get_extensions()); $path_info = pathinfo($filename); $current_extension = $path_info['extension']; // change file extension to the correct extension, but only if the correct extension is not restricted, this is an extra security check! if (strtolower($current_extension) != strtolower($correct_extension) && !in_array($correct_extension, $force_upload_forbiddenext)) { // change the file extension to the correct extension $old_filename = $filename; $filename = $path_info['filename'] . '.' . $correct_extension; } } } } // Get File object for requested target location: $FileCache =& get_FileCache(); $newFile =& $FileCache->get_by_root_and_path($fm_FileRoot->type, $fm_FileRoot->in_type_ID, trailing_slash($rds_subpath) . $filename, true); if ($newFile->exists()) { if ($overwrite && $newFile->unlink()) { // OK, file deleted // Delete thumb caches from old location: logIO('Old file deleted'); $newFile->rm_cache(); } else { return xmlrpcs_resperror(8, sprintf(T_('The file «%s» already exists.'), $filename)); } } // Trigger plugin event if ($Plugins->trigger_event_first_false('AfterFileUpload', array('File' => &$newFile, 'name' => &$filename, 'type' => &$file_mimetype, 'tmp_name' => &$tmpfile_name, 'size' => &$filesize))) { // Plugin returned 'false'. // Abort upload for this file: @unlink($tmpfile_name); return xmlrpcs_resperror(16, 'File upload aborted by a plugin.'); } if (!mkdir_r($newFile->get_dir())) { // Dir didn't already exist and could not be created return xmlrpcs_resperror(9, 'Error creating sub directories: ' . $newFile->get_rdfs_rel_path()); } if (!@rename($tmpfile_name, $newFile->get_full_path())) { return xmlrpcs_resperror(13, 'Error while writing to file.'); } // chmod the file $newFile->chmod(); // Initializes file properties (type, size, perms...) $newFile->load_properties(); // Load meta data AND MAKE SURE IT IS CREATED IN DB: $newFile->meta == 'unknown'; $newFile->load_meta(true); // Resize and rotate logIO('Running file post-processing (resize and rotate)...'); prepare_uploaded_files(array($newFile)); logIO('Done'); $url = $newFile->get_url(); logIO('URL of new file: ' . $url); $struct = new xmlrpcval(array('file' => new xmlrpcval($filename, 'string'), 'url' => new xmlrpcval($url, 'string'), 'type' => new xmlrpcval($file_mimetype, 'string')), 'struct'); logIO('OK.'); return new xmlrpcresp($struct); }
$message['text'] = '<span class="result_error"> ' . $error_filename . '</span>'; out_echo($message, $specialchars); exit; } list($newFile, $oldFile_thumb) = check_file_exists($fm_FileRoot, $path, $newName); $newName = $newFile->get('name'); // If everything is ok, save the file somewhere if (save_to_file($file->get_content(), $newFile->get_full_path(), 'wb')) { // Change to default chmod settings $newFile->chmod(NULL); // Refreshes file properties (type, size, perms...) $newFile->load_properties(); // save file into the db $newFile->dbsave(); // Prepare the uploaded file to the final format ( E.g. Resize and Rotate images ) prepare_uploaded_files(array($newFile)); $message = ''; if (!empty($oldFile_thumb)) { $image_info = getimagesize($newFile->get_full_path()); if ($image_info) { $newFile_thumb = $newFile->get_preview_thumb('fulltype'); } else { $newFile_thumb = $newFile->get_size_formatted(); } $message = '<br />'; $message .= sprintf(T_('%s was renamed to %s. Would you like to replace %s with the new version instead?'), '«' . $oldName . '»', '«' . $newName . '»', '«' . $oldName . '»'); $message .= '<div class="invalid" title="' . T_('File name changed.') . '">'; $message .= '<input type="radio" name="Renamed_' . $newFile->ID . '" value="Yes" id="Yes_' . $newFile->ID . '"/>'; $message .= '<label for="Yes_' . $newFile->ID . '">'; $message .= sprintf(T_("Replace the old version %s with the new version %s and keep old version as %s."), $oldFile_thumb, $newFile_thumb, $newName) . '</label><br />'; $message .= '<input type="radio" name="Renamed_' . $newFile->ID . '" value="No" id="No_' . $newFile->ID . '" checked="checked"/>';
/** * Process file uploads (this can process multiple file uploads at once) * * @param string FileRoot id string * @param string the upload dir relative path in the FileRoot * @param boolean Shall we create path dirs if they do not exist? * @param boolean Shall we check files add permission for current_User? * @param boolean upload quick mode * @param boolean show warnings if filename is not valid * @param integer minimum size for pictures in pixels (width and height) * @return mixed NULL if upload was impossible to complete for some reason (wrong fileroot ID, insufficient user permission, etc.) * array, which contains uploadedFiles, failedFiles, renamedFiles and renamedMessages */ function process_upload($root_ID, $path, $create_path_dirs = false, $check_perms = true, $upload_quickmode = true, $warn_invalid_filenames = true, $min_size = 0) { global $Settings, $Plugins, $Messages, $current_User, $force_upload_forbiddenext; if (empty($_FILES)) { // We have NO uploaded files to process... return NULL; } /** * Remember failed files (and the error messages) * @var array */ $failedFiles = array(); /** * Remember uploaded files * @var array */ $uploadedFiles = array(); /** * Remember renamed files * @var array */ $renamedFiles = array(); /** * Remember renamed Messages * @var array */ $renamedMessages = array(); $FileRootCache =& get_FileRootCache(); $fm_FileRoot =& $FileRootCache->get_by_ID($root_ID, true); if (!$fm_FileRoot) { // fileRoot not found: return NULL; } if ($check_perms && (!isset($current_User) || $current_User->check_perm('files', 'add', false, $fm_FileRoot))) { // Permission check required but current User has no permission to upload: return NULL; } // Let's get into requested list dir... $non_canonical_list_path = $fm_FileRoot->ads_path . $path; // Dereference any /../ just to make sure, and CHECK if directory exists: $ads_list_path = get_canonical_path($non_canonical_list_path); // check if the upload dir exists if (!is_dir($ads_list_path)) { if ($create_path_dirs) { // Create path mkdir_r($ads_list_path); } else { // This case should not happen! If it happens then there is a bug in the code where this function was called! return NULL; } } // Get param arrays for all uploaded files: $uploadfile_title = param('uploadfile_title', 'array/string', array()); $uploadfile_alt = param('uploadfile_alt', 'array/string', array()); $uploadfile_desc = param('uploadfile_desc', 'array/string', array()); $uploadfile_name = param('uploadfile_name', 'array/string', array()); // LOOP THROUGH ALL UPLOADED FILES AND PROCCESS EACH ONE: foreach ($_FILES['uploadfile']['name'] as $lKey => $lName) { if (empty($lName)) { // No file name: if ($upload_quickmode || !empty($uploadfile_title[$lKey]) || !empty($uploadfile_alt[$lKey]) || !empty($uploadfile_desc[$lKey]) || !empty($uploadfile_name[$lKey])) { // User specified params but NO file! Warn the user: $failedFiles[$lKey] = T_('Please select a local file to upload.'); } // Abort upload for this file: continue; } if ($Settings->get('upload_maxkb') && $_FILES['uploadfile']['size'][$lKey] > $Settings->get('upload_maxkb') * 1024) { // File is larger than allowed in settings: $failedFiles[$lKey] = sprintf(T_('The file is too large: %s but the maximum allowed is %s.'), bytesreadable($_FILES['uploadfile']['size'][$lKey]), bytesreadable($Settings->get('upload_maxkb') * 1024)); // Abort upload for this file: continue; } if (!empty($min_size)) { // Check pictures for small sizes $image_sizes = imgsize($_FILES['uploadfile']['tmp_name'][$lKey], 'widthheight'); if ($image_sizes[0] < $min_size || $image_sizes[1] < $min_size) { // Abort upload for this file: $failedFiles[$lKey] = sprintf(T_('Your profile picture must have a minimum size of %dx%d pixels.'), $min_size, $min_size); continue; } } if ($_FILES['uploadfile']['error'][$lKey]) { // PHP itself has detected an error!: switch ($_FILES['uploadfile']['error'][$lKey]) { case UPLOAD_ERR_FORM_SIZE: // The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the html form. // This can easily be edited by the user/hacker, so we do not use it.. file size gets checked for real just above. break; case UPLOAD_ERR_INI_SIZE: // File is larger than allowed in php.ini: $failedFiles[$lKey] = 'The file exceeds the upload_max_filesize directive in php.ini.'; // Configuration error, no translation // Abort upload for this file: continue; case UPLOAD_ERR_PARTIAL: $failedFiles[$lKey] = T_('The file was only partially uploaded.'); // Abort upload for this file: continue; case UPLOAD_ERR_NO_FILE: // Is probably the same as empty($lName) before. $failedFiles[$lKey] = T_('No file was uploaded.'); // Abort upload for this file: continue; case 6: // numerical value of UPLOAD_ERR_NO_TMP_DIR # (min_php: 4.3.10, 5.0.3) case UPLOAD_ERR_NO_TMP_DIR: // Missing a temporary folder. $failedFiles[$lKey] = 'Temporary upload dir is missing! (upload_tmp_dir in php.ini)'; // Configuration error, no translation // Abort upload for this file: continue; default: $failedFiles[$lKey] = T_('An unknown error has occurred!') . ' Error code #' . $_FILES['uploadfile']['error'][$lKey]; // Abort upload for this file: continue; } } if (!isset($_FILES['uploadfile']['_evo_fetched_url'][$lKey]) && !is_uploaded_file($_FILES['uploadfile']['tmp_name'][$lKey])) { // Ensure that a malicious user hasn't tried to trick the script into working on files upon which it should not be working. $failedFiles[$lKey] = T_('The file does not seem to be a valid upload! It may exceed the upload_max_filesize directive in php.ini.'); // Abort upload for this file: continue; } // Use new name on server if specified: $newName = !empty($uploadfile_name[$lKey]) ? $uploadfile_name[$lKey] : $lName; // validate file name if ($error_filename = process_filename($newName, !$warn_invalid_filenames)) { // Not a valid file name or not an allowed extension: $failedFiles[$lKey] = $error_filename; // Abort upload for this file: continue; } // Check if the uploaded file type is an image, and if is an image then try to fix the file extension based on mime type // If the mime type is a known mime type and user has right to upload files with this kind of file type, // this part of code will check if the file extension is the same as admin defined for this file type, and will fix it if it isn't the same // Note: it will also change the jpeg extensions to jpg. $uploadfile_path = $_FILES['uploadfile']['tmp_name'][$lKey]; // this image_info variable will be used again to get file thumb $image_info = getimagesize($uploadfile_path); if ($image_info) { // This is an image, validate mimetype vs. extension: $image_mimetype = $image_info['mime']; $FiletypeCache =& get_FiletypeCache(); // Get correct file type based on mime type $correct_Filetype = $FiletypeCache->get_by_mimetype($image_mimetype, false, false); // Check if file type is known by us, and if it is allowed for upload. // If we don't know this file type or if it isn't allowed we don't change the extension! The current extension is allowed for sure. if ($correct_Filetype && $correct_Filetype->is_allowed()) { // A FileType with the given mime type exists in database and it is an allowed file type for current User // The "correct" extension is a plausible one, proceed... $correct_extension = array_shift($correct_Filetype->get_extensions()); $path_info = pathinfo($newName); $current_extension = $path_info['extension']; // change file extension to the correct extension, but only if the correct extension is not restricted, this is an extra security check! if (strtolower($current_extension) != strtolower($correct_extension) && !in_array($correct_extension, $force_upload_forbiddenext)) { // change the file extension to the correct extension $old_name = $newName; $newName = $path_info['filename'] . '.' . $correct_extension; $Messages->add(sprintf(T_('The extension of the file «%s» has been corrected. The new filename is «%s».'), $old_name, $newName), 'warning'); } } } // Get File object for requested target location: $oldName = strtolower($newName); list($newFile, $oldFile_thumb) = check_file_exists($fm_FileRoot, $path, $newName, $image_info); $newName = $newFile->get('name'); // Trigger plugin event if ($Plugins->trigger_event_first_false('AfterFileUpload', array('File' => &$newFile, 'name' => &$_FILES['uploadfile']['name'][$lKey], 'type' => &$_FILES['uploadfile']['type'][$lKey], 'tmp_name' => &$_FILES['uploadfile']['tmp_name'][$lKey], 'size' => &$_FILES['uploadfile']['size'][$lKey]))) { // Plugin returned 'false'. // Abort upload for this file: continue; } // Attempt to move the uploaded file to the requested target location: if (isset($_FILES['uploadfile']['_evo_fetched_url'][$lKey])) { // fetched remotely if (!rename($_FILES['uploadfile']['tmp_name'][$lKey], $newFile->get_full_path())) { $failedFiles[$lKey] = T_('An unknown error occurred when moving the uploaded file on the server.'); // Abort upload for this file: continue; } } elseif (!move_uploaded_file($_FILES['uploadfile']['tmp_name'][$lKey], $newFile->get_full_path())) { $failedFiles[$lKey] = T_('An unknown error occurred when moving the uploaded file on the server.'); // Abort upload for this file: continue; } // change to default chmod settings if ($newFile->chmod(NULL) === false) { // add a note, this is no error! $Messages->add(sprintf(T_('Could not change permissions of «%s» to default chmod setting.'), $newFile->dget('name')), 'note'); } // Refreshes file properties (type, size, perms...) $newFile->load_properties(); if (!empty($oldFile_thumb)) { // The file name was changed! if ($image_info) { $newFile_thumb = $newFile->get_preview_thumb('fulltype'); } else { $newFile_thumb = $newFile->get_size_formatted(); } //$newFile_size = bytesreadable ($_FILES['uploadfile']['size'][$lKey]); $renamedMessages[$lKey]['message'] = sprintf(T_('"%s was renamed to %s. Would you like to replace %s with the new version instead?'), '«' . $oldName . '»', '«' . $newName . '»', '«' . $oldName . '»'); $renamedMessages[$lKey]['oldThumb'] = $oldFile_thumb; $renamedMessages[$lKey]['newThumb'] = $newFile_thumb; $renamedFiles[$lKey]['oldName'] = $oldName; $renamedFiles[$lKey]['newName'] = $newName; } // Store extra info about the file into File Object: if (isset($uploadfile_title[$lKey])) { // If a title text has been passed... (does not happen in quick upload mode) $newFile->set('title', trim(strip_tags($uploadfile_title[$lKey]))); } if (isset($uploadfile_alt[$lKey])) { // If an alt text has been passed... (does not happen in quick upload mode) $newFile->set('alt', trim(strip_tags($uploadfile_alt[$lKey]))); } if (isset($uploadfile_desc[$lKey])) { // If a desc text has been passed... (does not happen in quick upload mode) $newFile->set('desc', trim(strip_tags($uploadfile_desc[$lKey]))); } // Store File object into DB: $newFile->dbsave(); $uploadedFiles[] = $newFile; } prepare_uploaded_files($uploadedFiles); return array('uploadedFiles' => $uploadedFiles, 'failedFiles' => $failedFiles, 'renamedFiles' => $renamedFiles, 'renamedMessages' => $renamedMessages); }