$buffer = fread($input_handle, 65536); $new_size += strlen($buffer); // write it if (!fwrite($output_handle, $buffer)) { Safe::header('500 Internal Server Error'); Logger::error('Impossible to write to local file'); break; } } fclose($output_handle); // also update the database $item['file_size'] = $new_size; $item['edit_name'] = $user['nick_name']; $item['edit_id'] = $user['id']; $item['edit_address'] = $user['email']; if (!($item['id'] = Files::post($item, 'A'))) { Safe::header('500 Internal Server Error'); } else { Files::clear($item); Safe::header('200 OK'); } } // clear assignment information, if any } elseif ($action == 'release' && ($anchor->is_assigned() || isset($item['assign_id']) && Surfer::is($item['assign_id']))) { // change page title $context['page_title'] = sprintf(i18n::s('%s: %s'), i18n::s('Release reservation'), $context['page_title']); // clear assignment information if (Files::assign($item['id'], NULL)) { // inform surfer $context['text'] .= '<p>' . i18n::s('You have released this file, and other surfers can reserve it for revision.') . '</p>'; // help the surfer
/** * create an attached file * * @param array of entity attributes (e.g., 'Content-Disposition') * @param string file actual content * @param array poster attributes * @param string the target anchor (e.g., 'article:123') * @param string reference of the object to be extended, if any * @return string reference to the created object, or NULL */ public static function submit_file($entity_headers, $content, $user, $anchor, $target = NULL) { global $context; // retrieve queue parameters list($server, $account, $password, $allowed, $match, $section, $options, $hooks, $prefix, $suffix) = $context['mail_queue']; // locate content-disposition foreach ($entity_headers as $header) { if (preg_match('/Content-Disposition/i', $header['name'])) { $content_disposition = $header['value']; break; } } // find file name in content-disposition $file_name = ''; if ($content_disposition && preg_match('/filename="*([a-zA-Z0-9\'\\(\\)\\+_,-\\.\\/:=\\? ]+)"*\\s*/i', $content_disposition, $matches)) { $file_name = $matches[1]; } // as an alternative, look in content-type if (!$file_name) { // locate content-type foreach ($entity_headers as $header) { if (preg_match('/Content-Type/i', $header['name'])) { $content_type = $header['value']; break; } } // find file name in content-type if ($content_type && preg_match('/name="*([a-zA-Z0-9\'\\(\\)\\+_,-\\.\\/:=\\? ]+)"*\\s*/i', $content_type, $matches)) { $file_name = $matches[1]; } } // as an alternative, look in content-description if (!$file_name) { // locate content-description foreach ($entity_headers as $header) { if (preg_match('/Content-Description/i', $header['name'])) { $content_description = $header['value']; $file_name = $content_description; break; } } } // sanity check if (!$file_name) { Logger::remember('agents/messages.php: No name to use for submitted file'); return NULL; } // we don't accept all extensions if (!Files::is_authorized($file_name)) { Logger::remember('agents/messages.php: Rejected file type for ' . $file_path . $file_name); return NULL; } // file size $file_size = strlen($content); // sanity check if ($file_size < 7) { Logger::remember('agents/messages.php: Short file skipped'); return NULL; } // sanity check if (!$anchor) { Logger::remember('agents/messages.php: No anchor to use for submitted file', $file_name); return NULL; } // get anchor data -- this is a mutable object $host = Anchors::get($anchor, TRUE); if (!is_object($host)) { Logger::remember('agents/messages.php: Unknown anchor ' . $anchor); return NULL; } // create target folders list($anchor_type, $anchor_id) = explode(':', $anchor, 2); $file_path = 'files/' . $anchor_type . '/' . $anchor_id; if (!Safe::make_path($file_path)) { Logger::remember('agents/messages.php: Impossible to create ' . $file_path); return NULL; } $file_path = $context['path_to_root'] . $file_path . '/'; // save the entity in the file system if (!($file = Safe::fopen($file_path . $file_name, 'wb'))) { Logger::remember('agents/messages.php: Impossible to open ' . $file_path . $file_name); return NULL; } if (fwrite($file, $content) === FALSE) { Logger::remember('agents/messages.php: Impossible to write to ' . $file_path . $file_name); return NULL; } fclose($file); // update file description $item = array(); $item['anchor'] = $anchor; $item['file_name'] = $file_name; $item['file_size'] = $file_size; if (isset($content_description) && $content_description != $file_name) { $item['description'] = $content_description; } $item['edit_date'] = gmstrftime('%Y-%m-%d %H:%M:%S', time()); $item['edit_name'] = $user['nick_name']; $item['edit_id'] = $user['id']; $item['edit_address'] = $user['email']; // create a file record in the database if (!($item['id'] = Files::post($item))) { Logger::remember('agents/messages.php: ' . Logger::error_pop()); return NULL; } if ($context['debug_messages'] == 'Y') { Logger::remember('agents/messages.php: Messages::submit_file()', $item, 'debug'); } return 'file:' . $item['id']; }
// change has been documented if (isset($_REQUEST['version']) && $_REQUEST['version']) { $_REQUEST['description'] = Files::add_to_history($item, $_REQUEST['version']); } // when the file has been overlaid if (is_object($overlay)) { // allow for change detection $overlay->snapshot(); // update the overlay from form content $overlay->parse_fields($_REQUEST); // save content of the overlay in this item $_REQUEST['overlay'] = $overlay->save(); $_REQUEST['overlay_id'] = $overlay->get_id(); } // save in the database Files::post($_REQUEST); // nothing has been posted } else { Logger::error(i18n::s('No file has been transmitted.')); } // an error has already been encoutered if (count($context['error'])) { $item = $_REQUEST; $with_form = TRUE; // reward the poster for new posts, or for actual upload } elseif (!isset($item['id']) || $action == 'file:create' || $action == 'file:upload') { // increment the post counter of the surfer Users::increment_posts(Surfer::get_id()); // thanks $context['page_title'] = i18n::s('Thank you for your contribution'); // only one file
/** * process uploaded file * * This function processes files from the temporary directory, and put them at their definitive * place. * * It returns FALSE if there is a disk error, or if some virus has been detected, or if * the operation fails for some other reason (e.g., file size). * * @param array usually, $_FILES['upload'] * @param string target location for the file * @param mixed reference to the target anchor, of a function to parse every file individually * @return mixed file name or array of file names or FALSE if an error has occured */ public static function upload($input, $file_path, $target = NULL, $overlay = NULL) { global $context, $_REQUEST; // size exceeds php.ini settings -- UPLOAD_ERR_INI_SIZE if (isset($input['error']) && $input['error'] == 1) { Logger::error(i18n::s('The size of this file is over limit.')); } elseif (isset($input['error']) && $input['error'] == 2) { Logger::error(i18n::s('The size of this file is over limit.')); } elseif (isset($input['error']) && $input['error'] == 3) { Logger::error(i18n::s('No file has been transmitted.')); } elseif (isset($input['error']) && $input['error'] == 4) { Logger::error(i18n::s('No file has been transmitted.')); } elseif (!$input['size']) { Logger::error(i18n::s('No file has been transmitted.')); } // do we have a file? if (!isset($input['name']) || !$input['name'] || $input['name'] == 'none') { return FALSE; } // access the temporary uploaded file $file_upload = $input['tmp_name']; // $_FILES transcoding to utf8 is not automatic $input['name'] = utf8::encode($input['name']); // enhance file name $file_name = $input['name']; $file_extension = ''; $position = strrpos($input['name'], '.'); if ($position !== FALSE) { $file_name = substr($input['name'], 0, $position); $file_extension = strtolower(substr($input['name'], $position + 1)); } $input['name'] = $file_name; if ($file_extension) { $input['name'] .= '.' . $file_extension; } // ensure we have a file name $file_name = utf8::to_ascii($input['name']); // uploads are not allowed if (!Surfer::may_upload()) { Logger::error(i18n::s('You are not allowed to perform this operation.')); } elseif (!Files::is_authorized($input['name'])) { Logger::error(i18n::s('This type of file is not allowed.')); } elseif ($file_path && !Safe::is_uploaded_file($file_upload)) { Logger::error(i18n::s('Possible file attack.')); } else { // create folders if ($file_path) { Safe::make_path($file_path); } // sanity check if ($file_path && $file_path[strlen($file_path) - 1] != '/') { $file_path .= '/'; } // move the uploaded file if ($file_path && !Safe::move_uploaded_file($file_upload, $context['path_to_root'] . $file_path . $file_name)) { Logger::error(sprintf(i18n::s('Impossible to move the upload file to %s.'), $file_path . $file_name)); } else { // process the file where it is if (!$file_path) { $file_path = str_replace($context['path_to_root'], '', dirname($file_upload)); $file_name = basename($file_upload); } // check against viruses $result = Files::has_virus($context['path_to_root'] . $file_path . '/' . $file_name); // no virus has been found in this file if ($result == 'N') { $context['text'] .= Skin::build_block(i18n::s('No virus has been found.'), 'note'); } // this file has been infected! if ($result == 'Y') { // delete this file immediately Safe::unlink($file_path . '/' . $file_name); Logger::error(i18n::s('This file has been infected by a virus and has been rejected!')); return FALSE; } // explode a .zip file include_once $context['path_to_root'] . 'shared/zipfile.php'; if (preg_match('/\\.zip$/i', $file_name) && isset($_REQUEST['explode_files'])) { $zipfile = new zipfile(); // check files extracted from the archive file function explode_callback($name) { global $context; // reject all files put in sub-folders if (($path = substr($name, strlen($context['uploaded_path'] . '/'))) && strpos($path, '/') !== FALSE) { Safe::unlink($name); } elseif (!Files::is_authorized($name)) { Safe::unlink($name); } else { // make it easy to download $ascii = utf8::to_ascii(basename($name)); Safe::rename($name, $context['uploaded_path'] . '/' . $ascii); // remember this name $context['uploaded_files'][] = $ascii; } } // extract archive components and save them in mentioned directory $context['uploaded_files'] = array(); $context['uploaded_path'] = $file_path; if (!($count = $zipfile->explode($context['path_to_root'] . $file_path . '/' . $file_name, $file_path, '', 'explode_callback'))) { Logger::error(sprintf('Nothing has been extracted from %s.', $file_name)); return FALSE; } // one single file has been uploaded } else { $context['uploaded_files'] = array($file_name); } // ensure we know the surfer Surfer::check_default_editor($_REQUEST); // post-process all uploaded files foreach ($context['uploaded_files'] as $file_name) { // this will be filtered by umask anyway Safe::chmod($context['path_to_root'] . $file_path . $file_name, $context['file_mask']); // invoke post-processing function if ($target && is_callable($target)) { call_user_func($target, $file_name, $context['path_to_root'] . $file_path); // we have to update an anchor page } elseif ($target && is_string($target)) { $fields = array(); // update a file with the same name for this anchor if ($matching =& Files::get_by_anchor_and_name($target, $file_name)) { $fields['id'] = $matching['id']; } elseif (isset($input['id']) && ($matching = Files::get($input['id']))) { $fields['id'] = $matching['id']; // silently delete the previous version of the file if (isset($matching['file_name'])) { Safe::unlink($file_path . '/' . $matching['file_name']); } } // prepare file record $fields['file_name'] = $file_name; $fields['file_size'] = filesize($context['path_to_root'] . $file_path . $file_name); $fields['file_href'] = ''; $fields['anchor'] = $target; // change title if (isset($_REQUEST['title'])) { $fields['title'] = $_REQUEST['title']; } // change has been documented if (!isset($_REQUEST['version']) || !$_REQUEST['version']) { $_REQUEST['version'] = ''; } else { $_REQUEST['version'] = ' - ' . $_REQUEST['version']; } // always remember file uploads, for traceability $_REQUEST['version'] = $fields['file_name'] . ' (' . Skin::build_number($fields['file_size'], i18n::s('bytes')) . ')' . $_REQUEST['version']; // add to file history $fields['description'] = Files::add_to_history($matching, $_REQUEST['version']); // if this is an image, maybe we can derive a thumbnail for it? if (Files::is_image($file_name)) { include_once $context['path_to_root'] . 'images/image.php'; Image::shrink($context['path_to_root'] . $file_path . $file_name, $context['path_to_root'] . $file_path . 'thumbs/' . $file_name); if (file_exists($context['path_to_root'] . $file_path . 'thumbs/' . $file_name)) { $fields['thumbnail_url'] = $context['url_to_home'] . $context['url_to_root'] . $file_path . 'thumbs/' . rawurlencode($file_name); } } // change active_set if (isset($_REQUEST['active_set'])) { $fields['active_set'] = $_REQUEST['active_set']; } // change source if (isset($_REQUEST['source'])) { $fields['source'] = $_REQUEST['source']; } // change keywords if (isset($_REQUEST['keywords'])) { $fields['keywords'] = $_REQUEST['keywords']; } // change alternate_href if (isset($_REQUEST['alternate_href'])) { $fields['alternate_href'] = $_REQUEST['alternate_href']; } // overlay, if any if (is_object($overlay)) { // allow for change detection $overlay->snapshot(); // update the overlay from form content $overlay->parse_fields($_REQUEST); // save content of the overlay in this item $fields['overlay'] = $overlay->save(); $fields['overlay_id'] = $overlay->get_id(); } // create the record in the database if (!($fields['id'] = Files::post($fields))) { return FALSE; } // record surfer activity Activities::post('file:' . $fields['id'], 'upload'); } } // so far so good if (count($context['uploaded_files']) == 1) { return $context['uploaded_files'][0]; } else { return $context['uploaded_files']; } } } // some error has occured return FALSE; }