Esempio n. 1
0
/**
 * Handles the actual saving of attachments to a directory.
 *
 * What it does:
 * - Loops through $_FILES['attachment'] array and saves each file to the current attachments folder.
 * - Validates the save location actually exists.
 *
 * @package Attachments
 * @param int|null $id_msg = null or id of the message with attachments, if any.
 *                  If null, this is an upload in progress for a new post.
 */
function processAttachments($id_msg = null)
{
    global $context, $modSettings, $txt, $user_info, $ignore_temp, $topic, $board;
    $attach_errors = Attachment_Error_Context::context();
    // Make sure we're uploading to the right place.
    if (!empty($modSettings['automanage_attachments'])) {
        automanage_attachments_check_directory();
    }
    if (!is_array($modSettings['attachmentUploadDir'])) {
        $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']);
    }
    $context['attach_dir'] = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']];
    // Is the attachments folder actualy there?
    if (!empty($context['dir_creation_error'])) {
        $initial_error = $context['dir_creation_error'];
    } elseif (!is_dir($context['attach_dir'])) {
        $initial_error = 'attach_folder_warning';
        log_error(sprintf($txt['attach_folder_admin_warning'], $context['attach_dir']), 'critical');
    }
    if (!isset($initial_error) && !isset($context['attachments']['quantity'])) {
        // If this isn't a new post, check the current attachments.
        if (!empty($id_msg)) {
            list($context['attachments']['quantity'], $context['attachments']['total_size']) = attachmentsSizeForMessage($id_msg);
        } else {
            $context['attachments']['quantity'] = 0;
            $context['attachments']['total_size'] = 0;
        }
    }
    // Hmm. There are still files in session.
    $ignore_temp = false;
    if (!empty($_SESSION['temp_attachments']['post']['files']) && count($_SESSION['temp_attachments']) > 1) {
        // Let's try to keep them. But...
        $ignore_temp = true;
        // If new files are being added. We can't ignore those
        foreach ($_FILES['attachment']['tmp_name'] as $dummy) {
            if (!empty($dummy)) {
                $ignore_temp = false;
                break;
            }
        }
        // Need to make space for the new files. So, bye bye.
        if (!$ignore_temp) {
            foreach ($_SESSION['temp_attachments'] as $attachID => $attachment) {
                if (strpos($attachID, 'post_tmp_' . $user_info['id']) !== false) {
                    @unlink($attachment['tmp_name']);
                }
            }
            $attach_errors->activate()->addError('temp_attachments_flushed');
            $_SESSION['temp_attachments'] = array();
        }
    }
    if (!isset($_FILES['attachment']['name'])) {
        $_FILES['attachment']['tmp_name'] = array();
    }
    if (!isset($_SESSION['temp_attachments'])) {
        $_SESSION['temp_attachments'] = array();
    }
    // Remember where we are at. If it's anywhere at all.
    if (!$ignore_temp) {
        $_SESSION['temp_attachments']['post'] = array('msg' => !empty($id_msg) ? $id_msg : 0, 'last_msg' => !empty($_REQUEST['last_msg']) ? $_REQUEST['last_msg'] : 0, 'topic' => !empty($topic) ? $topic : 0, 'board' => !empty($board) ? $board : 0);
    }
    // If we have an initial error, lets just display it.
    if (!empty($initial_error)) {
        $_SESSION['temp_attachments']['initial_error'] = $initial_error;
        // This is a generic error
        $attach_errors->activate();
        $attach_errors->addError('attach_no_upload');
        $attach_errors->addError(is_array($attachment) ? array($attachment[0], $attachment[1]) : $attachment);
        // And delete the files 'cos they ain't going nowhere.
        foreach ($_FILES['attachment']['tmp_name'] as $n => $dummy) {
            if (file_exists($_FILES['attachment']['tmp_name'][$n])) {
                unlink($_FILES['attachment']['tmp_name'][$n]);
            }
        }
        $_FILES['attachment']['tmp_name'] = array();
    }
    // Loop through $_FILES['attachment'] array and move each file to the current attachments folder.
    foreach ($_FILES['attachment']['tmp_name'] as $n => $dummy) {
        if ($_FILES['attachment']['name'][$n] == '') {
            continue;
        }
        // First, let's first check for PHP upload errors.
        $errors = attachmentUploadChecks($n);
        // Set the names and destination for this file
        $attachID = 'post_tmp_' . $user_info['id'] . '_' . md5(mt_rand());
        $destName = $context['attach_dir'] . '/' . $attachID;
        // If we are error free, Try to move and rename the file before doing more checks on it.
        if (empty($errors)) {
            $_SESSION['temp_attachments'][$attachID] = array('name' => htmlspecialchars(basename($_FILES['attachment']['name'][$n]), ENT_COMPAT, 'UTF-8'), 'tmp_name' => $destName, 'attachid' => $attachID, 'size' => $_FILES['attachment']['size'][$n], 'type' => $_FILES['attachment']['type'][$n], 'id_folder' => $modSettings['currentAttachmentUploadDir'], 'errors' => array());
            // Move the file to the attachments folder with a temp name for now.
            if (@move_uploaded_file($_FILES['attachment']['tmp_name'][$n], $destName)) {
                @chmod($destName, 0644);
            } else {
                $_SESSION['temp_attachments'][$attachID]['errors'][] = 'attach_timeout';
                if (file_exists($_FILES['attachment']['tmp_name'][$n])) {
                    unlink($_FILES['attachment']['tmp_name'][$n]);
                }
            }
        } else {
            $_SESSION['temp_attachments'][$attachID] = array('name' => htmlspecialchars(basename($_FILES['attachment']['name'][$n]), ENT_COMPAT, 'UTF-8'), 'tmp_name' => $destName, 'errors' => $errors);
            if (file_exists($_FILES['attachment']['tmp_name'][$n])) {
                unlink($_FILES['attachment']['tmp_name'][$n]);
            }
        }
        // If there were no errors to this pont, we apply some addtional checks
        if (empty($_SESSION['temp_attachments'][$attachID]['errors'])) {
            attachmentChecks($attachID);
        }
        // Sort out the errors for display and delete any associated files.
        if (!empty($_SESSION['temp_attachments'][$attachID]['errors'])) {
            $attach_errors->addAttach($attachID, $_SESSION['temp_attachments'][$attachID]['name']);
            $log_these = array('attachments_no_create', 'attachments_no_write', 'attach_timeout', 'ran_out_of_space', 'cant_access_upload_path', 'attach_0_byte_file');
            foreach ($_SESSION['temp_attachments'][$attachID]['errors'] as $error) {
                if (!is_array($error)) {
                    $attach_errors->addError($error);
                    if (in_array($error, $log_these)) {
                        log_error($_SESSION['temp_attachments'][$attachID]['name'] . ': ' . $txt[$error], 'critical');
                    }
                } else {
                    $attach_errors->addError(array($error[0], $error[1]));
                }
            }
        }
    }
    // Mod authors, finally a hook to hang an alternate attachment upload system upon
    // Upload to the current attachment folder with the file name $attachID or 'post_tmp_' . $user_info['id'] . '_' . md5(mt_rand())
    // Populate $_SESSION['temp_attachments'][$attachID] with the following:
    //   name => The file name
    //   tmp_name => Path to the temp file ($context['attach_dir'] . '/' . $attachID).
    //   size => File size (required).
    //   type => MIME type (optional if not available on upload).
    //   id_folder => $modSettings['currentAttachmentUploadDir']
    //   errors => An array of errors (use the index of the $txt variable for that error).
    // Template changes can be done using "integrate_upload_template".
    call_integration_hook('integrate_attachment_upload');
}
    /**
     * Maintenance function to move attachments from one directory to another
     */
    public function action_transfer()
    {
        global $modSettings, $txt;
        checkSession();
        // We will need the functions from here
        require_once SUBSDIR . '/Attachments.subs.php';
        require_once SUBSDIR . '/ManageAttachments.subs.php';
        // The list(s) of directory's that are available.
        $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']);
        if (!empty($modSettings['attachment_basedirectories'])) {
            $modSettings['attachment_basedirectories'] = unserialize($modSettings['attachment_basedirectories']);
        } else {
            $modSettings['basedirectory_for_attachments'] = array();
        }
        // Clean the inputs
        $_POST['from'] = (int) $_POST['from'];
        $_POST['auto'] = !empty($_POST['auto']) ? (int) $_POST['auto'] : 0;
        $_POST['to'] = (int) $_POST['to'];
        $start = !empty($_POST['empty_it']) ? 0 : $modSettings['attachmentDirFileLimit'];
        $_SESSION['checked'] = !empty($_POST['empty_it']) ? true : false;
        // Prepare for the moving
        $limit = 501;
        $results = array();
        $dir_files = 0;
        $current_progress = 0;
        $total_moved = 0;
        $total_not_moved = 0;
        // Need to know where we are moving things from
        if (empty($_POST['from']) || empty($_POST['auto']) && empty($_POST['to'])) {
            $results[] = $txt['attachment_transfer_no_dir'];
        }
        // Same location, that's easy
        if ($_POST['from'] == $_POST['to']) {
            $results[] = $txt['attachment_transfer_same_dir'];
        }
        // No errors so determine how many we may have to move
        if (empty($results)) {
            // Get the total file count for the progress bar.
            $total_progress = getFolderAttachmentCount($_POST['from']);
            $total_progress -= $start;
            if ($total_progress < 1) {
                $results[] = $txt['attachment_transfer_no_find'];
            }
        }
        // Nothing to move (no files in source or below the max limit)
        if (empty($results)) {
            // Moving them automaticaly?
            if (!empty($_POST['auto'])) {
                $modSettings['automanage_attachments'] = 1;
                // Create sub directroys off the root or from an attachment directory?
                $modSettings['use_subdirectories_for_attachments'] = $_POST['auto'] == -1 ? 0 : 1;
                $modSettings['basedirectory_for_attachments'] = $_POST['auto'] > 0 ? $modSettings['attachmentUploadDir'][$_POST['auto']] : $modSettings['basedirectory_for_attachments'];
                // Finaly, where do they need to go
                automanage_attachments_check_directory();
                $new_dir = $modSettings['currentAttachmentUploadDir'];
            } else {
                $new_dir = $_POST['to'];
            }
            $modSettings['currentAttachmentUploadDir'] = $new_dir;
            $break = false;
            while ($break === false) {
                @set_time_limit(300);
                if (function_exists('apache_reset_timeout')) {
                    @apache_reset_timeout();
                }
                // If limits are set, get the file count and size for the destination folder
                if ($dir_files <= 0 && (!empty($modSettings['attachmentDirSizeLimit']) || !empty($modSettings['attachmentDirFileLimit']))) {
                    $current_dir = attachDirProperties($new_dir);
                    $dir_files = $current_dir['files'];
                    $dir_size = $current_dir['size'];
                }
                // Find some attachments to move
                list($tomove_count, $tomove) = findAttachmentsToMove($_POST['from'], $start, $limit);
                // Nothing found to move
                if ($tomove_count === 0) {
                    if (empty($current_progress)) {
                        $results[] = $txt['attachment_transfer_no_find'];
                    }
                    break;
                }
                // No more to move after this batch then set the finished flag.
                if ($tomove_count < $limit) {
                    $break = true;
                }
                // Move them
                $moved = array();
                foreach ($tomove as $row) {
                    $source = getAttachmentFilename($row['filename'], $row['id_attach'], $row['id_folder'], false, $row['file_hash']);
                    $dest = $modSettings['attachmentUploadDir'][$new_dir] . '/' . basename($source);
                    // Size and file count check
                    if (!empty($modSettings['attachmentDirSizeLimit']) || !empty($modSettings['attachmentDirFileLimit'])) {
                        $dir_files++;
                        $dir_size += !empty($row['size']) ? $row['size'] : filesize($source);
                        // If we've reached a directory limit. Do something if we are in auto mode, otherwise set an error.
                        if (!empty($modSettings['attachmentDirSizeLimit']) && $dir_size > $modSettings['attachmentDirSizeLimit'] * 1024 || !empty($modSettings['attachmentDirFileLimit']) && $dir_files > $modSettings['attachmentDirFileLimit']) {
                            // Since we're in auto mode. Create a new folder and reset the counters.
                            if (!empty($_POST['auto'])) {
                                automanage_attachments_by_space();
                                $results[] = sprintf($txt['attachments_transfered'], $total_moved, $modSettings['attachmentUploadDir'][$new_dir]);
                                if (!empty($total_not_moved)) {
                                    $results[] = sprintf($txt['attachments_not_transfered'], $total_not_moved);
                                }
                                $dir_files = 0;
                                $total_moved = 0;
                                $total_not_moved = 0;
                                $break = false;
                                break;
                            } else {
                                $results[] = $txt['attachment_transfer_no_room'];
                                $break = true;
                                break;
                            }
                        }
                    }
                    // Actually move the file
                    if (@rename($source, $dest)) {
                        $total_moved++;
                        $current_progress++;
                        $moved[] = $row['id_attach'];
                    } else {
                        $total_not_moved++;
                    }
                }
                // Update the database to reflect the new file location
                if (!empty($moved)) {
                    moveAttachments($moved, $new_dir);
                }
                $new_dir = $modSettings['currentAttachmentUploadDir'];
                // Create / update the progress bar.
                // @todo why was this done this way?
                if (!$break) {
                    $percent_done = min(round($current_progress / $total_progress * 100, 0), 100);
                    $prog_bar = '
						<div class="progress_bar">
							<div class="full_bar">' . $percent_done . '%</div>
							<div class="green_percent" style="width: ' . $percent_done . '%;">&nbsp;</div>
						</div>';
                    // Write it to a file so it can be displayed
                    $fp = fopen(BOARDDIR . '/progress.php', 'w');
                    fwrite($fp, $prog_bar);
                    fclose($fp);
                    usleep(500000);
                }
            }
            $results[] = sprintf($txt['attachments_transfered'], $total_moved, $modSettings['attachmentUploadDir'][$new_dir]);
            if (!empty($total_not_moved)) {
                $results[] = sprintf($txt['attachments_not_transfered'], $total_not_moved);
            }
        }
        // All done, time to clean up
        $_SESSION['results'] = $results;
        if (file_exists(BOARDDIR . '/progress.php')) {
            unlink(BOARDDIR . '/progress.php');
        }
        redirectexit('action=admin;area=manageattachments;sa=maintenance#transfer');
    }
Esempio n. 3
0
function processAttachments()
{
    global $context, $modSettings, $smcFunc, $txt, $user_info;
    // Make sure we're uploading to the right place.
    if (!empty($modSettings['automanage_attachments'])) {
        automanage_attachments_check_directory();
    }
    if (!is_array($modSettings['attachmentUploadDir'])) {
        $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']);
    }
    $context['attach_dir'] = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']];
    // Is the attachments folder actualy there?
    if (!empty($context['dir_creation_error'])) {
        $initial_error = $context['dir_creation_error'];
    } elseif (!is_dir($context['attach_dir'])) {
        $initial_error = 'attach_folder_warning';
        log_error(sprintf($txt['attach_folder_admin_warning'], $context['attach_dir']), 'critical');
    }
    if (!isset($initial_error) && !isset($context['attachments'])) {
        // If this isn't a new post, check the current attachments.
        if (isset($_REQUEST['msg'])) {
            $request = $smcFunc['db_query']('', '
				SELECT COUNT(*), SUM(size)
				FROM {db_prefix}attachments
				WHERE id_msg = {int:id_msg}
					AND attachment_type = {int:attachment_type}', array('id_msg' => (int) $_REQUEST['msg'], 'attachment_type' => 0));
            list($context['attachments']['quantity'], $context['attachments']['total_size']) = $smcFunc['db_fetch_row']($request);
            $smcFunc['db_free_result']($request);
        } else {
            $context['attachments'] = array('quantity' => 0, 'total_size' => 0);
        }
    }
    // Hmm. There are still files in session.
    $ignore_temp = false;
    if (!empty($_SESSION['temp_attachments']['post']['files']) && count($_SESSION['temp_attachments']) > 1) {
        // Let's try to keep them. But...
        $ignore_temp = true;
        // If new files are being added. We can't ignore those
        foreach ($_FILES['attachment']['tmp_name'] as $dummy) {
            if (!empty($dummy)) {
                $ignore_temp = false;
                break;
            }
        }
        // Need to make space for the new files. So, bye bye.
        if (!$ignore_temp) {
            foreach ($_SESSION['temp_attachments'] as $attachID => $attachment) {
                if (strpos($attachID, 'post_tmp_' . $user_info['id']) !== false) {
                    unlink($attachment['tmp_name']);
                }
            }
            $context['we_are_history'] = $txt['error_temp_attachments_flushed'];
            $_SESSION['temp_attachments'] = array();
        }
    }
    if (!isset($_FILES['attachment']['name'])) {
        $_FILES['attachment']['tmp_name'] = array();
    }
    if (!isset($_SESSION['temp_attachments'])) {
        $_SESSION['temp_attachments'] = array();
    }
    // Remember where we are at. If it's anywhere at all.
    if (!$ignore_temp) {
        $_SESSION['temp_attachments']['post'] = array('msg' => !empty($_REQUEST['msg']) ? $_REQUEST['msg'] : 0, 'last_msg' => !empty($_REQUEST['last_msg']) ? $_REQUEST['last_msg'] : 0, 'topic' => !empty($topic) ? $topic : 0, 'board' => !empty($board) ? $board : 0);
    }
    // If we have an itital error, lets just display it.
    if (!empty($initial_error)) {
        $_SESSION['temp_attachments']['initial_error'] = $initial_error;
        // And delete the files 'cos they ain't going nowhere.
        foreach ($_FILES['attachment']['tmp_name'] as $n => $dummy) {
            if (file_exists($_FILES['attachment']['tmp_name'][$n])) {
                unlink($_FILES['attachment']['tmp_name'][$n]);
            }
        }
        $_FILES['attachment']['tmp_name'] = array();
    }
    // Loop through $_FILES['attachment'] array and move each file to the current attachments folder.
    foreach ($_FILES['attachment']['tmp_name'] as $n => $dummy) {
        if ($_FILES['attachment']['name'][$n] == '') {
            continue;
        }
        // First, let's first check for PHP upload errors.
        $errors = array();
        if (!empty($_FILES['attachment']['error'][$n])) {
            if ($_FILES['attachment']['error'][$n] == 2) {
                $errors[] = array('file_too_big', array($modSettings['attachmentSizeLimit']));
            } elseif ($_FILES['attachment']['error'][$n] == 6) {
                log_error($_FILES['attachment']['name'][$n] . ': ' . $txt['php_upload_error_6'], 'critical');
            } else {
                log_error($_FILES['attachment']['name'][$n] . ': ' . $txt['php_upload_error_' . $_FILES['attachment']['error'][$n]]);
            }
            if (empty($errors)) {
                $errors[] = 'attach_php_error';
            }
        }
        // Try to move and rename the file before doing any more checks on it.
        $attachID = 'post_tmp_' . $user_info['id'] . '_' . md5(mt_rand());
        $destName = $context['attach_dir'] . '/' . $attachID;
        if (empty($errors)) {
            $_SESSION['temp_attachments'][$attachID] = array('name' => htmlspecialchars(basename($_FILES['attachment']['name'][$n])), 'tmp_name' => $destName, 'size' => $_FILES['attachment']['size'][$n], 'type' => $_FILES['attachment']['type'][$n], 'id_folder' => $modSettings['currentAttachmentUploadDir'], 'errors' => array());
            // Move the file to the attachments folder with a temp name for now.
            if (@move_uploaded_file($_FILES['attachment']['tmp_name'][$n], $destName)) {
                @chmod($destName, 0644);
            } else {
                $_SESSION['temp_attachments'][$attachID]['errors'][] = 'attach_timeout';
                if (file_exists($_FILES['attachment']['tmp_name'][$n])) {
                    unlink($_FILES['attachment']['tmp_name'][$n]);
                }
            }
        } else {
            $_SESSION['temp_attachments'][$attachID] = array('name' => htmlspecialchars(basename($_FILES['attachment']['name'][$n])), 'tmp_name' => $destName, 'errors' => $errors);
            if (file_exists($_FILES['attachment']['tmp_name'][$n])) {
                unlink($_FILES['attachment']['tmp_name'][$n]);
            }
        }
        // If there's no errors to this pont. We still do need to apply some addtional checks before we are finished.
        if (empty($_SESSION['temp_attachments'][$attachID]['errors'])) {
            attachmentChecks($attachID);
        }
    }
    // Mod authors, finally a hook to hang an alternate attachment upload system upon
    // Upload to the current attachment folder with the file name $attachID or 'post_tmp_' . $user_info['id'] . '_' . md5(mt_rand())
    // Populate $_SESSION['temp_attachments'][$attachID] with the following:
    //   name => The file name
    //   tmp_name => Path to the temp file ($context['attach_dir'] . '/' . $attachID).
    //   size => File size (required).
    //   type => MIME type (optional if not available on upload).
    //   id_folder => $modSettings['currentAttachmentUploadDir']
    //   errors => An array of errors (use the index of the $txt variable for that error).
    // Template changes can be done using "integrate_upload_template".
    call_integration_hook('integrate_attachment_upload', array());
}