<?php /*

 Composr
 Copyright (c) Christopher Graham, 2004-2024

 See docs/LICENSE.md for full licensing 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  Christopher Graham
 * @package    core
 */

/*EXTRA FUNCTIONS: curl_.*|imagefilledrectangle|usleep*/

/**
 * Script to access a gravatar, while protecting the user's privacy.
 *
 * @param  boolean $from_driver Whether this script was called from a forum driver
 * @ignore
 */
function gravatar_script(bool $from_driver = false)
{
    header('X-Robots-Tag: noindex');

    $id = get_param_integer('id');

    $email_address = $GLOBALS['FORUM_DRIVER']->get_member_email_address($id);

    if ($from_driver) {
        $avatar_url = ''; // Prevent recursive get_member_avatar_url calls from forum drivers by initialising to blank
    } else {
        $avatar_url = $GLOBALS['FORUM_DRIVER']->get_member_avatar_url($id, false);
    }

    $is_error = true; // We'll only set to false once everything is good

    if (($avatar_url == '') && ($email_address != '')) {
        require_code('http');
        $gravatar_url = 'https://www.gravatar.com/avatar/' . md5($email_address) . '?d=404';
        $f = cms_http_request($gravatar_url, ['trigger_error' => false]);
    } else {
        $f = false; // We already have an avatar, or can't get a gravatar, so let it get to error state
    }

    if (($f !== false) && ($f->data !== null)) {
        if (substr($f->download_mime_type, 0, 6) != 'image/') {
            fatal_exit(do_lang_tempcode('INTERNAL_ERROR'));
        } else {
            $is_error = false;
        }
    }

    if ($is_error) {
        if (($avatar_url == '') && (get_forum_type() == 'cns')) {
            require_code('cns_members2');
            $avatar_url = cns_choose_default_avatar($email_address, 'dynamic');
        }

        if ((url_is_local($avatar_url)) && ($avatar_url != '')) {
            $avatar_url = get_complex_base_url($avatar_url) . '/' . $avatar_url;
        }

        if ($avatar_url == '') {
            $avatar_url = 'https://www.gravatar.com/avatar/00000000000000000000000000000000?d=mp&f=y';
        }

        header('Location: ' . $avatar_url);
        return;
    }

    // All good, so show gravatar...

    cms_ini_set('ocproducts.xss_detect', '0');
    header('Content-Type: ' . $f->download_mime_type);

    echo $f->data;
}

/**
 * Script to make a nice textual image, vertical writing.
 *
 * @ignore
 */
function gd_text_script()
{
    if (!function_exists('imagefontwidth')) {
        return;
    }

    header('X-Robots-Tag: noindex');

    $text = get_param_string('text', false, INPUT_FILTER_GET_COMPLEX);

    $direction = array_key_exists('direction', $_GET) ? $_GET['direction'] : 'vertical';

    $font_size = array_key_exists('size', $_GET) ? intval($_GET['size']) : 8;

    require_code('fonts');
    $font = get_param_string('font', find_default_font(true));
    $font_path = find_font_path($font);

    if (!has_ttf()) {
        switch ($font_size) {
            case 1:
            case 2:
                $pfont = 1;
                break;

            case 3:
            case 4:
                $pfont = 2;
                break;

            case 5:
            case 6:
                $pfont = 3;
                break;

            case 7:
            case 8:
                $pfont = 4;
                break;

            default:
                $pfont = 5;
        }
        $baseline_offset = 0;

        if ($direction == 'horizontal') {
            $width = intval(imagefontwidth($pfont) * strlen($text) * 1.05);
            $height = imagefontheight($pfont);
        } else {
            $height = intval(imagefontwidth($pfont) * strlen($text) * 1.05);
            $width = imagefontheight($pfont);
        }
    } else {
        $scale = 4;

        if ($direction == 'horizontal') {
            list(, , $width, , , , , $height) = imagettfbbox(floatval($font_size * $scale), 0.0, $font_path, $text);
            $baseline_offset = 2 * $scale * intval(ceil(floatval($font_size) / 8.0));
            $height = abs($height);
            $width += $font_size * $scale * 2; // This is just due to inaccuracy in imagettfbbox, possibly due to italics not being computed correctly
            $height += $baseline_offset;

            list(, , $real_width, , , , , $real_height) = imagettfbbox(floatval($font_size), 0.0, $font_path, $text);
            $real_height = abs($real_height);
            $real_width += $font_size * 2;
            $real_height += $baseline_offset / $scale;
        } else {
            list(, , $height, , , , , $width) = imagettfbbox(floatval($font_size * $scale), 0.0, $font_path, $text);
            $baseline_offset = 2 * $scale * intval(ceil(floatval($font_size) / 8.0));
            $width = abs($width);
            $width += $baseline_offset;
            $height += $font_size * $scale * 2; // This is just due to inaccuracy in imagettfbbox, possibly due to italics not being computed correctly

            list(, , $real_height, , , , , $real_width) = imagettfbbox(floatval($font_size), 0.0, $font_path, $text);
            $real_width = abs($real_width);
            $real_width += $baseline_offset / $scale;
            $real_height += $font_size * 2;
        }
    }
    if ($width == 0) {
        $width = 1;
    }
    if ($height == 0) {
        $height = 1;
    }
    $trans_color = array_key_exists('trans_color', $_GET) ? $_GET['trans_color'] : 'FF00FF';
    $img = imagecreatetruecolor($width, $height + $baseline_offset);
    imagealphablending($img, false);
    $fg_color = array_key_exists('fg_color', $_GET) ? $_GET['fg_color'] : '000000';
    if (substr($fg_color, 0, 5) == 'seed-') {
        $theme = substr($fg_color, 5);

        require_code('themes2');
        $fg_color = find_theme_seed($theme);
    }
    if (strlen($fg_color) == 3) {
        $fg_color = $fg_color[0] . $fg_color[0] . $fg_color[1] . $fg_color[1] . $fg_color[2] . $fg_color[2];
    }
    if (preg_match('#^[\dA-F]{6}$#i', $fg_color) == 0) {
        $fg_color = '000000';
    }
    $color = imagecolorallocate($img, hexdec(substr($fg_color, 0, 2)), hexdec(substr($fg_color, 2, 2)), hexdec(substr($fg_color, 4, 2)));
    if (!has_ttf()) {
        $trans = imagecolorallocate($img, hexdec(substr($trans_color, 0, 2)), hexdec(substr($trans_color, 2, 2)), hexdec(substr($trans_color, 4, 2)));
        imagefill($img, 0, 0, $trans);
        imagecolortransparent($img, $trans);
        if ($direction == 'horizontal') {
            imagestring($img, $pfont, 0, intval($height * 0.02), $text, $color);
        } else {
            imagestringup($img, $pfont, 0, $height - 1 - intval($height * 0.02), $text, $color);
        }
    } else {
        if (function_exists('imagecolorallocatealpha')) {
            $trans = imagecolorallocatealpha($img, hexdec(substr($trans_color, 0, 2)), hexdec(substr($trans_color, 2, 2)), hexdec(substr($trans_color, 4, 2)), 127);
        } else {
            $trans = imagecolorallocate($img, hexdec(substr($trans_color, 0, 2)), hexdec(substr($trans_color, 2, 2)), hexdec(substr($trans_color, 4, 2)));
        }
        imagefilledrectangle($img, 0, 0, $width, $height, $trans);
        require_code('character_sets');
        $text = convert_to_html_encoding(convert_to_internal_encoding($text, cms_strtolower_ascii(get_param_string('charset', get_charset())), get_charset()));
        if (strpos($text, '&#') === false) {
            $previous = null;
            $nxpos = 0;
            for ($i = 0; $i < strlen($text); $i++) {
                if ($previous !== null) { // check for existing previous character
                    list(, , $rx1, , $rx2) = imagettfbbox(floatval($font_size * $scale), 0.0, $font_path, $previous);
                    $nxpos += max($rx1, $rx2) + 3;
                }
                if ($direction == 'horizontal') {
                    imagettftext($img, floatval($font_size * $scale), 0.0, $nxpos, $height - $baseline_offset, $color, $font_path, $text[$i]);
                } else {
                    imagettftext($img, floatval($font_size * $scale), 270.0, $baseline_offset, $nxpos, $color, $font_path, $text[$i]);
                }
                $previous = $text[$i];
            }
        } else {
            if ($direction == 'horizontal') {
                imagettftext($img, floatval($font_size * $scale), 0.0, 0, $height - 4, $color, $font_path, $text);
            } else {
                imagettftext($img, floatval($font_size * $scale), 270.0, 4, 0, $color, $font_path, $text);
            }
        }
        $dest_img = imagecreatetruecolor($real_width + intval(ceil(floatval($baseline_offset) / floatval($scale))), $real_height);
        imagealphablending($dest_img, false);
        imagecopyresampled($dest_img, $img, 0, 0, 0, 0, $real_width + intval(ceil(floatval($baseline_offset) / floatval($scale))), $real_height, $width, $height); // Sizes down, for simple antialiasing-like effect
        imagedestroy($img);
        $img = $dest_img;
        if (function_exists('imagesavealpha')) {
            imagesavealpha($img, true);
        }
    }

    header('Content-Type: image/png');
    imagepng($img);
    imagedestroy($img);
}

/**
 * Script to show previews of content being added/edited.
 *
 * @ignore
 */
function preview_script()
{
    disable_browser_reflective_xss_detection();

    require_code('preview');

    $result = build_preview(true);
    list($output, $validation, $keyword_density, $spelling, $health_check, $has_device_preview_modes) = $result;

    if (get_param_integer('js_only', 0) == 0) {
        $output = do_template('PREVIEW_SCRIPT', [
            '_GUID' => '97bd8909e8b9983a0bbf7ab68fab92f3',
            'OUTPUT' => $output->evaluate(),
            'WEBSTANDARDS' => $validation,
            'KEYWORD_DENSITY' => $keyword_density,
            'SPELLING' => $spelling,
            'HEALTH_CHECK' => $health_check,
            'HIDDEN' => build_keep_post_fields(),
            'HAS_DEVICE_PREVIEW_MODES' => $has_device_preview_modes,
        ]);

        $tpl = do_template('STANDALONE_HTML_WRAP', [
            '_GUID' => '0a96e3b9be154e8b29bee5b1c1c7cc69',
            'TITLE' => do_lang_tempcode('PREVIEW'),
            'FRAME' => true,
            'TARGET' => '_top',
            'NOINDEX' => true,
            'CONTENT' => $output,
        ]);
    } else {
        $tpl = $output;
    }
    $tpl->handle_symbol_preprocessing();
    $tpl->evaluate_echo();
}

/**
 * Script to handle iframe.
 *
 * @ignore
 */
function iframe_script()
{
    $zone = get_param_string('zone');
    $page = get_page_name();
    $ajax = (get_param_integer('ajax', 0) == 1);

    process_url_monikers(true, true, $page);

    // AJAX prep
    if ($ajax) {
        prepare_backend_response(null, BACKEND_RESPONSE_NOINDEX | BACKEND_RESPONSE_CACHE_BREAK);
    }

    // Check permissions
    $zones = $GLOBALS['SITE_DB']->query_select('zones', ['*'], ['zone_name' => $zone], '', 1);
    if (!array_key_exists(0, $zones)) {
        warn_exit(do_lang_tempcode('MISSING_RESOURCE'));
    }
    if ($zones[0]['zone_require_session'] == 1) {
        set_no_clickjacking_csp();
    }
    if (($zones[0]['zone_name'] != '') && (!session_considered_confirmed()) && ($zones[0]['zone_require_session'] == 1)) {
        access_denied('ZONE_ACCESS_SESSION');
    }
    if (!has_actual_page_access(get_member(), $page, $zone)) {
        access_denied('ZONE_ACCESS');
    }

    // Closed site
    $site_closed = get_option('site_closed');
    if (($site_closed != '0') && (!has_privilege(get_member(), 'access_closed_site')) && (!$GLOBALS['IS_ACTUALLY_ADMIN'])) {
        http_response_code(503);
        header('Content-Type: text/plain; charset=' . get_charset());
        @exit(get_option('closed'));
    }

    // SEO
    require_code('site');
    attach_to_screen_header('<meta name="robots" content="noindex" />'); // XHTMLXHTML

    // Load page
    $output = request_page($page, true);

    // Simple AJAX output?
    if ($ajax) {
        cms_ini_set('ocproducts.xss_detect', '0');

        $output->handle_symbol_preprocessing();
        echo $output->evaluate();
        return;
    }

    // Normal output
    $tpl = do_template('STANDALONE_HTML_WRAP', [
        '_GUID' => '04cf4ef7aac4201bb985327ec0e04c87',
        'OPENS_BELOW' => get_param_integer('opens_below', 0) == 1,
        'FRAME' => true,
        'NOINDEX' => true,
        'TARGET' => '_top',
        'CONTENT' => $output,
        'TITLE' => ($GLOBALS['DISPLAYED_TITLE'] === null) ? do_lang_tempcode('NA') : $GLOBALS['DISPLAYED_TITLE'],
    ]);
    $tpl->handle_symbol_preprocessing();
    $tpl->evaluate_echo();

    require_code('site');
    save_static_caching($tpl);
}

/**
 * Outputs the page-link chooser pop-up.
 *
 * @ignore
 */
function page_link_chooser_script()
{
    // Check we are allowed here
    if (!has_zone_access(get_member(), 'adminzone')) {
        access_denied('ZONE_ACCESS');
    }

    require_lang('menus');

    require_javascript('core_menus');
    require_javascript('tree_list');

    require_code('site');
    attach_to_screen_header('<meta name="robots" content="noindex" />'); // XHTMLXHTML

    // Display
    $content = do_template('PAGE_LINK_CHOOSER', [
        '_GUID' => '235d969528d7b81aeb17e042a17f5537',
        'NAME' => 'tree_list',
        'VALUE' => '',
        'GET_TITLE_TOO' => true,
        'AS_FIELD' => null,
        'PAGE_TYPE' => null,
    ]);
    $echo = do_template('STANDALONE_HTML_WRAP', [
        '_GUID' => '58768379196d6ad27d6298134e33fabd',
        'TITLE' => do_lang_tempcode('CHOOSE'),
        'CONTENT' => $content,
        'POPUP' => true,
        'NOINDEX' => true,
    ]);
    $echo->handle_symbol_preprocessing();
    $echo->evaluate_echo();
}

/**
 * Shows an HTML page of all emoticons clickably.
 *
 * @ignore
 */
function emoticons_script()
{
    if (get_forum_type() != 'cns') {
        warn_exit(do_lang_tempcode('NO_CNS'));
    }

    require_css('cns');

    require_lang('cns');
    require_javascript('editing');

    $extra = has_privilege(get_member(), 'use_special_emoticons') ? '' : ' AND e_is_special=0';
    $_rows = $GLOBALS['FORUM_DB']->query('SELECT * FROM ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_emoticons WHERE e_relevance_level<3' . $extra);

    // Work out what grid spacing to use
    $max_emoticon_width = 0;
    require_code('images');
    foreach ($_rows as $myrow) {
        $test = cms_getimagesize_url(find_theme_image($myrow['e_theme_img_code'], true));
        if (($test !== false) && ($test[0] !== null)) {
            list($_width,) = $test;
            $max_emoticon_width = max($max_emoticon_width, $_width);
        }
    }
    if ($max_emoticon_width == 0) {
        $max_emoticon_width = 36;
    }
    $padding = 2;
    $window_width = 300;
    $cols = intval(floor(floatval($window_width) / floatval($max_emoticon_width + $padding)));
    if ($cols == 0) {
        $cols = 1;
    }

    // Render UI
    $rows = [];
    $cells = [];
    foreach ($_rows as $i => $myrow) {
        if (($i % $cols == 0) && ($i != 0)) {
            $rows[] = ['CELLS' => $cells];
            $cells = [];
        }

        $code_esc = $myrow['e_code'];
        $cells[] = [
            '_GUID' => 'ddb838e6fa296df41299c8758db92f8d',
            'COLS' => strval($cols),
            'FIELD_NAME' => filter_naughty_harsh(get_param_string('field_name', 'post')),
            'CODE_ESC' => $code_esc,
            'THEME_IMG_CODE' => $myrow['e_theme_img_code'],
            'CODE' => $myrow['e_code'],
        ];
    }
    if (!empty($cells)) {
        $rows[] = ['CELLS' => $cells];
    }

    $content = do_template('CNS_EMOTICON_TABLE', ['_GUID' => 'fb8c4c51f57cd8334800ef12e60d2a8a', 'ROWS' => $rows]);

    require_code('site');
    attach_to_screen_header('<meta name="robots" content="noindex" />'); // XHTMLXHTML

    $echo = do_template('STANDALONE_HTML_WRAP', ['_GUID' => '8acac778b145bfe7b063317fbcae7fde', 'TITLE' => do_lang_tempcode('EMOTICONS_POPUP'), 'POPUP' => true, 'CONTENT' => $content]);
    $echo->handle_symbol_preprocessing();
    $echo->evaluate_echo();
}

/**
 * Outputs a modal question dialog.
 *
 * @ignore
 */
function question_ui_script()
{
    $title = get_param_string('window_title', false, INPUT_FILTER_GET_COMPLEX);
    $_message = nl2br(escape_html(get_param_string('message', false, INPUT_FILTER_GET_COMPLEX)));
    if (function_exists('ocp_mark_as_escaped')) {
        ocp_mark_as_escaped($_message);
    }
    $button_set = explode(',', get_param_string('button_set', false, INPUT_FILTER_GET_COMPLEX));
    $_image_set = get_param_string('image_set', false, INPUT_FILTER_GET_COMPLEX);
    $image_set = ($_image_set == '') ? [] : explode(',', $_image_set);
    $message = do_template('QUESTION_UI_BUTTONS', ['_GUID' => '0c5a1efcf065e4281670426c8fbb2769', 'TITLE' => $title, 'IMAGES' => $image_set, 'BUTTONS' => $button_set, 'MESSAGE' => $_message]);

    require_code('site');
    attach_to_screen_header('<meta name="robots" content="noindex" />'); // XHTMLXHTML

    $echo = do_template('STANDALONE_HTML_WRAP', ['_GUID' => '8d72daa4c9f922656b190b643a6fe61d', 'TITLE' => escape_html($title), 'POPUP' => true, 'CONTENT' => $message]);
    $echo->handle_symbol_preprocessing();
    $echo->evaluate_echo();
}

/**
 * Allows conversion of a URL to a thumbnail via a simple script.
 *
 * @ignore
 */
function thumb_script()
{
    $url_full = get_param_string('url', false, INPUT_FILTER_URL_GENERAL);

    require_code('images');

    $thumb_width = intval(get_option('thumb_width'));
    $box_size = get_param_integer('box_size', $thumb_width);

    if ($box_size == $thumb_width) {
        $new_name = url_to_filename($url_full);
    } else {
        $new_name = md5($url_full . '?box_size=' . strval($box_size)) . '.jpg';
    }

    $file_thumb = get_custom_file_base() . '/uploads/auto_thumbs/' . $new_name;
    if (!file_exists($file_thumb)) {
        $url_thumb = convert_image($url_full, $file_thumb, null, null, $box_size, false);
    } else {
        $url_thumb = 'uploads/auto_thumbs/' . rawurlencode($new_name);
    }
    if (url_is_local($url_thumb)) {
        $url_thumb = get_custom_base_url() . '/' . $url_thumb;
    }

    require_code('mime_types');
    $mime_type = get_mime_type($url_thumb, false);
    header('Content-Type: ' . $mime_type);

    cms_ini_set('ocproducts.xss_detect', '0');

    if ((strpos($url_thumb, "\n") !== false) || (strpos($url_thumb, "\r") !== false)) {
        log_hack_attack_and_exit('HEADER_SPLIT_HACK');
    }
    header('Location: ' . escape_header($url_thumb)); // assign_refresh not used, as no UI here
}

/**
 * Redirect the browser to where a page_link specifies.
 *
 * @ignore
 */
function page_link_redirect_script()
{
    $page_link = get_param_string('id');
    $tpl = symbol_tempcode('PAGE_LINK', [$page_link]);

    $x = $tpl->evaluate();

    if ((strpos($x, "\n") !== false) || (strpos($x, "\r") !== false)) {
        log_hack_attack_and_exit('HEADER_SPLIT_HACK');
    }

    require_code('site2');
    redirect_exit($x);
}

/**
 * Proxy an external URL.
 *
 * @ignore
 */
function external_url_proxy_script()
{
    header('X-Robots-Tag: noindex');

    $url = get_param_string('url', false, INPUT_FILTER_URL_GENERAL);

    // Don't allow loops
    if (strpos($url, 'external_url_proxy.php') !== false) {
        warn_exit(do_lang_tempcode('INTERNAL_ERROR'));
    }

    // Don't allow non-HTTP(S)
    if (preg_match('#^https?://#', $url) == 0) {
        warn_exit(do_lang_tempcode('INTERNAL_ERROR'));
    }

    // No time-limits wanted
    cms_disable_time_limit();

    // No ocProducts XSS filter
    cms_ini_set('ocproducts.xss_detect', '0');

    // Stream
    $content_type = 'application/octet-stream';
    $f = @fopen($url, 'rb');
    if (isset($http_response_header)) {
        // Work out appropriate content type (with restrictions)
        require_code('mime_types');
        $mime_types = array_flip(get_mime_types(false));
        $matches = [];
        foreach ($http_response_header as $header) {
            if (preg_match('#^Content-Type:\s*(.*)\s*#i', $header, $matches) != 0) {
                $content_type = $matches[1];
            }
        }
        if (!isset($mime_types[$content_type])) {
            $content_type = 'application/octet-stream';
        }
        header('Content-Type: ' . $content_type);

        foreach ($http_response_header as $header) {
            if (preg_match('#^Content-Type:\s*(.*)\s*#i', $header) == 0) {
                header($header);
            }
        }
    } else {
        header('Content-Type: ' . $content_type);
    }
    if ($f !== false) {
        cms_ob_end_clean();
        fpassthru($f);
        @fclose($f);
    }
}
