View source
<?php
require_once dirname(__FILE__) . '/file_resup.field.inc';
define('FILE_RESUP_DEFAULT_CHUNKSIZE', 2 * 1024 * 1024);
define('FILE_RESUP_TEMPORARY', 'file_resup_temporary');
function file_resup_permission() {
return array(
'upload via file_resup' => array(
'title' => t('Upload via <em>File Resumable Upload</em>'),
),
);
}
function file_resup_menu() {
$items['file_resup/upload'] = array(
'page callback' => 'file_resup_upload',
'access arguments' => array(
'upload via file_resup',
),
'type' => MENU_CALLBACK,
);
return $items;
}
function file_resup_upload() {
$form_parents = func_get_args();
$form_build_id = (string) array_pop($form_parents);
if (empty($_REQUEST['form_build_id']) || $form_build_id != $_REQUEST['form_build_id']) {
drupal_exit();
}
if (empty($_REQUEST['resup_file_id']) || !($upload_id = file_resup_upload_id($_REQUEST['resup_file_id']))) {
drupal_exit();
}
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
$upload = file_resup_upload_load($upload_id);
if ($upload) {
return file_resup_plain_output($upload->uploaded_chunks);
}
$_POST['form_build_id'] = $form_build_id;
list($form) = ajax_get_form();
$form_parents = array_filter($form_parents, 'element_child');
$element = drupal_array_get_nested_value($form, $form_parents);
if (!$element) {
drupal_exit();
}
if (empty($_GET['resup_file_name']) || empty($_GET['resup_file_size'])) {
drupal_exit();
}
$filename = $_GET['resup_file_name'];
$filesize = $_GET['resup_file_size'];
if (strlen($filename) > 240) {
drupal_exit();
}
if (isset($element['#file_resup_upload_validators']['file_validate_extensions'][0])) {
$regex = '/\\.(?:' . preg_replace('/ +/', '|', preg_quote($element['#file_resup_upload_validators']['file_validate_extensions'][0])) . ')$/i';
if (!preg_match($regex, $filename)) {
drupal_exit();
}
}
if (!preg_match('`^[1-9]\\d*$`', $filesize) || $filesize > $element['#file_resup_upload_validators']['file_validate_size'][0]) {
drupal_exit();
}
$scheme = file_uri_scheme($element['#upload_location']);
if (!$scheme || !file_stream_wrapper_valid_scheme($scheme)) {
drupal_exit();
}
$directory = $scheme . '://' . FILE_RESUP_TEMPORARY;
if (!file_prepare_directory($directory, FILE_CREATE_DIRECTORY)) {
drupal_exit();
}
file_create_htaccess($directory, TRUE);
$upload = new stdClass();
$upload->upload_id = $upload_id;
$upload->filename = $filename;
$upload->filesize = $filesize;
$upload->scheme = $scheme;
$upload->timestamp = time();
try {
if (!drupal_write_record('file_resup', $upload)) {
drupal_exit();
}
} catch (Exception $e) {
drupal_exit();
}
file_resup_upload_delete_file($upload);
return file_resup_plain_output('0');
}
if (empty($_FILES['resup_chunk'])) {
drupal_exit();
}
$file = $_FILES['resup_chunk'];
if ($file['error'] != UPLOAD_ERR_OK || !is_uploaded_file($file['tmp_name']) || !$file['size'] || $file['size'] > file_resup_chunksize()) {
drupal_exit();
}
if (empty($_POST['resup_chunk_number']) || !preg_match('`^[1-9]\\d*$`', $_POST['resup_chunk_number'])) {
drupal_exit();
}
$chunk_number = (int) $_POST['resup_chunk_number'];
$upload = file_resup_upload_load($upload_id);
if (!$upload) {
drupal_exit();
}
if ($chunk_number > ceil($upload->filesize / file_resup_chunksize())) {
drupal_exit();
}
if ($chunk_number != $upload->uploaded_chunks + 1) {
return file_resup_plain_output($upload->uploaded_chunks);
}
$fp = @fopen(file_resup_upload_uri($upload), 'ab');
if (!$fp) {
drupal_exit();
}
if (!flock($fp, LOCK_EX)) {
fclose($fp);
drupal_exit();
}
$transaction = db_transaction();
try {
$affected = db_update('file_resup')
->fields(array(
'uploaded_chunks' => $chunk_number,
'timestamp' => time(),
))
->condition('upload_id', $upload_id)
->condition('uploaded_chunks', $chunk_number - 1)
->execute();
if (!$affected || ($contents = file_get_contents($file['tmp_name'])) === FALSE || !fwrite($fp, $contents)) {
throw new Exception();
}
} catch (Exception $e) {
$transaction
->rollback();
flock($fp, LOCK_UN);
fclose($fp);
drupal_exit();
}
unset($transaction);
fflush($fp);
flock($fp, LOCK_UN);
fclose($fp);
file_resup_plain_output($chunk_number);
}
function file_resup_plain_output($text = '') {
drupal_page_is_cacheable(FALSE);
drupal_add_http_header('Content-Type', 'text/plain');
echo $text;
}
function file_resup_save_upload($element, $resup_file_id) {
global $user;
$upload_id = file_resup_upload_id($resup_file_id);
if (!$upload_id) {
return FALSE;
}
$upload = file_resup_upload_load($upload_id);
if (!$upload) {
return FALSE;
}
if ($upload->fid) {
return file_load($upload->fid);
}
if ($upload->uploaded_chunks != ceil($upload->filesize / file_resup_chunksize())) {
return FALSE;
}
$destination = $element['#upload_location'];
$destination_scheme = file_uri_scheme($destination);
if (!$destination_scheme || $destination_scheme != $upload->scheme) {
return FALSE;
}
$upload_uri = file_resup_upload_uri($upload);
if (!file_exists($upload_uri)) {
return FALSE;
}
$file = new stdClass();
$file->uid = $user->uid;
$file->status = 0;
$file->filename = trim(drupal_basename($upload->filename), '.');
$file->uri = $upload_uri;
$file->filemime = file_get_mimetype($file->filename);
$file->filesize = $upload->filesize;
if (module_exists('transliteration') && variable_get('transliteration_file_uploads', TRUE)) {
$orig_filename = $file->filename;
$file->filename = transliteration_clean_filename($file->filename);
}
$validators = $element['#file_resup_upload_validators'];
$extensions = '';
if (isset($validators['file_validate_extensions'])) {
if (isset($validators['file_validate_extensions'][0])) {
$extensions = $validators['file_validate_extensions'][0];
}
else {
unset($validators['file_validate_extensions']);
}
}
else {
$extensions = 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp';
$validators['file_validate_extensions'][] = $extensions;
}
if (!empty($extensions)) {
$file->filename = file_munge_filename($file->filename, $extensions);
}
if (!variable_get('allow_insecure_uploads', 0) && preg_match('/\\.(php|pl|py|cgi|asp|js)(\\.|$)/i', $file->filename) && substr($file->filename, -4) != '.txt') {
$file->filemime = 'text/plain';
$file->uri .= '.txt';
$file->filename .= '.txt';
if (!empty($extensions)) {
$validators['file_validate_extensions'][0] .= ' txt';
drupal_set_message(t('For security reasons, your upload has been renamed to %filename.', array(
'%filename' => $file->filename,
)));
}
}
$element_parents = $element['#parents'];
if (end($element_parents) == 'resup') {
unset($element_parents[key($element_parents)]);
}
$form_field_name = implode('_', $element_parents);
$validators['file_validate_name_length'] = array();
$errors = file_validate($file, $validators);
if ($errors) {
$message = t('The specified file %name could not be uploaded.', array(
'%name' => $file->filename,
));
if (count($errors) > 1) {
$message .= theme('item_list', array(
'items' => $errors,
));
}
else {
$message .= ' ' . array_pop($errors);
}
form_set_error($form_field_name, $message);
return FALSE;
}
if (!file_prepare_directory($destination, FILE_CREATE_DIRECTORY)) {
watchdog('file_resup', 'The upload directory %directory for the file field !name could not be created or is not accessible. A newly uploaded file could not be saved in this directory as a consequence, and the upload was canceled.', array(
'%directory' => $destination,
'!name' => $element['#field_name'],
));
form_set_error($form_field_name, t('The file could not be uploaded.'));
return FALSE;
}
if (substr($destination, -1) != '/') {
$destination .= '/';
}
$destination = file_destination($destination . $file->filename, FILE_EXISTS_RENAME);
$file->uri = $destination;
if (!rename($upload_uri, $file->uri)) {
form_set_error($form_field_name, t('File upload error. Could not move uploaded file.'));
watchdog('file_resup', 'Upload error. Could not move uploaded file %file to destination %destination.', array(
'%file' => $file->filename,
'%destination' => $file->uri,
));
return FALSE;
}
drupal_chmod($file->uri);
if (isset($orig_filename) && !variable_get('transliteration_file_uploads_display_name', TRUE)) {
$file->filename = $orig_filename;
}
$file->file_resup_filesize = $upload->filesize;
$file = file_save($file);
if (!$file) {
return FALSE;
}
$upload->fid = $file->fid;
drupal_write_record('file_resup', $upload, 'upload_id');
return $file;
}
function file_resup_file_presave($file) {
if (isset($file->file_resup_filesize)) {
$file->filesize = $file->file_resup_filesize;
}
elseif (isset($file->original) && $file->original->filesize > PHP_INT_MAX) {
$file->filesize = $file->original->filesize;
}
}
function file_resup_file_insert($file) {
if ($file->filesize > PHP_INT_MAX) {
db_query('UPDATE {file_managed} SET filesize = :filesize WHERE fid = :fid', array(
':filesize' => $file->filesize,
':fid' => $file->fid,
));
}
}
function file_resup_file_update($file) {
file_resup_file_insert($file);
}
function file_resup_upload_load($upload_id) {
$upload = db_query('SELECT * FROM {file_resup} WHERE upload_id = :upload_id', array(
':upload_id' => $upload_id,
))
->fetchObject();
if (!empty($upload->fid)) {
$file = file_load($upload->fid);
if (!$file || !in_array(file_uri_scheme($file->uri), variable_get('file_public_schema', array(
'public',
))) && !file_download_access($file->uri)) {
file_resup_upload_delete_record($upload);
return;
}
}
return $upload;
}
function file_resup_upload_delete_record($upload) {
db_query('DELETE FROM {file_resup} WHERE upload_id = :upload_id', array(
':upload_id' => $upload->upload_id,
));
}
function file_resup_upload_delete_file($upload) {
$uri = file_resup_upload_uri($upload);
if (file_exists($uri)) {
file_unmanaged_delete($uri);
}
}
function file_resup_upload_uri($upload) {
return $upload->scheme . '://' . FILE_RESUP_TEMPORARY . '/' . $upload->upload_id;
}
function file_resup_upload_id($resup_file_id) {
global $user;
if (preg_match('`^[1-9]\\d*-\\d+-[\\w%]+$`', $resup_file_id)) {
$prefix = $user->uid ? $user->uid : str_replace('.', '_', $user->hostname);
return substr($prefix . '-' . $resup_file_id, 0, 240);
}
return FALSE;
}
function file_resup_cron() {
$result = db_query('SELECT * FROM {file_resup} WHERE timestamp < :timestamp', array(
':timestamp' => REQUEST_TIME - DRUPAL_MAXIMUM_TEMP_FILE_AGE,
));
foreach ($result as $upload) {
file_resup_upload_delete_record($upload);
file_resup_upload_delete_file($upload);
}
}
function file_resup_chunksize() {
return variable_get('file_resup_chunksize', FILE_RESUP_DEFAULT_CHUNKSIZE);
}