Beispiel #1
0
 /**
  * Upload the file
  *
  * @param &object &$attachment the partially constructed attachment object
  * @param &object &$parent An Attachments plugin parent object with partial parent info including:
  *		$parent->new : True if the parent has not been created yet
  *						(like adding attachments to an article before it has been saved)
  *		$parent->title : Title/name of the parent object
  * @param int $attachment_id false if this is a new attachment
  * @param string $save_type 'update' or 'update'
  *
  * @return a message indicating succes or failure
  *
  * NOTE: The caller should set up all the parent info in the record before calling this
  *		 (see $parent->* below for necessary items)
  */
 public static function upload_file(&$attachment, &$parent, $attachment_id = false, $save_type = 'update')
 {
     $user = JFactory::getUser();
     $db = JFactory::getDBO();
     $from = JRequest::getWord('from');
     // Figure out if the user may publish this attachment
     $may_publish = $parent->userMayChangeAttachmentState($attachment->parent_id, $attachment->parent_entity, $attachment->created_by);
     // Get the component parameters
     jimport('joomla.application.component.helper');
     $params = JComponentHelper::getParams('com_attachments');
     // Make sure the attachments directory exists
     $upload_dir = JPATH_SITE . '/' . AttachmentsDefines::$ATTACHMENTS_SUBDIR;
     $secure = $params->get('secure', false);
     if (!AttachmentsHelper::setup_upload_directory($upload_dir, $secure)) {
         $errmsg = JText::sprintf('ATTACH_ERROR_UNABLE_TO_SETUP_UPLOAD_DIR_S', $upload_dir) . ' (ERR 33)';
         JError::raiseError(500, $errmsg);
     }
     // If we are updating, note the name of the old filename
     $old_filename = null;
     $old_filename_sys = null;
     $old_uri_type = $attachment->uri_type;
     if ($old_uri_type) {
         $old_filename = $attachment->filename;
         $old_filename_sys = $attachment->filename_sys;
     }
     // Get the new filename
     // (Note: The following replacement is necessary to allow
     //		  single quotes in filenames to work correctly.)
     // Trim of any trailing period (to avoid exploits)
     $filename = rtrim(JString::str_ireplace("\\'", "'", $_FILES['upload']['name']), '.');
     $ftype = $_FILES['upload']['type'];
     // Check the file size
     $max_upload_size = (int) ini_get('upload_max_filesize');
     $max_attachment_size = (int) $params->get('max_attachment_size', 0);
     if ($max_attachment_size == 0) {
         $max_attachment_size = $max_upload_size;
     }
     $max_size = min($max_upload_size, $max_attachment_size);
     $file_size = filesize($_FILES['upload']['tmp_name']) / 1048576.0;
     if ($file_size > $max_size) {
         $errmsg = JText::sprintf('ATTACH_ERROR_FILE_S_TOO_BIG_N_N_N', $filename, $file_size, $max_attachment_size, $max_upload_size);
         JError::raiseError(500, '<b>' . $errmsg . '</b>');
     }
     // Get the maximum allowed filename length (for the filename display)
     $max_filename_length = (int) $params->get('max_filename_length', 0);
     if ($max_filename_length == 0) {
         $max_filename_length = AttachmentsDefines::$MAXIMUM_FILENAME_LENGTH;
     } else {
         $max_filename_length = min($max_filename_length, AttachmentsDefines::$MAXIMUM_FILENAME_LENGTH);
     }
     // Truncate the filename, if necessary and alert the user
     if (JString::strlen($filename) > $max_filename_length) {
         $filename = AttachmentsHelper::truncate_filename($filename, $max_filename_length);
         $msg = JText::_('ATTACH_WARNING_FILENAME_TRUNCATED');
         $app = JFactory::getApplication();
         if ($app->isAdmin()) {
             $lang = JFactory::getLanguage();
             if ($lang->isRTL()) {
                 $msg = "'{$filename}' " . $msg;
             } else {
                 $msg = $msg . " '{$filename}'";
             }
             $app->enqueueMessage($msg, 'warning');
         } else {
             $msg .= "\\n \\'{$filename}\\'";
             echo "<script type=\"text/javascript\">alert('{$msg}')</script>";
         }
     }
     // Check the filename for bad characters
     $bad_chars = false;
     $forbidden_chars = $params->get('forbidden_filename_characters', '#=?%&');
     for ($i = 0; $i < strlen($forbidden_chars); $i++) {
         $char = $forbidden_chars[$i];
         if (strpos($filename, $char) !== false) {
             $bad_chars = true;
             break;
         }
     }
     // Check for double-extension exploit
     $bad_filename = false;
     if (AttachmentsHelper::is_double_extension_exploit($filename)) {
         $bad_filename = true;
     }
     // Set up the entity name for display
     $parent_entity = $parent->getCanonicalEntityId($attachment->parent_entity);
     $parent_entity_name = JText::_('ATTACH_' . $parent_entity);
     // A little formatting
     $msgbreak = '<br />';
     $app = JFactory::getApplication();
     if ($app->isAdmin()) {
         $msgbreak = '';
     }
     // Make sure a file was successfully uploaded
     if ($_FILES['upload']['size'] == 0 && $_FILES['upload']['tmp_name'] == '' || $bad_chars || $bad_filename) {
         // Guess the type of error
         if ($bad_chars) {
             $error = 'bad_chars';
             $error_msg = JText::sprintf('ATTACH_ERROR_BAD_CHARACTER_S_IN_FILENAME_S', $char, $filename);
             if ($app->isAdmin()) {
                 $result = new JObject();
                 $result->error = true;
                 $result->error_msg = $error_msg;
                 return $result;
             }
         } elseif ($bad_filename) {
             $error = 'illegal_file_extension';
             $format = JString::strtolower(JFile::getExt($filename));
             $error_msg = JText::_('ATTACH_ERROR_ILLEGAL_FILE_EXTENSION') . " .php.{$format}";
             if ($app->isAdmin()) {
                 $result = new JObject();
                 $result->error = true;
                 $result->error_msg = $error_msg;
                 return $result;
             }
         } elseif ($filename == '') {
             $error = 'no_file';
             $error_msg = JText::sprintf('ATTACH_ERROR_UPLOADING_FILE_S', $filename);
             $error_msg .= $msgbreak . ' (' . JText::_('ATTACH_YOU_MUST_SELECT_A_FILE_TO_UPLOAD') . ')';
             if ($app->isAdmin()) {
                 $result = new JObject();
                 $result->error = true;
                 $result->error_msg = $error_msg;
                 return $result;
             }
         } else {
             $error = 'file_too_big';
             $error_msg = JText::sprintf('ATTACH_ERROR_UPLOADING_FILE_S', $filename);
             $error_msg .= $msgbreak . '(' . JText::_('ATTACH_ERROR_MAY_BE_LARGER_THAN_LIMIT') . ' ';
             $error_msg .= get_cfg_var('upload_max_filesize') . ')';
             if ($app->isAdmin()) {
                 $result = new JObject();
                 $result->error = true;
                 $result->error_msg = $error_msg;
                 return $result;
             }
         }
         // Set up the view to redisplay the form with warnings
         if ($save_type == 'update') {
             require_once JPATH_COMPONENT_SITE . '/views/update/view.html.php';
             $view = new AttachmentsViewUpdate();
             AttachmentsHelper::add_view_urls($view, 'update', $attachment->parent_id, $attachment->parent_type, $attachment_id, $from);
             $view->update = JRequest::getWord('update');
         } else {
             require_once JPATH_COMPONENT_SITE . '/views/upload/view.html.php';
             $view = new AttachmentsViewUpload();
             AttachmentsHelper::add_view_urls($view, 'upload', $attachment->parent_id, $attachment->parent_type, null, $from);
         }
         // Suppress the display filename if we are changing from file to url
         $display_name = $attachment->display_name;
         if ($save_type == 'update') {
             $new_uri_type = JRequest::getWord('update');
             if ($new_uri_type && ($new_uri_type == 'file' || $new_uri_type != $attachment->uri_type)) {
                 $attachment->display_name = '';
             }
         }
         // Set up the view
         $view->attachment = $attachment;
         $view->new_parent = $parent->new;
         $view->parent = $parent;
         $view->params = $params;
         $view->from = $from;
         $view->Itemid = JRequest::getInt('Itemid', 1);
         $view->error = $error;
         $view->error_msg = $error_msg;
         // Display the view
         $view->display();
         exit;
     }
     // Make sure the file type is okay (respect restrictions imposed by media manager)
     $cmparams = JComponentHelper::getParams('com_media');
     // Check to make sure the extension is allowed
     jimport('joomla.filesystem.file');
     $allowable = explode(',', $cmparams->get('upload_extensions'));
     $ignored = explode(',', $cmparams->get('ignore_extensions'));
     $extension = JString::strtolower(JFile::getExt($filename));
     $error = false;
     $error_msg = false;
     if (!in_array($extension, $allowable) && !in_array($extension, $ignored)) {
         $error = 'illegal_file_extension';
         $error_msg = JText::sprintf('ATTACH_ERROR_UPLOADING_FILE_S', $filename);
         $error_msg .= "<br />" . JText::_('ATTACH_ERROR_ILLEGAL_FILE_EXTENSION') . " {$extension}";
         if ($user->authorise('core.admin')) {
             $error_msg .= "<br />" . JText::_('ATTACH_ERROR_CHANGE_IN_MEDIA_MANAGER');
         }
     }
     // Check to make sure the mime type is okay
     if ($cmparams->get('restrict_uploads', true)) {
         if ($cmparams->get('check_mime', true)) {
             $allowed_mime = explode(',', $cmparams->get('upload_mime'));
             $illegal_mime = explode(',', $cmparams->get('upload_mime_illegal'));
             if (JString::strlen($ftype) && !in_array($ftype, $allowed_mime) && in_array($ftype, $illegal_mime)) {
                 $error = 'illegal_mime_type';
                 $error_msg = JText::sprintf('ATTACH_ERROR_UPLOADING_FILE_S', $filename);
                 $error_msg .= ', ' . JText::_('ATTACH_ERROR_ILLEGAL_FILE_MIME_TYPE') . " {$ftype}";
                 if ($user->authorise('core.admin')) {
                     $error_msg .= "\t <br />" . JText::_('ATTACH_ERROR_CHANGE_IN_MEDIA_MANAGER');
                 }
             }
         }
     }
     // If it is an image file, make sure it is a valid image file (and not some kind of exploit)
     if (AttachmentsHelper::is_image_file($filename)) {
         if (!AttachmentsHelper::is_valid_image_file($_FILES['upload']['tmp_name'])) {
             $error = 'illegal_file_extension';
             $error_msg = JText::sprintf('ATTACH_ERROR_UPLOADING_FILE_S', $filename);
             $error_msg .= "<br />" . JText::_('ATTACH_ERROR_ILLEGAL_FILE_EXTENSION') . " (corrupted image file)";
             // ??? Need new error message
         }
     }
     // Handle PDF mime types
     if ($extension == 'pdf') {
         require_once JPATH_COMPONENT_SITE . '/file_types.php';
         if (in_array($ftype, AttachmentsFileTypes::$attachments_pdf_mime_types)) {
             $ftype = 'application/pdf';
         }
     }
     // If there was an error, refresh the form with a warning
     if ($error) {
         if ($app->isAdmin()) {
             $result = new JObject();
             $result->error = true;
             $result->error_msg = $error_msg;
             return $result;
         }
         // Set up the view to redisplay the form with warnings
         if ($save_type == 'update') {
             require_once JPATH_COMPONENT_SITE . '/views/update/view.html.php';
             $view = new AttachmentsViewUpdate();
             AttachmentsHelper::add_view_urls($view, 'update', $attachment->parent_id, $attachment->parent_type, $attachment_id, $from);
             $view->update = JRequest::getWord('update');
         } else {
             require_once JPATH_COMPONENT_SITE . '/views/upload/view.html.php';
             $view = new AttachmentsViewUpload();
             AttachmentsHelper::add_view_urls($view, 'upload', $attachment->parent_id, $attachment->parent_type, null, $from);
         }
         // Suppress the display filename if we are changing from file to url
         $display_name = $attachment->display_name;
         if ($save_type == 'update') {
             $new_uri_type = JRequest::getWord('update');
             if ($new_uri_type && ($new_uri_type == 'file' || $new_uri_type != $attachment->uri_type)) {
                 $attachment->display_name = '';
             }
         }
         // Set up the view
         $view->attachment = $attachment;
         $view->new_parent = $parent->new;
         $view->parent = $parent;
         $view->params = $params;
         $view->from = $from;
         $view->Itemid = JRequest::getInt('Itemid', 1);
         $view->error = $error;
         $view->error_msg = $error_msg;
         // Display the view
         $view->display();
         exit;
     }
     // Define where the attachments go
     $upload_url = AttachmentsDefines::$ATTACHMENTS_SUBDIR;
     $upload_dir = JPATH_SITE . '/' . $upload_url;
     // Figure out the system filename
     $path = $parent->getAttachmentPath($attachment->parent_entity, $attachment->parent_id, null);
     $fullpath = $upload_dir . '/' . $path;
     // Make sure the directory exists
     if (!JFile::exists($fullpath)) {
         jimport('joomla.filesystem.folder');
         if (!JFolder::create($fullpath)) {
             $errmsg = JText::sprintf('ATTACH_ERROR_UNABLE_TO_SETUP_UPLOAD_DIR_S', $upload_dir) . ' (ERR 34)';
             JError::raiseError(500, $errmsg);
         }
         AttachmentsHelper::write_empty_index_html($fullpath);
     }
     // Get ready to save the file
     $filename_sys = $fullpath . $filename;
     $url = $upload_url . '/' . $path . $filename;
     // If we are on windows, fix the filename and URL
     if (DIRECTORY_SEPARATOR != '/') {
         $filename_sys = str_replace('/', DIRECTORY_SEPARATOR, $filename_sys);
         $url = str_replace(DIRECTORY_SEPARATOR, '/', $url);
     }
     // Check on length of filename_sys
     if (JString::strlen($filename_sys) > AttachmentsDefines::$MAXIMUM_FILENAME_SYS_LENGTH) {
         $errmsg = JText::sprintf('ATTACH_ERROR_FILEPATH_TOO_LONG_N_N_S', JString::strlen($filename_sys), AttachmentsDefines::$MAXIMUM_FILENAME_SYS_LENGTH, $filename) . '(ERR 35)';
         JError::raiseError(500, $errmsg);
     }
     // Make sure the system filename doesn't already exist
     $error = false;
     $duplicate_filename = false;
     if ($save_type == 'upload' && JFile::exists($filename_sys)) {
         // Cannot overwrite an existing file when creating a new attachment!
         $duplicate_filename = true;
     }
     if ($save_type == 'update' && JFile::exists($filename_sys)) {
         // If updating, we may replace the existing file but may not overwrite any other existing file
         $query = $db->getQuery(true);
         $query->select('id')->from('#__attachments');
         $query->where('filename_sys=' . $db->quote($filename_sys) . ' AND id != ' . (int) $attachment->id);
         $db->setQuery($query, 0, 1);
         if ($db->loadResult() > 0) {
             $duplicate_filename = true;
         }
     }
     // Handle duplicate filename error
     if ($duplicate_filename) {
         $error = 'file_already_on_server';
         $error_msg = JText::sprintf('ATTACH_ERROR_FILE_S_ALREADY_ON_SERVER', $filename);
         if ($app->isAdmin()) {
             $result = new JObject();
             $result->error = true;
             $result->error_msg = $error_msg;
             return $result;
         }
         $save_url = JRoute::_("index.php?option=com_attachments&task=save&tmpl=component");
         // Set up the view to redisplay the form with warnings
         require_once JPATH_COMPONENT_SITE . '/views/upload/view.html.php';
         $view = new AttachmentsViewUpload();
         AttachmentsHelper::add_view_urls($view, 'upload', $attachment->parent_id, $attachment->parent_type, null, $from);
         // Set up the view
         $view->attachment = $attachment;
         $view->save_url = $save_url;
         $view->new_parent = $parent->new;
         $view->parent = $parent;
         $view->params = $params;
         $view->from = $from;
         $view->Itemid = JRequest::getInt('Itemid', 1);
         $view->error = $error;
         $view->error_msg = $error_msg;
         // Display the view
         $view->display();
         exit;
     }
     // Create a display filename, if needed (for long filenames)
     if ($max_filename_length > 0 && JString::strlen($attachment->display_name) == 0 && JString::strlen($filename) > $max_filename_length) {
         $attachment->display_name = AttachmentsHelper::truncate_filename($filename, $max_filename_length);
     }
     // Copy the info about the uploaded file into the new record
     $attachment->uri_type = 'file';
     $attachment->filename = $filename;
     $attachment->filename_sys = $filename_sys;
     $attachment->url = $url;
     $attachment->file_type = $ftype;
     $attachment->file_size = $_FILES['upload']['size'];
     // If the user is not authorised to change the state (eg, publish/unpublish),
     // ignore the form data and make sure the publish state is is set correctly.
     if (!$may_publish) {
         if ($save_type == 'upload') {
             // Use the default publish state (ignore form info)
             jimport('joomla.application.component.helper');
             $params = JComponentHelper::getParams('com_attachments');
             $attachment->state = $params->get('publish_default', false);
         } else {
             // Restore the old state (ignore form info)
             $db = JFactory::getDBO();
             $query = $db->getQuery(true);
             $query->select('state')->from('#__attachments')->where('id = ' . (int) $attachment->id);
             $db->setQuery($query, 0, 1);
             $old_state = $db->loadResult();
             if ($db->getErrorNum()) {
                 $errmsg = $db->stderr() . ' (ERR 36)';
                 JError::raiseError(500, $errmsg);
             }
             $attachment->state = $old_state;
         }
     }
     // Set the create/modify dates
     $now = JFactory::getDate();
     $now = $now->toSql();
     // Update the create/modify info
     if ($save_type == 'upload') {
         $attachment->created = $now;
     }
     $attachment->modified = $now;
     // Add the icon file type
     require_once JPATH_COMPONENT_SITE . '/file_types.php';
     $attachment->icon_filename = AttachmentsFileTypes::icon_filename($filename, $ftype);
     // Save the updated attachment
     if (!$attachment->store()) {
         $errmsg = JText::_('ATTACH_ERROR_SAVING_FILE_ATTACHMENT_RECORD') . $attachment->getError() . ' (ERR 37)';
         JError::raiseError(500, $errmsg);
     }
     // Get the attachment id
     // If we're updating we may not get an insertid, so don't blindly overwrite the old
     // attachment_id just in case (Thanks to Franz-Xaver Geiger for a bug fix on this)
     $new_attachment_id = $db->insertid();
     if (!empty($new_attachment_id)) {
         $attachment_id = (int) $new_attachment_id;
     }
     // Move the file
     $msg = "";
     if (JFile::upload($_FILES['upload']['tmp_name'], $filename_sys)) {
         $file_size = (int) ($attachment->file_size / 1024.0);
         $file_size_str = JText::sprintf('ATTACH_S_KB', $file_size);
         if ($file_size_str == 'ATTACH_S_KB') {
             // Work around until all translations are updated ???
             $file_size_str = $file_size . ' kB';
         }
         chmod($filename_sys, 0644);
         // ??? The following items need to be updated for RTL
         if ($save_type == 'update') {
             $msg = JText::_('ATTACH_UPDATED_ATTACHMENT') . ' ' . $filename . ' (' . $file_size_str . ')!';
         } else {
             $msg = JText::_('ATTACH_UPLOADED_ATTACHMENT') . ' ' . $filename . ' (' . $file_size_str . ')!';
         }
     } else {
         $query = $db->getQuery(true);
         $query->delete('#__attachments')->where('id = ' . (int) $attachment_id);
         $db->setQuery($query);
         $result = $db->query();
         if ($db->getErrorNum()) {
             $errmsg = $db->stderr() . ' (ERR 38)';
             JError::raiseError(500, $errmsg);
         }
         $msg = JText::_('ATTACH_ERROR_MOVING_FILE') . " {$_FILES['upload']['tmp_name']} -> {$filename_sys})";
     }
     // If we are updating, we may need to delete the old file
     if ($save_type == 'update') {
         if ($filename_sys != $old_filename_sys && JFile::exists($old_filename_sys)) {
             JFile::delete($old_filename_sys);
             AttachmentsHelper::clean_directory($old_filename_sys);
         }
     }
     return $msg;
 }