<?php /*

 Composr
 Copyright (c) ocProducts, 2004-2016

 See text/EN/licence.txt for full licencing information.


 NOTE TO PROGRAMMERS:
   Do not edit this file. If you need to make changes, save your changed file to the appropriate *_custom folder
   **** If you ignore this advice, then your website upgrades (e.g. for bug fixes) will likely kill your changes ****

*/

/**
 * @license    http://opensource.org/licenses/cpal_1.0 Common Public Attribution License
 * @copyright  ocProducts Ltd
 * @package    core
 */

/**
 * Standard code module initialisation function.
 *
 * @ignore
 */
function init__uploads()
{
    if (!defined('CMS_UPLOAD_ANYTHING')) {
        define('CMS_UPLOAD_IMAGE', 1);
        define('CMS_UPLOAD_VIDEO', 2);
        define('CMS_UPLOAD_AUDIO', 4);
        define('CMS_UPLOAD_SWF', 8); // Banners
        define('CMS_UPLOAD_ANYTHING', 15);

        define('OBFUSCATE_NEVER', 0);
        define('OBFUSCATE_LEAVE_SUFFIX', 1);
        define('OBFUSCATE_BIN_SUFFIX', 2);
        define('OBFUSCATE_LAX', 3); // TODO: Remove in v11
    }

    require_code('images_cleanup_pipeline');
    require_code('urls_simplifier');

    reset_images_cleanup_pipeline_settings();
}

/**
 * Reset the images cleanup pipeline settings.
 */
function reset_images_cleanup_pipeline_settings()
{
    global $ICPS__RECOMPRESS_MODE, $ICPS__MAXIMUM_DIMENSION, $ICPS__WATERMARKS, $ICPS__STRIP_GPS;
    $ICPS__RECOMPRESS_MODE = IMG_RECOMPRESS_LOSSLESS;
    $ICPS__MAXIMUM_DIMENSION = null;
    $ICPS__WATERMARKS = null;
    $ICPS__STRIP_GPS = true;
}

/**
 * Set the images cleanup pipeline settings.
 *
 * @param  integer $recompress_mode How to recompress, an IMG_RECOMPRESS_* constant
 * @param  ?integer $maximum_dimension The size of the bounding box (null: none)
 * @param  ?array $watermarks Watermark corners (top-left, top-right, bottom-left, bottom-right) (null: none)
 * @param  boolean $strip_gps Whether to strip GPS metadata
 */
function set_images_cleanup_pipeline_settings($recompress_mode = 0, $maximum_dimension = null, $watermarks = null, $strip_gps = true)
{
    global $ICPS__RECOMPRESS_MODE, $ICPS__MAXIMUM_DIMENSION, $ICPS__WATERMARKS, $ICPS__STRIP_GPS;
    $ICPS__RECOMPRESS_MODE = $recompress_mode;
    $ICPS__MAXIMUM_DIMENSION = $maximum_dimension;
    $ICPS__WATERMARKS = $watermarks;
    $ICPS__STRIP_GPS = $strip_gps;
}

/**
 * An upload has been passed through by POST, either as a file (a new upload), a url, or as a filedump reference.
 * For images use post_param_image instead.
 * Used with form_input_upload_multi_source.
 * Get the file URL from the POST data.
 *
 * @param  ID_TEXT $name Form field prefix (input type suffixes will be added automatically)
 * @param  ?PATH $upload_to Where to upload to (null: the correct place for $theme_image_type)
 * @param  boolean $required Whether an image is required
 * @param  boolean $is_edit Whether this is an edit operation
 * @param  ?string $filename Pass the filename back by reference (null: do not pass)
 * @param  ?string $thumb_url Pass the thumbnail back by reference (null: do not pass & do not collect a thumbnail)
 * @param  integer $upload_type A CMS_UPLOAD_* constant
 * @param  boolean $copy_to_server Whether to copy a URL (if a URL) to the server, and return a local reference
 * @param  integer $obfuscate Whether to obfuscate file names so the URLs can not be guessed/derived (a OBFUSCATE_* constant)
 * @return ?URLPATH The URL (either to an independent upload, or the theme image, or a filedump URL) (null: leave alone, when doing an edit operation)
 */
function post_param_multi_source_upload($name, $upload_to, $required = true, $is_edit = false, &$filename = null, &$thumb_url = null, $upload_type = 15, $copy_to_server = false, $obfuscate = 0)
{
    $thumb_specify_name = $name . '__thumb__url';
    $test = post_param_string($thumb_specify_name, '');
    if ($test == '') {
        $thumb_specify_name = $name . '__thumb__filedump';
    }

    // Upload
    // ------

    require_code('uploads');
    $field_file = $name . '__upload';
    $thumb_attach_name = $name . '__thumb__upload';
    is_plupload(true);
    if (((array_key_exists($field_file, $_FILES)) && ((is_plupload()) || (is_uploaded_file($_FILES[$field_file]['tmp_name']))))) {
        $urls = get_url('', $field_file, $upload_to, $obfuscate, $upload_type, $thumb_url !== null, $thumb_specify_name, $thumb_attach_name);

        if ((substr($urls[0], 0, 8) != 'uploads/') && (is_null(http_download_file($urls[0], 0, false))) && (!is_null($GLOBALS['HTTP_MESSAGE_B']))) {
            attach_message($GLOBALS['HTTP_MESSAGE_B'], 'warn');
        }

        if ($thumb_url !== null) {
            $thumb_url = $urls[1];
        }
        $filename = $urls[2];

        return cms_rawurlrecode($urls[0]);
    }

    // URL
    // ---

    $field_url = $name . '__url';
    $url = post_param_string($field_url, '');
    if ($url != '') {
        // We should use compliant encoding
        $coder_ob = new HarmlessURLCoder(); // TODO: This should move into post_param_string in v11, filter based
        $url = $coder_ob->encode($url);

        $filename = urldecode(preg_replace('#\?.*#', '', basename($url)));

        // Get thumbnail
        $urls = get_url($field_url, '', $upload_to, 0, $upload_type, $thumb_url !== null, $thumb_specify_name, $thumb_attach_name, $copy_to_server);
        if ($thumb_url !== null) {
            $thumb_url = $urls[1];
        }

        return cms_rawurlrecode($url);
    }

    // Filedump
    // --------

    if (addon_installed('filedump')) {
        $field_filedump = $name . '__filedump';
        $url = post_param_string($field_filedump, '');
        if ($url != '') {
            $filename = urldecode(basename($url));

            // Get thumbnail
            $urls = get_url($field_filedump, '', $upload_to, 0, $upload_type, $thumb_url !== null, $thumb_specify_name, $thumb_attach_name);
            if ($thumb_url !== null) {
                $thumb_url = $urls[1];
            }

            return cms_rawurlrecode($url);
        }
    }

    // ---

    if (!$required) {
        if (($is_edit) && (post_param_integer($field_file . '_unlink', 0) != 1)) {
            return null;
        }

        return '';
    }

    warn_exit(do_lang_tempcode('IMPROPERLY_FILLED_IN_UPLOAD'));
}

/**
 * Find whether an plupload upload has just happened, and optionally simulate as if it were a normal upload (although 'is_uploaded_file'/'move_uploaded_file' would not work).
 *
 * @param  boolean $fake_prepopulation Simulate population of the $_FILES array.
 * @return boolean Whether an plupload upload has just happened.
 */
function is_plupload($fake_prepopulation = false)
{
    static $done_fake_prepopulation = false;

    $plupload = false;
    $rolling_offset = 0;
    foreach ($_POST as $key => $value) {
        if (!is_string($value)) {
            continue;
        }
        if (!is_string($key)) {
            $key = strval($key);
        }

        if ((preg_match('#^hidFileID\_#i', $key) != 0) && ($value != '-1')) {
            // Get the incoming uploads appropriate database table row
            if (substr($value, -4) == '.bin') { // By .bin name
                $filename = post_param_string(str_replace('hidFileID', 'hidFileName', $key), '');
                if ($filename == '') {
                    continue; // Was cancelled during plupload, but plupload can't cancel so was allowed to finish. So we have hidFileID but not hidFileName.
                }

                $path = 'uploads/incoming/' . filter_naughty($value);
                if (file_exists(get_custom_file_base() . '/' . $path)) {
                    $plupload = true;
                    if ($fake_prepopulation) {
                        $_FILES[substr($key, 10)] = array(
                            'type' => 'plupload',
                            'name' => $filename,
                            'tmp_name' => get_custom_file_base() . '/' . $path,
                            'size' => filesize(get_custom_file_base() . '/' . $path)
                        );
                    }
                }
            } else { // By incoming upload ID
                $rolling_offset = 0; // We do assume that if we have multiple multi-file fields in the same space that they are spaced with a large enough gap; so we don't maintain a rolling offset between fields
                foreach (array_map('intval', explode(':', $value)) as $i => $incoming_uploads_id) { // Some uploaders may delimite with ":" within a single POST field (plupload); others may give multiple POST fields (plupload, native)
                    $incoming_uploads_row = $GLOBALS['SITE_DB']->query('SELECT * FROM ' . get_table_prefix() . 'incoming_uploads WHERE (i_submitter=' . strval(get_member()) . ' OR i_submitter=' . strval($GLOBALS['FORUM_DRIVER']->get_guest_id()) . ') AND id=' . strval($incoming_uploads_id), 1);
                    if (array_key_exists(0, $incoming_uploads_row)) {
                        if (file_exists(get_custom_file_base() . '/' . $incoming_uploads_row[0]['i_save_url'])) {
                            $plupload = true;
                            if ($fake_prepopulation) {
                                if (!$done_fake_prepopulation) {
                                    $new_key = $key;
                                    $matches = array();
                                    if (preg_match('#^hidFileID\_(.*)(\d+)$#', $key, $matches) != 0) {
                                        $new_key = $matches[1] . strval(intval($matches[2]) + $rolling_offset);
                                    } else {
                                        $new_key = substr($key, 10);
                                    }
                                    $_FILES[$new_key] = array(
                                        'type' => 'plupload',
                                        'name' => $incoming_uploads_row[0]['i_orig_filename'],
                                        'tmp_name' => get_custom_file_base() . '/' . $incoming_uploads_row[0]['i_save_url'],
                                        'size' => filesize(get_custom_file_base() . '/' . $incoming_uploads_row[0]['i_save_url'])
                                    );
                                    $_POST['hidFileID_' . $new_key] = strval($incoming_uploads_id);

                                    $rolling_offset++;
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    if ($plupload) {
        // Filter out vestigial files (been reported as an issue)
        foreach (array_keys($_FILES) as $attach_name) {
            if ((array_key_exists($attach_name, $_FILES)) && (array_key_exists('error', $_FILES[$attach_name]))) {
                if ($_FILES[$attach_name]['error'] == 3) {
                    unset($_FILES[$attach_name]);
                }
            }
        }
    }

    if ($fake_prepopulation) {
        $done_fake_prepopulation = true;
    }

    return $plupload;
}

/**
 * Get URLs generated according to the specified information. It can also generate a thumbnail if required. It first tries attached upload, then URL, then fails.
 *
 * @param  ID_TEXT $specify_name The name of the POST parameter storing the URL (if '', then no POST parameter). Parameter value may be blank.
 * @param  ID_TEXT $attach_name The name of the HTTP file parameter storing the upload (if '', then no HTTP file parameter). No file necessarily is uploaded under this.
 * @param  ID_TEXT $upload_folder The folder name where we will put this upload
 * @param  integer $obfuscate Whether to obfuscate file names so the URLs can not be guessed/derived (a OBFUSCATE_* constant)
 * @param  integer $enforce_type The type of upload it is (bitmask, from CMS_UPLOAD_* constants)
 * @param  boolean $make_thumbnail Make a thumbnail (this only makes sense, if it is an image)
 * @param  ID_TEXT $thumb_specify_name The name of the POST parameter storing the thumb URL. As before
 * @param  ID_TEXT $thumb_attach_name The name of the HTTP file parameter storing the thumb upload. As before
 * @param  boolean $copy_to_server Whether to copy a URL (if a URL) to the server, and return a local reference
 * @param  boolean $accept_errors Whether to accept upload errors
 * @param  boolean $should_get_something Whether to give a (deferred?) error if no file was given at all
 * @param  boolean $only_make_smaller Whether to apply a 'never make the image bigger' rule for thumbnail creation (would affect very small images)
 * @param  ?MEMBER $member_id Member ID to run permissions with (null: current member)
 * @param  ?PATH $upload_folder_full Full path to upload folder, in case it is not relative to the base directory (null: work out)
 * @param  ?PATH $thumb_folder_full Full path to thumb folder, in case it is not relative to the base directory (null: work out)
 * @param  ?string $filename Filename to use (null: choose one)
 * @return array An array of 4 URL bits (URL, thumb URL, URL original filename, thumb original filename)
 */
function get_url($specify_name, $attach_name, $upload_folder, $obfuscate = 0, $enforce_type = 15, $make_thumbnail = false, $thumb_specify_name = '', $thumb_attach_name = '', $copy_to_server = false, $accept_errors = false, $should_get_something = false, $only_make_smaller = false, $member_id = null, $upload_folder_full = null, $thumb_folder_full = null, $filename = null)
{
    require_code('files2');

    if (is_null($member_id)) {
        $member_id = get_member();
    }

    $upload_folder = filter_naughty($upload_folder);
    if (is_null($upload_folder_full)) {
        $upload_folder_full = get_custom_file_base() . '/' . $upload_folder;
    }
    $thumb_folder = preg_replace('#^(uploads/[^/]+)#', '${1}_thumbs', $upload_folder);
    if (is_null($thumb_folder_full)) {
        $thumb_folder_full = get_custom_file_base() . '/' . $thumb_folder;
    }

    $out = array();
    $thumb = null;

    $filearrays = array();
    get_upload_filearray($attach_name, $filearrays);
    if ($thumb_specify_name != '') {
        get_upload_filearray($thumb_specify_name, $filearrays);
    }

    $plupload_uploaded = false;
    $plupload_uploaded_thumb = false;
    foreach (array($attach_name, $thumb_attach_name) as $i => $_attach_name) {
        if ($_attach_name == '') {
            continue;
        }

        // Check if it is an incoming upload
        $row_id_file = 'hidFileID_' . $_attach_name;
        $row_id_file_value = post_param_string($row_id_file, null);
        if ($row_id_file_value == '-1') {
            $row_id_file_value = null;
        }

        // ID of the upload from the incoming uploads database table
        if (!is_null($row_id_file_value)) { // plupload was used
            // Get the incoming upload's appropriate DB table row
            if ((substr($row_id_file_value, -4) == '.bin') && (strpos($row_id_file_value, ':') === false)) {
                $path = 'uploads/incoming/' . filter_naughty($row_id_file_value);
                if (file_exists(get_custom_file_base() . '/' . $path)) {
                    $filearrays[$_attach_name] = array('type' => 'plupload', 'name' => post_param_string(str_replace('hidFileID', 'hidFileName', $row_id_file)), 'tmp_name' => get_custom_file_base() . '/' . $path, 'size' => filesize(get_custom_file_base() . '/' . $path));
                    $_FILES[$_attach_name] = $filearrays[$_attach_name];
                    if ($i == 0) {
                        $plupload_uploaded = true;
                    } else {
                        $plupload_uploaded_thumb = true;
                    }
                }
            } else {
                $incoming_uploads_id = intval(preg_replace('#:.*$#', '', $row_id_file_value));
                $incoming_uploads_row = $GLOBALS['SITE_DB']->query('SELECT * FROM ' . get_table_prefix() . 'incoming_uploads WHERE (i_submitter=' . strval(get_member()) . ' OR i_submitter=' . strval($GLOBALS['FORUM_DRIVER']->get_guest_id()) . ') AND id=' . strval($incoming_uploads_id), 1);
                // If there is a DB record, proceed
                if (array_key_exists(0, $incoming_uploads_row)) {
                    if (file_exists(get_custom_file_base() . '/' . $incoming_uploads_row[0]['i_save_url'])) {
                        $filearrays[$_attach_name] = array('type' => 'plupload', 'name' => $incoming_uploads_row[0]['i_orig_filename'], 'tmp_name' => get_custom_file_base() . '/' . $incoming_uploads_row[0]['i_save_url'], 'size' => filesize(get_custom_file_base() . '/' . $incoming_uploads_row[0]['i_save_url']));
                        $_FILES[$_attach_name] = $filearrays[$_attach_name];
                        if ($i == 0) {
                            $plupload_uploaded = true;
                        } else {
                            $plupload_uploaded_thumb = true;
                        }
                    }
                }
            }
        }
    }

    if (count($filearrays) != 0) {
        cms_profile_start_for('get_url');
    }

    if ($obfuscate == 3) {
        $accept_errors = true;
    }

    if (!file_exists($upload_folder_full)) {
        require_code('files2');
        make_missing_directory($upload_folder_full);
    }
    if ((!file_exists($thumb_folder_full)) && ($make_thumbnail)) {
        require_code('files2');
        make_missing_directory($thumb_folder_full);
    }

    // Find URL
    require_code('images');
    if ((($enforce_type & CMS_UPLOAD_VIDEO) != 0) || (($enforce_type & CMS_UPLOAD_AUDIO) != 0)) {
        require_code('files2');
        $max_size = get_max_file_size(null, null, false);
    } else {
        $max_size = get_max_image_size(false);
    }
    $is_uploaded = ($attach_name != '') && (array_key_exists($attach_name, $filearrays)) && ((is_uploaded_file($filearrays[$attach_name]['tmp_name'])) || ($plupload_uploaded));
    if ($is_uploaded) { // If we uploaded
        if (!has_privilege($member_id, 'exceed_filesize_limit')) {
            if ($filearrays[$attach_name]['size'] > $max_size) {
                if ($accept_errors) {
                    attach_message(do_lang_tempcode('FILE_TOO_BIG', escape_html(integer_format($max_size))), 'warn');
                    return array('', '', '', '');
                } else {
                    warn_exit(do_lang_tempcode('FILE_TOO_BIG', escape_html(integer_format($max_size))));
                }
            }
        }

        $url = _get_upload_url($member_id, $attach_name, $upload_folder, $upload_folder_full, $enforce_type, $obfuscate, $accept_errors, $filename);
        if ($url == array('', '')) {
            return array('', '', '', '');
        }

        $is_image = is_image($filearrays[$attach_name]['name'], true);
    } elseif (post_param_string($specify_name, '') != '') { // If we specified
        $url = _get_specify_url($member_id, $specify_name, $upload_folder, $enforce_type, $accept_errors);
        $is_image = is_image($url[0]);
        if ($url[0] != '') {
            if ($enforce_type == CMS_UPLOAD_IMAGE) {
                $is_image = true; // Must be an image if it got to here. Maybe came from oEmbed and not having an image extension.
            }
        }
        if ($url == array('', '')) {
            return array('', '', '', '');
        }
        if (($copy_to_server) && (!url_is_local($url[0]))) {
            $path2 = cms_tempnam();
            $tmpfile = fopen($path2, 'wb');

            $file = http_download_file($url[0], $max_size, true, false, 'Composr', null, null, null, null, null, $tmpfile);
            fclose($tmpfile);
            if (is_null($file)) {
                @unlink($path2);
                if ($accept_errors) {
                    attach_message(do_lang_tempcode('CANNOT_COPY_TO_SERVER'), 'warn');
                    return array('', '', '', '');
                } else {
                    warn_exit(do_lang_tempcode('CANNOT_COPY_TO_SERVER'));
                }
            }
            global $HTTP_FILENAME;
            if (is_null($HTTP_FILENAME)) {
                $HTTP_FILENAME = $url[1];
            }

            if (!check_extension($HTTP_FILENAME, $obfuscate == 2, $path2, $accept_errors)) {
                if ($obfuscate == 3) { // We'll try again, with obfuscation to see if this would get through
                    $obfuscate = 2;
                    if (!check_extension($HTTP_FILENAME, $obfuscate == 2, $path2, $accept_errors)) {
                        return array('', '', '', '');
                    }
                } else {
                    return array('', '', '', '');
                }
            }

            if (url_is_local($url[0])) {
                unlink($path2);
                if ($accept_errors) {
                    attach_message(do_lang_tempcode('CANNOT_COPY_TO_SERVER'), 'warn');
                    return array('', '', '', '');
                } else {
                    warn_exit(do_lang_tempcode('CANNOT_COPY_TO_SERVER'));
                }
            }
            $ext = (($obfuscate == 2) && (!is_image($HTTP_FILENAME))) ? 'bin' : get_file_extension($HTTP_FILENAME);
            if (is_null($filename)) {
                if (($obfuscate != 0) && ($obfuscate != 3)) {
                    $filename = $HTTP_FILENAME . ((substr($HTTP_FILENAME, -strlen($ext) - 1) == '.' . $ext) ? '' : ('.' . $ext));
                    $place = $upload_folder_full . '/' . $filename;
                    while (file_exists($place)) {
                        $filename = uniqid('', true) . '.' . $ext;
                        $place = $upload_folder_full . '/' . $filename;
                    }
                } else {
                    $filename = shorten_urlencoded_filename($HTTP_FILENAME);
                    $place = $upload_folder_full . '/' . $filename;
                }
            } else {
                if (substr($filename, -4) == '.XXX') {
                    $filename = substr($filename, 0, strlen($filename) - 4) . '.' . $ext;
                }

                $place = $upload_folder_full . '/' . shorten_urlencoded_filename($filename);
            }
            if (!has_privilege($member_id, 'exceed_filesize_limit')) {
                $max_size = intval(get_option('max_download_size')) * 1024;
                if (strlen($file) > $max_size) {
                    if ($accept_errors) {
                        attach_message(do_lang_tempcode('FILE_TOO_BIG', escape_html(integer_format($max_size))), 'warn');
                        return array('', '', '', '');
                    } else {
                        warn_exit(do_lang_tempcode('FILE_TOO_BIG', escape_html(integer_format($max_size))));
                    }
                }
            }
            $result = @rename($path2, $place);
            global $HTTP_DOWNLOAD_MTIME;
            if ((!is_null($HTTP_DOWNLOAD_MTIME)) && ($HTTP_DOWNLOAD_MTIME != 0)) {
                @touch($place, $HTTP_DOWNLOAD_MTIME);
            }
            if (!$result) {
                unlink($path2);
                if ($accept_errors) {
                    attach_message(do_lang_tempcode('WRITE_ERROR', escape_html($upload_folder)), 'warn');
                    return array('', '', '', '');
                } else {
                    warn_exit(do_lang_tempcode('WRITE_ERROR', escape_html($upload_folder)));
                }
            }
            fix_permissions($place);
            sync_file($place);

            $url[0] = $upload_folder . '/' . $filename;
            if (strpos($HTTP_FILENAME, '/') === false) {
                $url[1] = $HTTP_FILENAME;
            }
        }
    } else { // Uh oh
        if ((array_key_exists($attach_name, $filearrays)) && (array_key_exists('error', $filearrays[$attach_name])) && (($filearrays[$attach_name]['error'] != 4) || ($should_get_something)) && ($filearrays[$attach_name]['error'] != 0)) { // If we uploaded
            if ($filearrays[$attach_name]['error'] == 1) {
                if ($accept_errors) {
                    attach_message(do_lang_tempcode('FILE_TOO_BIG', escape_html(integer_format($max_size))), 'warn');
                    return array('', '', '', '');
                } else {
                    warn_exit(do_lang_tempcode('FILE_TOO_BIG', escape_html(integer_format($max_size))));
                }
            } elseif ($filearrays[$attach_name]['error'] == 2) {
                if ($accept_errors) {
                    attach_message(do_lang_tempcode('FILE_TOO_BIG_QUOTA', escape_html(integer_format($max_size))), 'warn');
                    return array('', '', '', '');
                } else {
                    warn_exit(do_lang_tempcode('FILE_TOO_BIG_QUOTA', escape_html(integer_format($max_size))));
                }
            } elseif (($filearrays[$attach_name]['error'] == 3) || ($filearrays[$attach_name]['error'] == 4) || ($filearrays[$attach_name]['error'] == 6) || ($filearrays[$attach_name]['error'] == 7)) {
                attach_message(do_lang_tempcode('ERROR_UPLOADING_' . strval($filearrays[$attach_name]['error'])), 'warn');
                return array('', '', '', '');
            } else {
                warn_exit(do_lang_tempcode('ERROR_UPLOADING_' . strval($filearrays[$attach_name]['error'])));
            }
        }

        $url[0] = '';
        $url[1] = '';
        $is_image = false;
    }

    $out[0] = $url[0];
    $out[1] = '';
    $out[2] = $url[1];
    $out[3] = '';

    // Generate thumbnail if needed
    if (($make_thumbnail) && ($url[0] != '') && ($is_image)) {
        if ((array_key_exists($thumb_attach_name, $filearrays)) && ((is_uploaded_file($filearrays[$thumb_attach_name]['tmp_name'])) || ($plupload_uploaded_thumb))) { // If we uploaded
            if ($filearrays[$thumb_attach_name]['size'] > get_max_image_size(false)) {
                if ($accept_errors) {
                    attach_message(do_lang_tempcode('FILE_TOO_BIG', escape_html(integer_format(get_max_image_size(false)))), 'warn');
                    return array('', '', '', '');
                } else {
                    warn_exit(do_lang_tempcode('FILE_TOO_BIG', escape_html(integer_format(get_max_image_size(false)))));
                }
            }

            $_thumb = _get_upload_url($member_id, $thumb_attach_name, $thumb_folder, $thumb_folder_full, CMS_UPLOAD_IMAGE, 0, $accept_errors);
            $thumb = $_thumb[0];
        } elseif (array_key_exists($thumb_specify_name, $_POST)) { // If we specified
            $_thumb = _get_specify_url($member_id, $thumb_specify_name, $thumb_folder, CMS_UPLOAD_IMAGE, $accept_errors);
            $thumb = $_thumb[0];
        } else {
            if (function_exists('imagetypes')) {
                if ((!is_saveable_image($url[0])) && (get_file_extension($url[0]) != 'svg')) {
                    $ext = '.png';
                } else {
                    $ext = '';
                }
                $thumb_filename = basename(preg_replace('#[^' . URL_CONTENT_REGEXP . '\.]#', 'x', basename($url[0])));
                $place = $thumb_folder_full . '/' . $thumb_filename . $ext;
                $i = 2;
                while (file_exists($place)) {
                    $ext = '.' . get_file_extension($url[0]);
                    $thumb_filename = basename(preg_replace('#[^' . URL_CONTENT_REGEXP . '\.]#', 'x', basename($url[0])), $ext) . '_' . strval($i) . $ext;
                    $place = $thumb_folder_full . '/' . $thumb_filename . $ext;
                    $i++;
                }
                if (@file_put_contents($place, '') === false) { // Lock it in ASAP, to stop race conditions
                    intelligent_write_error($place);
                }
                sync_file($place);
                $url_full = url_is_local($url[0]) ? get_custom_base_url() . '/' . $url[0] : $url[0];

                convert_image($url_full, $place, -1, -1, intval(get_option('thumb_width')), true, null, false, $only_make_smaller);

                $thumb = $thumb_folder . '/' . rawurlencode($thumb_filename) . $ext;
            } else {
                if ($accept_errors) {
                    attach_message(do_lang_tempcode('GD_THUMB_ERROR'), 'warn');
                    return array('', '', '', '');
                } else {
                    warn_exit(do_lang_tempcode('GD_THUMB_ERROR'));
                }
            }
        }

        $out[1] = $thumb;
    } elseif ($make_thumbnail) {
        if ((array_key_exists($thumb_attach_name, $filearrays)) && ((is_uploaded_file($filearrays[$thumb_attach_name]['tmp_name'])) || ($plupload_uploaded_thumb))) { // If we uploaded
            if ($filearrays[$thumb_attach_name]['size'] > get_max_image_size(false)) {
                if ($accept_errors) {
                    attach_message(do_lang_tempcode('FILE_TOO_BIG', escape_html(integer_format(get_max_image_size()))), 'warn');
                    return array('', '', '', '');
                } else {
                    warn_exit(do_lang_tempcode('FILE_TOO_BIG', escape_html(integer_format(get_max_image_size()))));
                }
            }

            $_thumb = _get_upload_url($member_id, $thumb_attach_name, $thumb_folder, $thumb_folder_full, CMS_UPLOAD_IMAGE, 0, $accept_errors);
            $thumb = $_thumb[0];
        } elseif (array_key_exists($thumb_specify_name, $_POST)) {
            $_thumb = _get_specify_url($member_id, $thumb_specify_name, $thumb_folder, CMS_UPLOAD_IMAGE, $accept_errors);
            $thumb = $_thumb[0];
        }
        if (!is_null($thumb)) {
            $out[1] = $thumb;
        }
    }

    // Images cleanup pipeline
    if ((($enforce_type & CMS_UPLOAD_IMAGE) != 0) && ($enforce_type != CMS_UPLOAD_ANYTHING)) {
        if (($is_uploaded) && ($out[0] != '') && (url_is_local($out[0]))) {
            global $ICPS__RECOMPRESS_MODE, $ICPS__MAXIMUM_DIMENSION, $ICPS__WATERMARKS, $ICPS__STRIP_GPS;
            handle_images_cleanup_pipeline(get_custom_file_base() . '/' . rawurldecode($out[0]), $out[2], $ICPS__RECOMPRESS_MODE, $ICPS__MAXIMUM_DIMENSION, $ICPS__WATERMARKS, $ICPS__STRIP_GPS);
        }
    }

    // For reentrance of previews
    if ($specify_name != '') {
        $_POST[$specify_name] = $out[0];
    }
    if ($thumb_specify_name != '') {
        $_POST[$thumb_specify_name] = $out[1];
    }

    $out[0] = cms_rawurlrecode($out[0]);
    if (array_key_exists(2, $out)) {
        $out[2] = cms_rawurlrecode($out[2]);
    }

    if (count($filearrays) != 0) {
        cms_profile_end_for('get_url', $attach_name);
    }

    return $out;
}

/**
 * Filters specified URLs to make sure we're really allowed to access them.
 *
 * @param  MEMBER $member_id Member ID to check permissions with.
 * @param  ID_TEXT $specify_name The name of the POST parameter storing the URL (if '', then no POST parameter). Parameter value may be blank.
 * @param  ID_TEXT $upload_folder The folder name where we will put this upload
 * @param  integer $enforce_type The type of upload it is (bitmask, from CMS_UPLOAD_* constants)
 * @param  boolean $accept_errors Whether to accept upload errors
 * @return array A pair: the URL and the filename
 *
 * @ignore
 */
function _get_specify_url($member_id, $specify_name, $upload_folder, $enforce_type = 15, $accept_errors = false)
{
    // Security check against naughty url's
    $url = array();
    $url[0] = /*filter_naughty*/(post_param_string($specify_name));
    $url[1] = rawurldecode(basename($url[0]));

    // We should use compliant encoding
    $coder_ob = new HarmlessURLCoder(); // TODO: This should move into post_param_string in v11, filter based
    $url[0] = $coder_ob->encode($url[0]);

    // If this is a relative URL then it may be downloaded through a PHP script.
    //  So lets check we are allowed to download it!
    if (($url[0] != '') && (url_is_local($url[0]))) {
        $missing_ok = false;

        // Check the file exists
        if ((!file_exists(get_custom_file_base() . '/' . rawurldecode($url[0]))) && (!$missing_ok)) {
            if ($accept_errors) {
                attach_message(do_lang_tempcode('MISSING_FILE'), 'warn');
                return array('', '');
            } else {
                warn_exit(do_lang_tempcode('MISSING_FILE'));
            }
        }

        // Its not in the upload folder, so maybe we aren't allowed to download it
        if (
            (
                (substr($url[0], 0, strlen($upload_folder) + 1) != $upload_folder . '/') &&
                (substr($url[0], 0, strlen('data/images/') + 1) != 'data/images/') &&
                (preg_match('#^[^\?\.]*\.(m4v|mp4|flv|f4v|mpeg|mpg|webm|ogv|png|gif|jpg|jpeg|jpe)$#', $url[0]) == 0)/*Streaming/compression plugins can mess up our script detection so whitelist some formats*/
            ) ||
            (strpos($url[0], '..') !== false)
        ) {
            $myfile = @fopen(get_custom_file_base() . '/' . rawurldecode($url[0]), 'rb');
            if ($myfile !== false) {
                $shouldbe = fread($myfile, 8000);
                fclose($myfile);
            } else {
                $shouldbe = null;
            }
            global $HTTP_MESSAGE;
            $actuallyis = http_download_file(get_custom_base_url() . '/' . $url[0], 8000, false);

            if (($HTTP_MESSAGE == '200') && (is_null($shouldbe))) {
                // No error downloading, but error using file system - therefore file exists and we'll use URL to download. Hence no security check.
                $missing_ok = true;
            } else {
                if (@strcmp(substr($shouldbe, 0, 8000), substr($actuallyis, 0, 8000)) != 0) {
                    log_hack_attack_and_exit('TRY_TO_DOWNLOAD_SCRIPT');
                }
            }
        }
    }

    if ($url[0] != '') {
        // oEmbed etc
        if (($enforce_type != CMS_UPLOAD_ANYTHING) && (($enforce_type & CMS_UPLOAD_IMAGE) != 0) && (!is_image($url[0])) && ((($enforce_type & CMS_UPLOAD_SWF) == 0) || (get_file_extension($url[0]) != 'swf'))) {
            require_code('media_renderer');
            require_code('files2');
            $meta_details = get_webpage_meta_details($url[0]);
            require_code('hooks/systems/media_rendering/oembed');
            $oembed_ob = object_factory('Hook_media_rendering_oembed');
            if ($oembed_ob->recognises_mime_type($meta_details['t_mime_type'], $meta_details) || $oembed_ob->recognises_url($url[0])) {
                $oembed = $oembed_ob->get_oembed_data_result($url[0], array('width' => get_option('video_width_setting'), 'height' => get_option('video_height_setting')));
                if (($oembed !== null) && ($oembed['type'] == 'photo')) {
                    $url[0] = preg_replace('#.*(https?://)#', '${1}', array_key_exists('url', $oembed) ? $oembed['url'] : $oembed['thumbnail_url']); // Get thumbnail, but strip noembed.com (for example) resizer-proxy prefix if there
                    $url[1] = basename(urldecode($url[0]));
                    return $url;
                }
            }
            if (substr($meta_details['t_mime_type'], 0, 6) == 'image/') {
                return $url;
            }
            if ($meta_details['t_image_url'] != '') {
                $url[0] = $meta_details['t_image_url'];
                $url[1] = basename(urldecode($url[0]));
                return $url;
            }
        }

        if (!_check_enforcement_of_type($member_id, $url[0], $enforce_type, $accept_errors)) {
            return array('', '');
        }
    }

    return $url;
}

/**
 * Ensures a given filename is of the right file extension for the desired file type.
 *
 * @param  MEMBER $member_id Member ID to check permissions with.
 * @param  string $file The filename.
 * @param  integer $enforce_type The type of upload it is (bitmask, from CMS_UPLOAD_* constants)
 * @param  boolean $accept_errors Whether to accept upload errors
 * @return boolean Success status
 *
 * @ignore
 */
function _check_enforcement_of_type($member_id, $file, $enforce_type, $accept_errors = false)
{
    if (($enforce_type & CMS_UPLOAD_ANYTHING) != 0) {
        return true;
    }

    require_code('images');
    $ok = false;
    if (($enforce_type & CMS_UPLOAD_SWF) != 0) {
        if (get_file_extension($file) != 'swf') {
            if ($enforce_type == CMS_UPLOAD_SWF) {
                if ($accept_errors) {
                    attach_message(do_lang_tempcode('NOT_IMAGE'), 'warn');
                } else {
                    warn_exit(do_lang_tempcode('NOT_IMAGE'));
                }
                return false;
            }
        } else {
            $ok = true;
        }
    }
    if (($enforce_type & CMS_UPLOAD_IMAGE) != 0) {
        if (!is_image($file, true)) {
            if ($enforce_type == CMS_UPLOAD_IMAGE) {
                if ($accept_errors) {
                    attach_message(do_lang_tempcode('NOT_IMAGE'), 'warn');
                } else {
                    warn_exit(do_lang_tempcode('NOT_IMAGE'));
                }
                return false;
            }
        } else {
            $ok = true;
        }
    }
    if (($enforce_type & CMS_UPLOAD_VIDEO) != 0) {
        if (!is_video($file, has_privilege($member_id, 'comcode_dangerous'), false)) {
            if ($enforce_type == CMS_UPLOAD_VIDEO) {
                if ($accept_errors) {
                    attach_message(do_lang_tempcode('NOT_VIDEO'), 'warn');
                } else {
                    warn_exit(do_lang_tempcode('NOT_VIDEO'));
                }
                return false;
            }
        } else {
            $ok = true;
        }
    }
    if (($enforce_type & CMS_UPLOAD_AUDIO) != 0) {
        if (!is_audio($file, has_privilege($member_id, 'comcode_dangerous'))) {
            if ($enforce_type == CMS_UPLOAD_AUDIO) {
                if ($accept_errors) {
                    attach_message(do_lang_tempcode('NOT_AUDIO'), 'warn');
                } else {
                    warn_exit(do_lang_tempcode('NOT_AUDIO'));
                }
                return false;
            }
        } else {
            $ok = true;
        }
    }
    if (!$ok) {
        if ($accept_errors) {
            attach_message(do_lang_tempcode('_NOT_FILE_TYPE'), 'warn');
        } else {
            warn_exit(do_lang_tempcode('_NOT_FILE_TYPE'));
        }
        return false;
    }
    return true;
}

/**
 * Converts an uploaded file into a URL, by moving it to an appropriate place.
 *
 * @param  MEMBER $member_id Member ID to check permissions with.
 * @param  ID_TEXT $attach_name The name of the HTTP file parameter storing the upload (if '', then no HTTP file parameter). No file necessarily is uploaded under this.
 * @param  ID_TEXT $upload_folder The folder name where we will put this upload
 * @param  PATH $upload_folder_full Full folder path
 * @param  integer $enforce_type The type of upload it is (bitmask, from CMS_UPLOAD_* constants)
 * @param  integer $obfuscate Whether to obfuscate file names so the URLs can not be guessed/derived (0=do not, 1=do, 2=make extension .bin as well)
 * @param  boolean $accept_errors Whether to accept upload errors
 * @param  ?string $filename Filename to use (null: choose one)
 * @return array A pair: the URL and the filename
 *
 * @ignore
 */
function _get_upload_url($member_id, $attach_name, $upload_folder, $upload_folder_full, $enforce_type = 15, $obfuscate = 0, $accept_errors = false, $filename = null)
{
    $filearrays = array();
    get_upload_filearray($attach_name, $filearrays);

    $file = shorten_urlencoded_filename($filearrays[$attach_name]['name']);
    if (@get_magic_quotes_gpc()) {
        $file = stripslashes($file);
    }

    if (!check_extension($file, $obfuscate == 2, null, $accept_errors)) {
        if ($obfuscate == 3) { // We'll try again, with obfuscation to see if this would get through
            $obfuscate = 2;
            if (!check_extension($file, $obfuscate == 2, null, $accept_errors)) {
                return array('', '', '', '');
            }
        } else {
            return array('', '', '', '');
        }
    }

    if (!_check_enforcement_of_type($member_id, $file, $enforce_type, $accept_errors)) {
        return array('', '');
    }

    $ext = (($obfuscate == 2) && (!is_image($file))) ? 'bin' : get_file_extension($file);
    if (is_null($filename)) {
        // If we are not obfuscating then we will need to search for an available filename
        if (($obfuscate == 0) || ($obfuscate == 3) || (strlen($file) > 150)) {
            $filename = $file;
            $place = $upload_folder_full . '/' . $filename;
            // Hunt with sensible names until we don't get a conflict
            $i = 2;
            while (file_exists($place)) {
                $ext = '.' . get_file_extension($file);
                $filename = basename($file, $ext) . '_' . strval($i) . $ext;
                $place = $upload_folder_full . '/' . $filename;
                $i++;
            }
            if (@file_put_contents($place, '') === false) { // Lock it in ASAP, to stop race conditions
                intelligent_write_error($place);
            }
            sync_file($place);
        } else { // A result of some randomness
            $filename = uniqid('', true) . '.' . $ext;
            $place = $upload_folder_full . '/' . $filename;
            while (file_exists($place)) {
                $filename = uniqid('', true) . '.' . $ext;
                $place = $upload_folder_full . '/' . $filename;
            }
        }
    } else {
        if (substr($filename, -4) == '.XXX') {
            $filename = substr($filename, 0, strlen($filename) - 4) . '.' . $ext;
        }

        $place = $upload_folder_full . '/' . $filename;
    }

    // Is a CDN transfer hook going to kick in?
    $hooks = find_all_hooks('systems', 'cdn_transfer');
    foreach (array_keys($hooks) as $hook) {
        require_code('hooks/systems/cdn_transfer/' . filter_naughty_harsh($hook));
        $object = object_factory('Hook_cdn_transfer_' . filter_naughty_harsh($hook), true);
        if ((!is_null($object)) && ($object->is_enabled())) {
            $test = $object->transfer_upload($attach_name, $upload_folder, basename($place), $obfuscate, $accept_errors);
            if ($test !== null) {
                $url = array();
                $url[0] = $test;
                $url[1] = $file;
                return $url;
            }
        }
    }

    check_shared_space_usage($filearrays[$attach_name]['size']);

    // Copy there, and return our URL
    if ($filearrays[$attach_name]['type'] != 'plupload') {
        $test = @move_uploaded_file($filearrays[$attach_name]['tmp_name'], $place);
    } else {
        $test = @copy($filearrays[$attach_name]['tmp_name'], $place); // We could rename, but it would hurt integrity of refreshes
    }
    if ($test === false) {
        if ($accept_errors) {
            $df = do_lang_tempcode('FILE_MOVE_ERROR', escape_html($file), escape_html($place));
            attach_message($df, 'warn');
            return array('', '');
        } else {
            warn_exit(do_lang_tempcode('FILE_MOVE_ERROR', escape_html($file), escape_html($place)));
        }
    }
    fix_permissions($place);
    sync_file($place);

    $url = array();
    $url[0] = $upload_folder . '/' . rawurlencode($filename);
    $url[1] = $file;
    return $url;
}

/**
 * In Tapatalk files may be passed as arrays, so abstract that complexity.
 *
 * @param  ID_TEXT $name The name of the HTTP file parameter storing the upload.
 * @param  array $filearrays Where we are storing our file arrays.
 */
function get_upload_filearray($name, &$filearrays)
{
    if (!isset($_FILES[$name])) {
        $name = preg_replace('#\d+$#', '', $name);
    }

    if (!isset($_FILES[$name])) {
        return;
    }

    if (is_array($_FILES[$name]['name'])) {
        $matches = array();
        if (preg_match('#^([^\d]*)(\d+)([^\d]*)$#', $name, $matches) == 0) {
            $matches = array(1 => $name, 2 => '1', 3 => '');
        }
        $base_num = intval($matches[2]);

        $num_files = count($_FILES[$name]['name']);

        for ($i = 0; $i < $num_files; $i++) {
            $filearray = array();
            foreach ($_FILES[$name] as $key => $value) { // Goes through keys like 'name', 'tmp_name' etc
                $filearray[$key] = $value[$i];
            }
            $_name = $matches[1] . strval($i + $base_num) . $matches[3];
            $filearrays[$_name] = $filearray;
        }
        return;
    }

    $filearrays[$name] = $_FILES[$name];
}
