PHPIndex

This page lists files in the current directory. You can view content, get download/execute commands for Wget, Curl, or PowerShell, or filter the list using wildcards (e.g., `*.sh`).

_install
admin
attachments
cache
cron
css
docs
fonts
img
inc
js
language
theme
vendor
change_status.php
wget 'https://lists2.roe3.org/hesk/change_status.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

define('IN_SCRIPT',1);
define('HESK_PATH','./');

// Get all the required files and functions
require(HESK_PATH . 'hesk_settings.inc.php');
require(HESK_PATH . 'inc/common.inc.php');
require(HESK_PATH . 'inc/customer_accounts.inc.php');

// Are we in maintenance mode?
hesk_check_maintenance();

hesk_load_database_functions();
hesk_session_start('CUSTOMER');

// A security check
hesk_token_check();

// Get the tracking ID
$trackingID = hesk_cleanID() or die("$hesklang[int_error]: $hesklang[no_trackID]");

// Get new status
$status = intval( hesk_GET('s', 0) );

$locked = 0;

// Connect to database
hesk_dbConnect();

// Verify email address match if needed
hesk_verifyEmailMatch($trackingID);

// Can we get customer name for history?
$customer_name = $hesklang['customer'];
if (hesk_isCustomerLoggedIn(false)) {
    $customer_name = strlen($_SESSION['customer']['name']) ? $_SESSION['customer']['name'] : (strlen($_SESSION['customer']['email']) ? $_SESSION['customer']['email'] : $hesklang['customer']);
}

if ($status == 3) // Closed
{
	// Is customer closing tickets enabled?
	if ( ! $hesk_settings['custclose'])
	{
		hesk_error($hesklang['attempt']);
	}

	$action = $hesklang['closed'];
    $revision = sprintf($hesklang['thist3'],hesk_date(),$customer_name);

    if ($hesk_settings['custopen'] != 1)
    {
    	$locked = 1;
    }

	// Mark that customer resolved the ticket
	$closedby_sql = ' , `closedat`=NOW(), `closedby`=0 ';
}
elseif ($status == 2) // Opened
{
	// Is customer reopening tickets enabled?
	if ( ! $hesk_settings['custopen'])
	{
		hesk_error($hesklang['attempt']);
	}

	$action = $hesklang['opened'];
    $revision = sprintf($hesklang['thist4'],hesk_date(),$customer_name);

	// We will ask the customer why is the ticket being reopened
	$_SESSION['force_form_top'] = true;

	// Ticket is not resolved
	$closedby_sql = ' , `closedat`=NULL, `closedby`=NULL ';
}
else
{
	die("$hesklang[int_error]: $hesklang[status_not_valid].");
}

// Setup required session vars
$_SESSION['t_track'] = $trackingID;
$_SESSION['t_email'] = $hesk_settings['e_email'];

// Load statuses
require_once(HESK_PATH . 'inc/statuses.inc.php');

// Is current ticket status even changeable by customers?
$ticket = hesk_dbFetchAssoc( hesk_dbQuery( "SELECT `status`, `staffreplies`, `lastreplier` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `trackid`='".hesk_dbEscape($trackingID)."' LIMIT 1") );
if ( ! hesk_can_customer_change_status($ticket['status']))
{
    hesk_process_messages($hesklang['scno'],'ticket.php');
}

// Lets make status assignment a bit smarter when reopening tickets
if ($status == 2)
{
	// If ticket has no staff replies set the status to "New"
	if ($ticket['staffreplies'] < 1)
	{
		$status = 0;
	}
	// If last reply was by customer set status to "Waiting reply from staff"
	elseif ($ticket['lastreplier'] == 0)
	{
		$status = 1;
	}
	// If nothing matches: last reply was from staff, keep status "Waiting reply from customer"
}

// Modify values in the database
hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` SET `status`='{$status}', `locked`='{$locked}' $closedby_sql , `history`=CONCAT(`history`,'".hesk_dbEscape($revision)."') WHERE `trackid`='".hesk_dbEscape($trackingID)."' AND `locked` != '1'");

// Did we modify anything*
if (hesk_dbAffectedRows() != 1)
{
	hesk_process_messages($hesklang['elocked'],'ticket.php');
}

// Show success message
if ($status != 3)
{
	hesk_process_messages($hesklang['wrepo'],'ticket.php','NOTICE');
}
else
{
	hesk_process_messages($hesklang['your_ticket_been'].' '.$action,'ticket.php','SUCCESS');
}
credentials.txt
wget 'https://lists2.roe3.org/hesk/credentials.txt'
View Content
Username: admin
Password: 1+sx2bu2
download_all.php
wget 'https://lists2.roe3.org/hesk/download_all.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

define('IN_SCRIPT',1);
define('HESK_PATH','./');

// Get all the required files and functions
require(HESK_PATH . 'hesk_settings.inc.php');
require(HESK_PATH . 'inc/common.inc.php');
require(HESK_PATH . 'inc/admin_functions.inc.php');
hesk_load_database_functions();

// Page used by staff only
hesk_session_start();
hesk_dbConnect();
hesk_isLoggedIn();

// This function requires the ZipArchive class
if ( ! class_exists('ZipArchive')) {
    hesk_error($hesklang['download_class']);
}

// Attachmend ID and ticket tracking ID
$att_id = hesk_GET('att_id') or die($hesklang['id_not_valid']);
$tic_id = hesk_cleanID() or die("$hesklang[int_error]: $hesklang[no_trackID]");

$att_ids = explode(',', preg_replace('/[^0-9,]/', '', $att_id));
$att_ids = array_filter($att_ids);

// Too many attachments...
if (count($att_ids) > 10) {
    hesk_error($hesklang['download_tma']);
} elseif (count($att_ids) < 1) {
    hesk_error($hesklang['download_nva']);
}

$files_to_download = array();

// Is this staff member allowed to access this ticket?
hesk_verifyStaffTicketAccess($tic_id);

// Get attachment info
$res = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."attachments` WHERE `att_id` IN (".implode(',', $att_ids).")");
if (hesk_dbNumRows($res) != count($att_ids)) {
    hesk_error($hesklang['id_not_valid'].' (att_id)');
}

$file_names = array();

while ($file = hesk_dbFetchAssoc($res))
{
    // Is ticket ID valid for this attachment?
    if ($file['ticket_id'] != $tic_id) {
        hesk_error($hesklang['trackID_not_found']);
    }


    // Path of the file on the server
    $realpath = $hesk_settings['attach_dir'] . '/' . $file['saved_name'];

    // Perhaps the file has been deleted?
    if ( ! file_exists($realpath)) {
        hesk_error($hesklang['attdel']);
    }

    // Rename if files have the same names
    if (isset($file_names[$file['real_name']])) {
        $file_names[$file['real_name']]++;
        $file['real_name'] = substr_replace($file['real_name'], ' ('.$file_names[$file['real_name']].').', strpos($file['real_name'], '.'), 1);
    } else {
        $file_names[$file['real_name']] = 1;
    }

    $files_to_download[$file['real_name']] = $realpath;
}

if ( ! count($files_to_download)) {
    die($hesklang['download_ntd']);
}

$zip_name = $tic_id . '_' . implode('-', $att_ids) . '.zip';
$export_dir = HESK_PATH . $hesk_settings['cache_dir'] . '/';
$save_to_zip = $export_dir . $zip_name;
$zip_full_path = dirname(__FILE__) . '/' . $hesk_settings['cache_dir'] . '/' . $zip_name;

register_shutdown_function('unlink', $zip_full_path);

$zip = new ZipArchive;
$res = $zip->open($save_to_zip, ZipArchive::CREATE);
if ($res === true) {
    foreach ($files_to_download as $name => $file) {
        $zip->addFile($file, $tic_id . '/' . $name);
    }
    $zip->close();
} else {
    die("{$hesklang['eZIP']} <$save_to_zip>\n");
}

$zip_size = filesize($save_to_zip);

// Send the file as an attachment to prevent malicious code from executing
header("Pragma: "); # To fix a bug in IE when running https
header("Cache-Control: "); # To fix a bug in IE when running https
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Length: ' . $zip_size);
header('Content-Disposition: attachment; filename=' . $zip_name);

// For larger files use chunks, smaller ones can be read all at once
$chunksize = 1048576; // = 1024 * 1024 (1 Mb)
if ($zip_size > $chunksize)
{
    $handle = fopen($save_to_zip, 'rb');
    $buffer = '';
    while ( ! feof($handle))
    {
        set_time_limit(300);
        $buffer = fread($handle, $chunksize);
        echo $buffer;
        flush();
    }
    fclose($handle);
}
else
{
    readfile($save_to_zip);
}

exit();

download_attachment.php
wget 'https://lists2.roe3.org/hesk/download_attachment.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

define('IN_SCRIPT',1);
define('HESK_PATH','./');

// Get all the required files and functions
require(HESK_PATH . 'hesk_settings.inc.php');
require(HESK_PATH . 'inc/common.inc.php');
hesk_load_database_functions();

hesk_session_start('CUSTOMER');

// Are we in maintenance mode?
hesk_check_maintenance();

// Connect to database
hesk_dbConnect();

if ($hesk_settings['customer_accounts'] && $hesk_settings['customer_accounts_required'] == 2) {
    require_once(HESK_PATH . 'inc/customer_accounts.inc.php');
    hesk_isCustomerLoggedIn(true);
}

// Knowledgebase attachments
if ( isset($_GET['kb_att']) )
{
    if ( ! $hesk_settings['kb_enable']) {
        hesk_error($hesklang['kbdis']);
    }

    require_once(HESK_PATH . 'inc/knowledgebase_functions.inc.php');

	// Attachment ID
	$att_id = intval( hesk_GET('kb_att') ) or hesk_error($hesklang['id_not_valid']);

	// Get attachment info
	$res = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_attachments` WHERE `att_id`='{$att_id}' LIMIT 1");
	if (hesk_dbNumRows($res) != 1)
	{
		hesk_error($hesklang['id_not_valid'].' (att_id)');
	}
	$file = hesk_dbFetchAssoc($res);

    // Is this person allowed access to this attachment?
	$res = hesk_dbQuery("SELECT `t1`.`type` as `cat_type`, `t2`.`type` as `art_type`, `t2`.`catid`
						FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_articles` AS `t2`
                        JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_categories` AS `t1`
                        ON `t2`.`catid` = `t1`.`id`
                        WHERE (`t2`.`attachments` LIKE '{$att_id}#%' OR `t2`.`attachments` LIKE '%,{$att_id}#%' )
                        LIMIT 1");

    // If no attachment found, throw an error
	if (hesk_dbNumRows($res) != 1)
	{
		hesk_error($hesklang['id_not_valid'].' (no_art)');
	}
	$row = hesk_dbFetchAssoc($res);

    if ($row['cat_type'] || $row['art_type'] || ! isset($hesk_settings['public_kb_categories'][$row['catid']]))
    {
		// This is a staff-only attachment
		hesk_error($hesklang['attpri']);
    }
}

// Ticket attachments
else
{
	// Attachmend ID and ticket tracking ID
    $att_id = intval( hesk_GET('att_id', 0) ) or die($hesklang['id_not_valid']);
	$tic_id = hesk_cleanID() or die("$hesklang[int_error]: $hesklang[no_trackID]");

	// Get attachment info
	$res = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."attachments` WHERE `att_id`='{$att_id}' LIMIT 1");
	if (hesk_dbNumRows($res) != 1)
	{
		hesk_error($hesklang['id_not_valid'].' (att_id)');
	}
	$file = hesk_dbFetchAssoc($res);

	// Is ticket ID valid for this attachment?
	if ($file['ticket_id'] != $tic_id)
	{
	    hesk_error($hesklang['trackID_not_found']);
	}

	// Verify email address match if needed
	hesk_verifyEmailMatch($tic_id);

	// Only staff may download attachments to notes
	if ($file['type'])
	{
		hesk_error($hesklang['perm_deny']);
	}
}

// Path of the file on the server
$realpath = $hesk_settings['attach_dir'] . '/' . $file['saved_name'];

// Perhaps the file has been deleted?
if ( ! file_exists($realpath))
{
	hesk_error($hesklang['attdel']);
}

// Send the file as an attachment to prevent malicious code from executing
header("Pragma: "); # To fix a bug in IE when running https
header("Cache-Control: "); # To fix a bug in IE when running https
hesk_sendFileDownloadHeaders($file['real_name'], $file['size']);

// For larger files use chunks, smaller ones can be read all at once
$chunksize = 1048576; // = 1024 * 1024 (1 Mb)
if ($file['size'] > $chunksize)
{
	$handle = fopen($realpath, 'rb');
	$buffer = '';
	while ( ! feof($handle))
    {
        set_time_limit(300);
		$buffer = fread($handle, $chunksize);
		echo $buffer;
		flush();
	}
	fclose($handle);
}
else
{
	readfile($realpath);
}

exit();
elevator.php
wget 'https://lists2.roe3.org/hesk/elevator.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

use RobThree\Auth\TwoFactorAuth;

define('IN_SCRIPT',1);
define('HESK_PATH','./');

/* Get all the required files and functions */
require(HESK_PATH . 'hesk_settings.inc.php');
define('TEMPLATE_PATH', HESK_PATH . "theme/{$hesk_settings['site_theme']}/");
require(HESK_PATH . 'inc/common.inc.php');
require(HESK_PATH . 'inc/mfa_functions.inc.php');
require(HESK_PATH . 'inc/customer_accounts.inc.php');
hesk_load_database_functions();

hesk_session_start('CUSTOMER');
hesk_dbConnect();

$customerUserContext = hesk_isCustomerLoggedIn();

$mfa_enrollment = intval($_SESSION['customer']['mfa_enrollment']);
$skip_email = false;
$show_backup_code = false;

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    hesk_token_check('POST');

    if (hesk_POST('a') === 'backup_email') {
        // Force email verification instead of authenticator code
        $mfa_enrollment = 1;
        $force_send_email = true;

        // Let's limit the "Send another email" to max 3
        if (isset($_SESSION['customer']['mfa_emails_sent'])) {
            if ($_SESSION['customer']['mfa_emails_sent'] >= 3) {
                hesk_forceLogoutCustomer($hesklang['bf_int']);
            }
            $_SESSION['customer']['mfa_emails_sent']++;
        } else {
            $_SESSION['customer']['mfa_emails_sent'] = 1;
        }
    } elseif (hesk_POST('a') === 'verify') {
        $skip_email = true;
        $mfa_method = hesk_POST('mfa-method');
        if ($mfa_method === 'PASSWORD') {
            $pass = hesk_input( hesk_POST('verification-code') );
            if ( ! $pass) {
                $error = $hesklang['enter_pass'];
            } else {
                hesk_limitInternalBfAttempts();
                if (hesk_password_verify($pass, fetch_current_user_password())) {
                    hesk_cleanBfAttempts();
                    handle_successful_elevation();
                } else {
                    $error = $hesklang['wrong_pass'];
                }
            }
            hesk_process_messages($error, 'NOREDIRECT');
        } else {
            hesk_limitInternalBfAttempts();
            if (($mfa_method === 'EMAIL' && is_mfa_email_code_valid($_SESSION['customer']['id'], hesk_POST('verification-code'), 'CUSTOMER')) ||
                ($mfa_method === 'AUTH-APP' && is_mfa_app_code_valid($_SESSION['customer']['id'], hesk_POST('verification-code'), null, 'CUSTOMER'))) {
                hesk_cleanBfAttempts();
                handle_successful_elevation();
            } else {
                // Verification failed
                hesk_process_messages($hesklang['mfa_invalid_verification_code'], 'NOREDIRECT');
            }
        }
    } elseif (hesk_POST('a') === 'do_backup_code_verification') {
        $skip_email = true;
        hesk_limitInternalBfAttempts();
        if (verify_mfa_backup_code($_SESSION['customer']['id'], hesk_POST('backup-code'), 'CUSTOMER')) {
            hesk_cleanBfAttempts();
            handle_successful_elevation();
        } else {
            // Verification failed
            $show_backup_code = true;
            hesk_process_messages($hesklang['mfa_invalid_backup_code'], 'NOREDIRECT');
        }
    } else {
        // Invalid action, something strange is going on... Let's force logout
        hesk_forceLogoutCustomer($hesklang['invalid_action']);
    }
}

$message = '';

if ($mfa_enrollment === 0) {
    $mfa_verify_option = 'PASSWORD';
    $message .= $hesklang['elevator_enter_password'];
} elseif ($mfa_enrollment === 1) {
    // Email
    $mfa_verify_option = 'EMAIL';

    // Unless the "Send another email" link was clicked, don't send a new email until the old one is valid
    if (! $skip_email && empty($force_send_email) && isset($_SESSION['customer']['skip_mfa_emails_until']) && $_SESSION['customer']['skip_mfa_emails_until'] > date('Y-m-d H:i:s')) {
        $skip_email = true;
    }

    // Don't send a new email each time a verification fails
    if (! $skip_email) {
        $verification_code = generate_mfa_code();
        hash_and_store_mfa_verification_code($_SESSION['customer']['id'], $verification_code, 'CUSTOMER');
        send_mfa_email($_SESSION['customer']['name'], $_SESSION['customer']['email'], $verification_code);

        hesk_process_messages($hesklang['mfa_sent'], 'NOREDIRECT', 'INFO');

        // Don't send a new email until the old one is valid (with 15 min buffer) unless explicitly asked to
        $skip_mfa_emails_until = new DateTime();
        $skip_mfa_emails_until->add(new DateInterval('PT15M'));
        $_SESSION['customer']['skip_mfa_emails_until'] = $skip_mfa_emails_until->format('Y-m-d H:i:s');
    }

    $message .= $hesklang['mfa_verification_needed_email'];
} elseif ($mfa_enrollment === 2) {
    // Authenticator App
    $message .= $hesklang['mfa_verification_needed_auth_app'];
    $mfa_verify_option = 'AUTH-APP';
}

$messages = hesk_get_messages();


$hesk_settings['render_template'](TEMPLATE_PATH . 'customer/account/elevator.php', array(
    'message' => $message,
    'messages' => $messages,
    'customerUserContext' => $customerUserContext,
    'verificationMethod' => $mfa_verify_option,
    'showBackupCode' => $show_backup_code
));

function fetch_current_user_password() {
    global $hesk_settings, $hesklang;

    $res = hesk_dbQuery("SELECT `pass` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` WHERE `id` = ".intval($_SESSION['customer']['id'])." LIMIT 1");
    if (hesk_dbNumRows($res) != 1) {
        hesk_forceLogoutCustomer($hesklang['wrong_user']);
    }

    $row = hesk_dbFetchAssoc($res);

    return $row['pass'];
}

function handle_successful_elevation() {
    global $hesk_settings;

    hesk_session_regenerate_id();
    hesk_cleanBfAttempts();
    delete_mfa_codes($_SESSION['customer']['id'], 'CUSTOMER');
    hesk_cleanSessionVars('mfa_emails_sent');
    hesk_cleanSessionVars('skip_mfa_emails_until');

    $current_time = new DateTime();
    $interval_amount = $hesk_settings['elevator_duration'];
    if (in_array(substr($interval_amount, -1), array('M', 'H'))) {
        $interval_amount = 'T'.$interval_amount;
    }
    $elevation_expiration = $current_time->add(new DateInterval("P{$interval_amount}"));

    $_SESSION['customer']['elevated'] = $elevation_expiration;
    $elevator_target = isset($_SESSION['customer']['elevator_target']) ? $_SESSION['customer']['elevator_target'] : 'index.php';
    unset($_SESSION['customer']['elevator_target']);
    header('Location: ' . $elevator_target);
    exit();
}

exit();
extend_session.php
wget 'https://lists2.roe3.org/hesk/extend_session.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

define('IN_SCRIPT',1);
define('HESK_PATH','./');

/* Get all the required files and functions */
require(HESK_PATH . 'hesk_settings.inc.php');
require(HESK_PATH . 'inc/common.inc.php');
hesk_load_database_functions();

hesk_session_start();
?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="refresh" content="300">
    </head>

    <body style="background-color:transparent"></body>
</html>
file_limits.php
wget 'https://lists2.roe3.org/hesk/file_limits.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

define('IN_SCRIPT',1);
define('HESK_PATH','./');

// Get all the required files and functions
require(HESK_PATH . 'hesk_settings.inc.php');
require(HESK_PATH . 'inc/common.inc.php');
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML; 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<title><?php echo $hesklang['ful']; ?></title>
<meta http-equiv="Content-Type" content="text/html;charset=<?php echo $hesklang['ENCODING']; ?>" />
<style type="text/css">
body
{
	margin:5px 5px;
	padding:0;
	background:#fff;
	color: black;
	font : 68.8%/1.5 Verdana, Geneva, Arial, Helvetica, sans-serif;
	text-align:left;
}

p
{
	color : black;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-size: 1.0em;
}
h3
{
	color : #AF0000;
	font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
	font-weight: bold;
	font-size: 1.0em;
	text-align:left;
}
</style>
</head>
<body>

<h3><?php echo $hesklang['ful']; ?></h3>

<table border="0" cellspacing="1" cellpadding="3">
<tr>
<td valign="top">&raquo;</td>
<td valign="top"><?php echo $hesklang['nat']; ?> <b><?php echo $hesk_settings['attachments']['max_number']; ?></b></td>
</tr>
<tr>
<td valign="top">&raquo;</td>
<td valign="top"><?php echo $hesklang['mfs']; ?> <b><?php echo hesk_formatBytes($hesk_settings['attachments']['max_size']); ?></b></td>
</tr>
<tr>
<td valign="top">&raquo;</td>
<td valign="top"><?php echo $hesklang['ufl']; ?>
<p><?php echo implode(', ', $hesk_settings['attachments']['allowed_types']); ?></p>
</td>
</tr>
</table>

<p align="center"><a href="#" onclick="Javascript:window.close()"><?php echo $hesklang['cwin']; ?></a></p>

<p>&nbsp;</p>

</body>

</html>
footer.txt
wget 'https://lists2.roe3.org/hesk/footer.txt'
View Content
<!-- Custom code to be included before the </body> tag -->

head.txt
wget 'https://lists2.roe3.org/hesk/head.txt'
View Content
<!-- Custom code to be included before the </head> tag -->

header.txt
wget 'https://lists2.roe3.org/hesk/header.txt'
View Content
<!-- Custom code to be included after the <body> tag -->

hesk_settings.inc.php
wget 'https://lists2.roe3.org/hesk/hesk_settings.inc.php'
View Content
<?php
// Settings file for HESK 3.7.10

// ==> GENERAL

// --> General settings
$hesk_settings['site_title']='Website';
$hesk_settings['site_url']='https://lists2.roe3.org';
$hesk_settings['hesk_title']='Help Desk';
$hesk_settings['hesk_url']='https://lists2.roe3.org/hesk';
$hesk_settings['webmaster_mail']='support@example.com';
$hesk_settings['site_theme']='hesk3';
$hesk_settings['admin_css']=0;
$hesk_settings['admin_css_url']='https://www.example.com/hesk-style.css';
$hesk_settings['admin_js']=0;
$hesk_settings['admin_js_url']='https://www.example.com/hesk-script.js';

// --> Language settings
$hesk_settings['can_sel_lang']=0;
$hesk_settings['language']='English';
$hesk_settings['languages']=array(
'English' => array('folder'=>'en','hr'=>'------ Reply above this line ------'),
);

// --> Database settings
$hesk_settings['db_host']='localhost';
$hesk_settings['db_name']='hesk';
$hesk_settings['db_user']='heskuser';
$hesk_settings['db_pass']='heskp@55';
$hesk_settings['db_pfix']='hesk_';


// ==> HELP DESK

// --> Help desk settings
$hesk_settings['admin_dir']='admin';
$hesk_settings['attach_dir']='attachments';
$hesk_settings['cache_dir']='cache';
$hesk_settings['max_listings']=20;
$hesk_settings['print_font_size']=12;
$hesk_settings['autoclose']=0;
$hesk_settings['max_open']=0;
$hesk_settings['due_soon']=7;
$hesk_settings['new_top']=0;
$hesk_settings['reply_top']=0;
$hesk_settings['hide_replies']=0;
$hesk_settings['limit_width']=800;

// --> Features
$hesk_settings['autologin']=1;
$hesk_settings['autoassign']=1;
$hesk_settings['require_email']=1;
$hesk_settings['require_owner']=0;
$hesk_settings['require_subject']=1;
$hesk_settings['require_message']=1;
$hesk_settings['custclose']=1;
$hesk_settings['custopen']=1;
$hesk_settings['rating']=1;
$hesk_settings['cust_urgency']=1;
$hesk_settings['sequential']=1;
$hesk_settings['time_worked']=1;
$hesk_settings['spam_notice']=1;
$hesk_settings['list_users']=0;
$hesk_settings['debug_mode']=0;
$hesk_settings['short_link']=0;
$hesk_settings['submitting_wait']=1;
$hesk_settings['remember_custom_field_values']=1;
$hesk_settings['disable_autofill_admin']=0;
$hesk_settings['disable_autofill_customer']=0;
$hesk_settings['extend_admin']=0;
$hesk_settings['extend_customer']=0;
$hesk_settings['select_cat']=0;
$hesk_settings['select_pri']=0;
$hesk_settings['cat_show_select']=15;
$hesk_settings['staff_ticket_formatting']=0;
$hesk_settings['staff_nicknames']=0;

// --> Barcode
$hesk_settings['barcode']=array(
'print' => 0,
'staff_only' => 0,
'type' => 'C128',
'format' => 'svg',
'width' => 300,
'height' => 80,
'color' => 'black',
'bg' => 'white',
);

// --> Customer Accounts
$hesk_settings['customer_accounts']=0;
$hesk_settings['customer_accounts_required']=0;
$hesk_settings['customer_accounts_customer_self_register']=1;
$hesk_settings['customer_accounts_admin_approvals']=0;
$hesk_settings['customer_autologin']=1;
$hesk_settings['customer_accounts_allow_email_changes']=1;
$hesk_settings['customer_accounts_verify_email_cooldown']=15;

// --> SPAM Prevention
$hesk_settings['secimg_use']=1;
$hesk_settings['secimg_sum']='TMBQL7BN73';
$hesk_settings['recaptcha_use']=0;
$hesk_settings['recaptcha_public_key']='';
$hesk_settings['recaptcha_private_key']='';
$hesk_settings['question_use']=0;
$hesk_settings['question_ask']='Solve this equation to show you are human: 3 + 7 = ';
$hesk_settings['question_ans']='10';

// --> Security
$hesk_settings['attempt_limit']=6;
$hesk_settings['attempt_banmin']=60;
$hesk_settings['flood']=3;
$hesk_settings['reset_pass']=1;
$hesk_settings['email_view_ticket']=1;
$hesk_settings['x_frame_opt']=1;
$hesk_settings['samesite']='Lax';
$hesk_settings['force_ssl']=1;
$hesk_settings['url_key']='Bp0xpKcSTrneUa9V5Je8_S3N_Vf_';
$hesk_settings['require_mfa']=0;
$hesk_settings['require_mfa_customers']=0;
$hesk_settings['elevator_duration']='60M';

// --> Attachments
$hesk_settings['attachments']=array(
'use' => 1,
'max_number' => 2,
'max_size' => 2097152,
'allowed_types' => array('.gif','.jpg','.png','.zip','.rar','.csv','.doc','.docx','.xls','.xlsx','.txt','.pdf'),
'attachment_in_email_type' => 0,
'direct_attachment_in_email' => 0,
'direct_attachment_in_email_no_of_files' => 2,
'first_x_attachments' => 2,
'file_max_size' => 512000,
);


// ==> KNOWLEDGEBASE

// --> Knowledgebase settings
$hesk_settings['kb_enable']=1;
$hesk_settings['kb_wysiwyg']=1;
$hesk_settings['kb_search']=2;
$hesk_settings['kb_search_limit']=10;
$hesk_settings['kb_views']=0;
$hesk_settings['kb_date']=0;
$hesk_settings['kb_recommendanswers']=1;
$hesk_settings['kb_rating']=1;
$hesk_settings['kb_substrart']=200;
$hesk_settings['kb_cols']=2;
$hesk_settings['kb_numshow']=3;
$hesk_settings['kb_popart']=6;
$hesk_settings['kb_latest']=6;
$hesk_settings['kb_index_popart']=6;
$hesk_settings['kb_index_latest']=0;
$hesk_settings['kb_related']=5;


// ==> EMAIL

// --> Email sending
$hesk_settings['noreply_mail']='support@lists2.roe3.org';
$hesk_settings['noreply_name']='Help Desk';
$hesk_settings['email_max_recipients']=50;
$hesk_settings['email_formatting']=3;
$hesk_settings['smtp']=0;
$hesk_settings['smtp_host_name']='mail.example.com';
$hesk_settings['smtp_host_port']=587;
$hesk_settings['smtp_timeout']=20;
$hesk_settings['smtp_enc']='tls';
$hesk_settings['smtp_noval_cert']=0;
$hesk_settings['smtp_user']='';
$hesk_settings['smtp_password']='';
$hesk_settings['smtp_conn_type']='basic';
$hesk_settings['smtp_oauth_provider']=0;

// --> Email piping
$hesk_settings['email_piping']=0;

// --> IMAP Fetching
$hesk_settings['imap']=0;
$hesk_settings['imap_job_wait']=15;
$hesk_settings['imap_host_name']='mail.example.com';
$hesk_settings['imap_host_port']=993;
$hesk_settings['imap_enc']='ssl';
$hesk_settings['imap_noval_cert']=0;
$hesk_settings['imap_disable_GSSAPI']=0;
$hesk_settings['imap_keep']=0;
$hesk_settings['imap_user']='';
$hesk_settings['imap_password']='';
$hesk_settings['imap_conn_type']='basic';
$hesk_settings['imap_oauth_provider']=0;
$hesk_settings['imap_mailbox']='INBOX';

// --> POP3 Fetching
$hesk_settings['pop3']=0;
$hesk_settings['pop3_job_wait']=15;
$hesk_settings['pop3_host_name']='mail.example.com';
$hesk_settings['pop3_host_port']=110;
$hesk_settings['pop3_tls']=0;
$hesk_settings['pop3_keep']=0;
$hesk_settings['pop3_user']='';
$hesk_settings['pop3_password']='';
$hesk_settings['pop3_conn_type']='basic';
$hesk_settings['pop3_oauth_provider']=0;

$hesk_settings['strip_quoted']=1;
$hesk_settings['eml_req_msg']=0;
$hesk_settings['save_embedded']=1;
$hesk_settings['email_include_to']=1;
$hesk_settings['email_include_cc']=1;

// --> Ignore emails
$hesk_settings['pipe_block_noreply']=1;
$hesk_settings['pipe_block_returned']=1;
$hesk_settings['pipe_block_duplicate']=1;
$hesk_settings['loop_hits']=5;
$hesk_settings['loop_time']=300;
$hesk_settings['pipe_customer_rejection_notification']=1;
$hesk_settings['pipe_customer_rejection_email_cooldown_hours']=24;

// --> Detect email typos
$hesk_settings['detect_typos']=1;
$hesk_settings['email_providers']=array('aim.com','aol.co.uk','aol.com','att.net','bellsouth.net','blueyonder.co.uk','bt.com','btinternet.com','btopenworld.com','charter.net','comcast.net','cox.net','earthlink.net','email.com','facebook.com','fastmail.fm','free.fr','freeserve.co.uk','gmail.com','gmx.at','gmx.ch','gmx.com','gmx.de','gmx.fr','gmx.net','gmx.us','googlemail.com','hotmail.be','hotmail.co.uk','hotmail.com','hotmail.com.ar','hotmail.com.mx','hotmail.de','hotmail.es','hotmail.fr','hushmail.com','icloud.com','inbox.com','laposte.net','lavabit.com','list.ru','live.be','live.co.uk','live.com','live.com.ar','live.com.mx','live.de','live.fr','love.com','lycos.com','mac.com','mail.com','mail.ru','me.com','msn.com','nate.com','naver.com','neuf.fr','ntlworld.com','o2.co.uk','online.de','orange.fr','orange.net','outlook.com','pobox.com','prodigy.net.mx','qq.com','rambler.ru','rocketmail.com','safe-mail.net','sbcglobal.net','t-online.de','talktalk.co.uk','tiscali.co.uk','verizon.net','virgin.net','virginmedia.com','wanadoo.co.uk','wanadoo.fr','yahoo.co.id','yahoo.co.in','yahoo.co.jp','yahoo.co.kr','yahoo.co.uk','yahoo.com','yahoo.com.ar','yahoo.com.mx','yahoo.com.ph','yahoo.com.sg','yahoo.de','yahoo.fr','yandex.com','yandex.ru','ymail.com');

// --> Notify customer when
$hesk_settings['notify_new']=1;
$hesk_settings['notify_skip_spam']=1;
$hesk_settings['notify_spam_tags']=array('Spam?}','***SPAM***','[SPAM]','SPAM-LOW:','SPAM-MED:');
$hesk_settings['notify_closed']=1;

// --> Other
$hesk_settings['multi_eml']=0;
$hesk_settings['confirm_email']=0;
$hesk_settings['open_only']=1;


// ==> TICKET LIST

$hesk_settings['ticket_list']=array('trackid','lastchange','name','subject','status','lastreplier');
$hesk_settings['customer_ticket_list']=array('id','trackid','lastchange','subject','status');

// --> Other
$hesk_settings['submittedformat']=2;
$hesk_settings['updatedformat']=2;
$hesk_settings['format_submitted']='Y-m-d g:i a';
$hesk_settings['format_updated']='Y-m-d g:i a';
$hesk_settings['email_column']=0;


// ==> MISC

// --> Date & Time
$hesk_settings['timezone']='America/Chicago';
$hesk_settings['format_time']='H:i:s';
$hesk_settings['format_date']='m/d/Y';
$hesk_settings['format_timestamp']='Y-m-d H:i:s';
$hesk_settings['time_display']=1;
$hesk_settings['format_datepicker_js']='mm/dd/yyyy';
$hesk_settings['format_datepicker_php']='m/d/Y';

// --> Other
$hesk_settings['ip_whois']='https://whois.domaintools.com/{IP}';
$hesk_settings['maintenance_mode']=0;
$hesk_settings['alink']=1;
$hesk_settings['submit_notice']=0;
$hesk_settings['online']=0;
$hesk_settings['online_min']=10;
$hesk_settings['check_updates']=1;


// ==> LOOK & FEEL
$hesk_settings['customer_theme']='';
$hesk_settings['theme_overrides']=array();


#############################
#     DO NOT EDIT BELOW     #
#############################
$hesk_settings['hesk_version']='3.7.10';
if ($hesk_settings['debug_mode'])
{
    error_reporting(E_ALL);
}
else
{
    error_reporting(0);
}
if (!defined('IN_SCRIPT')) {die('Invalid attempt!');}
ip_whois.php
wget 'https://lists2.roe3.org/hesk/ip_whois.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

// The purpose of this file is to hide ticket tracking ID in HTTP_REFERER when querying the IP WHOIS service

define('IN_SCRIPT',1);
define('HESK_PATH','./');

// Get all the required files and functions
require(HESK_PATH . 'hesk_settings.inc.php');

// Set correct Content-Type header
header('Content-Type: text/html; charset=utf-8');

// Most people will never see this text, so it is not included in text.php
// (saves resources as we don't need to call common.inc.php and load language)
$hesklang['1']='Page Redirection';
$hesklang['2']='If you are not redirected automatically, follow <a href="%s">this link</a>'; // %s will be replaced with URL

// Don't bother validating IP address format, just sure no invalid chars are sent
if ( isset($_GET['ip']) && preg_match('/^[0-9A-Fa-f\:\.]+$/', $_GET['ip']) )
{
	// Create redirect URL
	$url = str_replace('{IP}', str_replace(':', '%3A', $_GET['ip']), $hesk_settings['ip_whois']);

	// Redirect to the IP whois
	?>
	<!DOCTYPE HTML>
	<html lang="en-US">
		<head>
			<meta charset="UTF-8">
			<meta http-equiv="refresh" content="1;url=<?php echo $url; ?>">
			<script type="text/javascript">
			window.location.href = "<?php echo $url; ?>"
			</script>
			<title><?php echo $hesklang['1']; ?></title>
		</head>
		<body>
			<?php echo sprintf($hesklang['2'], $url); ?>
		</body>
	</html>
	<?php
}

// Exit
exit;
knowledgebase.php
wget 'https://lists2.roe3.org/hesk/knowledgebase.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

define('IN_SCRIPT',1);
define('HESK_PATH','./');

/* Get all the required files and functions */
require(HESK_PATH . 'hesk_settings.inc.php');
define('TEMPLATE_PATH', HESK_PATH . "theme/{$hesk_settings['site_theme']}/");
require(HESK_PATH . 'inc/common.inc.php');
require(HESK_PATH . 'inc/customer_accounts.inc.php');

// Are we in maintenance mode?
hesk_check_maintenance();

// Is Knowledgebase enabled?
if (!$hesk_settings['kb_enable'])
{
    hesk_error($hesklang['kbdis']);
}

// Connect to database
hesk_load_database_functions();
hesk_dbConnect();
hesk_session_start('CUSTOMER');

// Do we require logged-in customers to view the help desk?
hesk_isCustomerLoggedIn($hesk_settings['customer_accounts'] && $hesk_settings['customer_accounts_required'] == 2);

// Do we have any public articles at all?
$res = hesk_dbQuery("SELECT `t1`.`id` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_articles` AS `t1`
                    LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_categories` AS `t2` ON `t1`.`catid` = `t2`.`id`
                    WHERE `t1`.`type`='0' AND `t2`.`type`='0' LIMIT 1");

// If yes, load KB functions; if not, disable and hide the KB
if (hesk_dbNumRows($res) < 1)
{
    hesk_error($hesklang['noa']);
}

// Load KB functions
require(HESK_PATH . 'inc/knowledgebase_functions.inc.php');

/* Rating? */
if (isset($_GET['rating']))
{
	hesk_token_check('GET');

	// Detect and block robots
    if (hesk_detect_bots())
    {
		?>
		<html>
		<head>
		<meta name="robots" content="noindex, nofollow">
		</head>
		<body>
		</body>
		</html>
		<?php
    }

	// Rating
	$rating = intval( hesk_GET('rating') );

	// Rating value may only be 1 or 5
	if ($rating != 1 && $rating != 5)
	{
		die($hesklang['attempt']);
	}

	// Article ID
    $artid = intval( hesk_GET('id', 0) ) or die($hesklang['kb_art_id']);

    // Check cookies for already rated, rate and set cookie if not already
    $_COOKIE['hesk_kb_rate'] = hesk_COOKIE('hesk_kb_rate');

    $public_category_ids = count($hesk_settings['public_kb_categories_ids']) ? implode(',', $hesk_settings['public_kb_categories_ids']) : '0';

    if (strpos($_COOKIE['hesk_kb_rate'],'a'.$artid.'%')===false)
    {
		// Update rating, make sure it's a public article in a public category
		hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_articles` AS `t1`
					LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_categories` AS `t2` ON t1.`catid` = t2.`id`
					SET `rating`=((`rating`*`votes`)+{$rating})/(`votes`+1), t1.`votes`=t1.`votes`+1
					WHERE t1.`id`='{$artid}' AND t1.`type`='0' AND t2.`type`='0' AND t2.`id` IN ({$public_category_ids})
					");
    }

    hesk_setcookie('hesk_kb_rate', $_COOKIE['hesk_kb_rate'].'a'.$artid.'%', time()+2592000);
    header('Location: knowledgebase.php?article='.$artid.'&rated=1');
    exit();
}

/* Any category ID set? */
$catid = intval( hesk_GET('category', 1) );
$artid = intval( hesk_GET('article', 0) );

if (isset($_GET['search']))
{
	$query = hesk_input( hesk_GET('search') );
}
else
{
	$query = 0;
}

$hesk_settings['kb_link'] = ($artid || $catid != 1 || $query) ? '<a href="knowledgebase.php" class="smaller">'.$hesklang['kb_text'].'</a>' : $hesklang['kb_text'];

if ($hesk_settings['kb_search'] && $query)
{
    hesk_kb_search($query);
}
elseif ($artid)
{
	// Get article from DB, make sure that article and category are public
	$result  = hesk_dbQuery("SELECT t1.*, t2.`name` AS `cat_name`
							FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_articles` AS `t1`
							LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_categories` AS `t2` ON `t1`.`catid` = `t2`.`id`
							WHERE `t1`.`id` = '{$artid}'
							AND `t1`.`type` = '0'
							AND `t2`.`type` = '0'
                            ");

    $article = hesk_dbFetchAssoc($result) or hesk_error($hesklang['kb_art_id']);
    if ( ! isset($hesk_settings['public_kb_categories'][$article['catid']])) {
        hesk_error($hesklang['kb_art_id']);
    }
    $article['views_formatted'] = number_format($article['views'], 0, null, $hesklang['sep_1000']);
    $article['votes_formatted'] = number_format($article['votes'], 0, null, $hesklang['sep_1000']);
    if ($article['catid'] == 1)
    {
        $article['cat_name'] = $hesklang['kb_text'];
    }
    hesk_show_kb_article($artid);
}
elseif ( ! isset($hesk_settings['public_kb_categories'][$catid]))
{
    hesk_error($hesklang['kb_cat_inv']);
}
else
{
	hesk_show_kb_category($catid);
}

exit();



function hesk_kb_search($query) {
	global $hesk_settings, $hesklang;

    define('HESK_NO_ROBOTS',1);

    $hesk_settings['tmp_title'] = $hesklang['sr'] . ': ' . hesk_mb_substr(hesk_htmlspecialchars(stripslashes($query)),0,20);

	$res = hesk_dbQuery('SELECT t1.`id`, t1.`subject`, LEFT(`t1`.`content`, '.max(200, $hesk_settings['kb_substrart'] * 2).') AS `content`, t1.`rating`, t1.`votes`, t1.`views` FROM `'.hesk_dbEscape($hesk_settings['db_pfix']).'kb_articles` AS t1
    					LEFT JOIN `'.hesk_dbEscape($hesk_settings['db_pfix'])."kb_categories` AS t2 ON t1.`catid` = t2.`id`
                        WHERE t1.`type`='0' AND t2.`type`='0'
                        AND `t2`.`id` IN (".implode(',', $hesk_settings['public_kb_categories_ids']).")
                        AND MATCH(`subject`,`content`,`keywords`) AGAINST ('".hesk_dbEscape($query)."') LIMIT " . intval($hesk_settings['kb_search_limit']));
    $num = hesk_dbNumRows($res);

    $articles = array();
    while ($article = hesk_dbFetchAssoc($res))
    {
        $article['content_preview'] = hesk_kbArticleContentPreview($article['content']);
        $article['views_formatted'] = number_format($article['views'], 0, null, $hesklang['sep_1000']);
        $article['votes_formatted'] = number_format($article['votes'], 0, null, $hesklang['sep_1000']);
        $articles[] = $article;
    }

    if ($num === 0) {
        hesk_show_kb_category(1, 1);
    } else {
        $customerUserContext = hesk_isCustomerLoggedIn(false);
        $hesk_settings['render_template'](TEMPLATE_PATH . 'customer/knowledgebase/search-results.php', array(
                'articles' => $articles,
                'customerLoggedIn' => $customerUserContext !== null,
                'customerUserContext' => $customerUserContext
            )
        );
    }

    return true;
} // END hesk_kb_search()


function hesk_show_kb_article($artid)
{
	global $hesk_settings, $hesklang, $article;

	// Print header
    $hesk_settings['tmp_title'] = $article['subject'];

    // Update views by 1 - exclude known bots and reloads because of ratings
    if (!isset($_GET['rated']) && !hesk_detect_bots())
    {
		hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_articles` SET `views`=`views`+1 WHERE `id`={$artid}");
        $article['views']++;
        $article['views_formatted'] = number_format($article['views'], 0, null, $hesklang['sep_1000']);
    }

    if ($article['catid']==1)
    {
        $link = 'knowledgebase.php';
    }
    else
    {
        $link = 'knowledgebase.php?category='.$article['catid'];
    }

    $response = array(
        'article' => $article,
        'attachments' => array(),
        'showRating' => $hesk_settings['kb_rating'] && strpos(hesk_COOKIE('hesk_kb_rate'),'a'.$artid.'%') === false,
        'categoryLink' => $link
    );

    if (!empty($article['attachments'])) {
        $attachments = explode(',', substr($article['attachments'], 0, -1));
        foreach ($attachments as $attachment) {
            list($att_id, $att_name) = explode('#', $attachment);
            $response['attachments'][] = array(
                'id' => $att_id,
                'name' => $att_name
            );
        }
    }

    if (isset($_GET['rated'])) {
        $article['views']++;
    }

    $related_articles = array();
    // Related articles
    if ($hesk_settings['kb_related'])
    {
        require(HESK_PATH . 'inc/mail/email_parser.php');

        $query = hesk_dbEscape( $article['subject'] . ' ' . convert_html_to_text($article['content']) );

        $public_category_ids = count($hesk_settings['public_kb_categories_ids']) ? implode(',', $hesk_settings['public_kb_categories_ids']) : '0';

        // Get relevant articles from the database
        $res = hesk_dbQuery("SELECT t1.`id`, t1.`subject`, MATCH(`subject`,`content`,`keywords`) AGAINST ('{$query}') AS `score` FROM `".hesk_dbEscape($hesk_settings['db_pfix']).'kb_articles` AS t1 LEFT JOIN `'.hesk_dbEscape($hesk_settings['db_pfix'])."kb_categories` AS t2 ON t1.`catid` = t2.`id` WHERE t1.`type`='0' AND t2.`type`='0' AND t2.`id` IN ({$public_category_ids}) AND MATCH(`subject`,`content`,`keywords`) AGAINST ('{$query}') LIMIT ".intval($hesk_settings['kb_related']+1));

        while ($related = hesk_dbFetchAssoc($res)) {
            // Get base match score from the first article
            if (!isset($base_score)) {
                $base_score = $related['score'];
            }

            // Ignore this article
            if ($related['id'] == $artid) {
                continue;
            }

            // Stop when articles reach less than 10% of base score
            if ($related['score'] / $base_score < 0.10) {
                break;
            }

            // This is a valid related article
            $related_articles[$related['id']] = $related['subject'];
        }
    }
    $response['relatedArticles'] = $related_articles;

    $customerUserContext = hesk_isCustomerLoggedIn(false);
    $response['customerLoggedIn'] = $customerUserContext !== null;
    $response['customerUserContext'] = $customerUserContext;
    $response['serviceMessages'] = hesk_get_service_messages('kb-art');

    $hesk_settings['render_template'](TEMPLATE_PATH . 'customer/knowledgebase/view-article.php', $response);
} // END hesk_show_kb_article()


function hesk_show_kb_category($catid, $is_search = 0) {
	global $hesk_settings, $hesklang;

	$res = hesk_dbQuery("SELECT `id`,`name`,`parent` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_categories` WHERE `id`='{$catid}' AND `type`='0' LIMIT 1");
    $thiscat = hesk_dbFetchAssoc($res) or hesk_error($hesklang['kb_cat_inv']);

    // Top category? Translate name
    if ($thiscat['id'] == 1)
    {
        $thiscat['name'] = $hesklang['kb_text'];
        $service_messages_location = 'kb-main';
    }
    else {
        $service_messages_location = 'kb-sub';
    }

    $response = array(
        'currentCategory' => $thiscat,
        'noSearchResults' => $is_search,
        'service_messages' => array()
    );
    if ($is_search == 0)
    {
        /* Print header */
        $hesk_settings['tmp_title'] = $hesk_settings['hesk_title'] . ' - ' . hesk_htmlspecialchars($thiscat['name']);
    }

    if ($thiscat['parent'])
    {
        $response['parentLink'] = ($thiscat['parent'] == 1) ? 'knowledgebase.php' : 'knowledgebase.php?category='.$thiscat['parent'];
    }

    $result = hesk_dbQuery("SELECT `id`,`name`,`articles` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_categories` WHERE `parent`='{$catid}' AND `type`='0' ORDER BY `cat_order` ASC");
    $response['subcategories'] = array();
    $response['subcategoriesWidth'] = intval(100 / $hesk_settings['kb_cols']) . '%';
    if (hesk_dbNumRows($result) > 0)
    {
        while ($cat = hesk_dbFetchAssoc($result))
        {
            $displayShowMoreLink = false;
            $articles_to_display = array();
            $number_of_articles_to_display = 0;

            if ($hesk_settings['kb_numshow']) {

                $subcat = $hesk_settings['public_kb_categories'][$cat['id']];

                if ($cat['articles']) {
                    hesk_kb_fetch_article_previews($articles_to_display, $number_of_articles_to_display, $displayShowMoreLink, array($subcat['id']), $hesk_settings['kb_numshow']);
                }

                // Do we need more articles to display?
                if ( ! $displayShowMoreLink && count($subcat['descendants'])) {
                    hesk_kb_fetch_article_previews($articles_to_display, $number_of_articles_to_display, $displayShowMoreLink, $subcat['descendants'], $hesk_settings['kb_numshow'] - $number_of_articles_to_display);
                }
            }

            $response['subcategories'][] = array(
                'subcategory' => $cat,
                'articles' => $articles_to_display,
                'displayShowMoreLink' => $displayShowMoreLink
            );
        }
    } // END if NumRows > 0

    $articles_in_category = array();
    $res = hesk_dbQuery("SELECT `id`, `subject`, LEFT(`content`, ".max(200, $hesk_settings['kb_substrart'] * 2).") AS `content`, `rating`, `votes`, `views` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_articles` WHERE `catid`='{$catid}' AND `type`='0' ORDER BY `sticky` DESC, `art_order` ASC");

    while ($article = hesk_dbFetchAssoc($res))
    {
        $article['content_preview'] = hesk_kbArticleContentPreview($article['content']);
        $article['views_formatted'] = number_format($article['views'], 0, null, $hesklang['sep_1000']);
        $article['votes_formatted'] = number_format($article['votes'], 0, null, $hesklang['sep_1000']);
        $articles_in_category[] = $article;
    }
    $response['articlesInCategory'] = $articles_in_category;

    /* On the main KB page print out top and latest articles if needed */
    if ($catid == 1)
    {
        /* Get list of top articles */
        $response['topArticles'] = hesk_kbTopArticles($hesk_settings['kb_popart'], 0);

        /* Get list of latest articles */
        $response['latestArticles'] = hesk_kbLatestArticles($hesk_settings['kb_latest'], 0);
    } else {
        $response['topArticles'] = array();
        $response['latestArticles'] = array();
    }

    $customerUserContext = hesk_isCustomerLoggedIn(false);
    $response['customerLoggedIn'] = $customerUserContext !== null;
    $response['customerUserContext'] = $customerUserContext;
    $response['serviceMessages'] = hesk_get_service_messages($service_messages_location);

    $hesk_settings['render_template'](TEMPLATE_PATH . 'customer/knowledgebase/view-category.php', $response);
} // END hesk_show_kb_category()


function hesk_kb_fetch_article_previews(&$articles_to_display, &$number_of_articles_to_display, &$displayShowMoreLink, $categories, $limit) {
    global $hesk_settings, $hesklang;

    $res = hesk_dbQuery("SELECT `id`,`subject` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_articles` WHERE `catid` IN (" . implode(',', $categories) . ") AND `type`='0' ORDER BY `sticky` DESC, `views` DESC, `art_order` ASC LIMIT " . intval($limit + 1) );
    while ($art = hesk_dbFetchAssoc($res)) {
        $articles_to_display[] = $art;
        $number_of_articles_to_display++;
    }

    // Do we have more articles for display than what we need?
    if ($number_of_articles_to_display > $hesk_settings['kb_numshow']) {
        $displayShowMoreLink = true;
        array_pop($articles_to_display);
    }

} // END hesk_kb_fetch_article_previews()
login.php
wget 'https://lists2.roe3.org/hesk/login.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

define('IN_SCRIPT',1);
define('HESK_PATH','./');
define('HESK_NO_ROBOTS',1);

/* Get all the required files and functions */
require(HESK_PATH . 'hesk_settings.inc.php');
define('TEMPLATE_PATH', HESK_PATH . "theme/{$hesk_settings['site_theme']}/");
require(HESK_PATH . 'inc/common.inc.php');
require(HESK_PATH . 'inc/customer_accounts.inc.php');
require(HESK_PATH . 'inc/mfa_functions.inc.php');
require(HESK_PATH . 'inc/email_functions.inc.php');

// Are we in maintenance mode?
hesk_check_maintenance();

hesk_load_database_functions();
hesk_session_start('CUSTOMER');
hesk_dbConnect();

// Are customer accounts enabled? If not, redirect
if (!$hesk_settings['customer_accounts']) {
    return hesk_process_messages($hesklang['customer_accounts_disabled'], 'index.php');
}

// Are actually logging out (and not in?)
if (hesk_GET('a') === 'logout') {
    if ( ! hesk_token_check('GET', 0)) {
        hesk_process_messages($hesklang['eto'], $hesk_settings['hesk_url'] . '/index.php');
    }

    if (hesk_isCustomerLoggedIn(false) === null) {
        // User isn't even logged in; just redirect
        header('Location: ' . $hesk_settings['hesk_url'] . '/index.php');
        exit();
    }

    // Clear user's tokens
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."auth_tokens` 
        WHERE `user_id` = ".intval($_SESSION['customer']['id'])." 
        AND `user_type` = 'CUSTOMER'");
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."mfa_verification_tokens`
        WHERE `user_id` = ".intval($_SESSION['customer']['id'])."
        AND `user_type` = 'CUSTOMER'");

    /* Destroy session and cookies */
    hesk_session_stop();
    hesk_session_start('CUSTOMER');

    /* Show success message and reset the cookie */
    hesk_setcookie('hesk_customer_username', '');
    hesk_setcookie('hesk_customer_remember', '');
    hesk_process_messages($hesklang['logout_success'],$hesk_settings['hesk_url'] . '/index.php','SUCCESS');
}

// Is the user already logged in? If so, they shouldn't be needing this page at all.
if (hesk_isCustomerLoggedIn(false) !== null) {
    header('Location: ' . $hesk_settings['hesk_url'] . '/index.php');
    exit();
}

// Tell header to load reCaptcha API if needed
if ($hesk_settings['recaptcha_use'])
{
    define('RECAPTCHA',1);
}

if (hesk_isREQUEST('notice')) {
    if ($hesk_settings['customer_accounts_customer_self_register']) {
        hesk_process_messages($hesklang['customer_must_be_logged_in_to_view'] . ' ' . $hesklang['customer_register_here'],'NOREDIRECT','NOTICE');
    } else {
        hesk_process_messages($hesklang['customer_must_be_logged_in_to_view'],'NOREDIRECT','NOTICE');
    }
}

$savedUser = hesk_REQUEST('email', '');
if ($savedUser === '') {
    $savedUser = hesk_SESSION('login_email');
}

if ($savedUser === '') {
    if (defined('HESK_USER_CUSTOMER')) {
        $savedUser = HESK_USER_CUSTOMER;
    } else {
        $savedUser = hesk_htmlspecialchars(hesk_COOKIE('hesk_customer_username'));
    }
}

$model = [
    'email' => $savedUser
];

$action = hesk_REQUEST('a');
if ($action !== false) {
    switch ($action) {
        case 'forgot_password':
            handle_reset_password_request();
            break;
        case 'login':
            handle_login_request();
            return;
        case 'mfa_verify':
            handle_mfa_verification();
            return;
        case 'mfa_backup_code':
            handle_mfa_backup_code();
            return;
        case 'mfa_backup_email':
            send_mfa_backup_email();
            return;
        case 'resend_verification_email':
            resend_verification_email();
            return;
    }
} else {
    hesk_customerAutoLogin();
}

$messages = hesk_get_messages();

if ( ! isset($_SESSION['a_iserror'])) {
    $_SESSION['a_iserror'] = array();
}

$remember_user = hesk_POST('remember_user');
$select_autologin = $hesk_settings['customer_autologin'] && (isset($_COOKIE['hesk_customer_remember']) || $remember_user === 'AUTOLOGIN');
$select_save_username = isset($_COOKIE['hesk_customer_username']) || $remember_user === 'JUSTUSER';
$hesk_settings['render_template'](TEMPLATE_PATH . 'customer/account/login.php', array(
    'messages' => $messages,
    'serviceMessages' => hesk_get_service_messages('c-login', $hesk_settings['customer_accounts_required'] == 2),
    'model' => $model,
    'validationFailures' => hesk_SESSION_array('a_iserror'),
    'displayForgotPasswordLink' => $hesk_settings['reset_pass'],
    'displayForgotPasswordModal' => !empty($_REQUEST['forgot']),
    'submittedForgotPasswordForm' => !empty($_REQUEST['submittedForgot']),
    'redirectUrl' => hesk_REQUEST('goto', ''),
    'allowAutologin' => $hesk_settings['customer_autologin'] === 1,
    'selectAutologin' => $select_autologin,
    'selectSaveEmail' => $select_save_username,
    'selectDoNotRemember' => !($select_autologin || $select_save_username)
));

hesk_clear_customer_pending_login();
hesk_cleanSessionVars('login_email');
hesk_cleanSessionVars('a_iserror');

function handle_login_request() {
    global $hesk_settings, $hesklang;

    hesk_clear_customer_pending_login();

    hesk_dbConnect();

    /* Limit brute force attempts */
    hesk_limitBfAttempts();

    // Validation checks
    validate_data();

    // Only fetch users who are verified in some way, or are pending verification
    $res = hesk_dbQuery("SELECT `customers`.*,
            CASE
                WHEN `verification_email_sent_at` + INTERVAL ".intval($hesk_settings['customer_accounts_verify_email_cooldown'])." MINUTE < NOW() THEN 1
                ELSE 0
            END AS `can_request_new_verification_email`
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` AS `customers`
        WHERE `email` = '".hesk_dbEscape($_SESSION['login_email'])."'
            AND (`verified` > 0 OR (`verified` = 0 AND `verification_token` IS NOT NULL))
        LIMIT 1");

    if (hesk_dbNumRows($res) === 0) {
        hesk_process_messages(sprintf($hesklang['customer_wrong_user'], $_SESSION['login_email']), 'login.php');
    }

    $user = hesk_dbFetchAssoc($res);

    if ( ! is_string($user['pass']) || ! strlen($user['pass']) || ! hesk_password_verify($_POST['password'], $user['pass'])) {
        hesk_process_messages($hesklang['customer_wrong_pass'], 'login.php');
    }

    if (intval($user['verified']) === 0) {
        //-- Not verified.  If it's been past the cooldown period, provide a resend link
        $resend_link = '<br><br>' . $hesklang['customer_login_not_verified2'];
        if (intval($user['can_request_new_verification_email']) === 1) {
            $resend_link .= '<br><br><a href="'.$hesk_settings['hesk_url'] . '/login.php?a=resend_verification_email&email='.urlencode($_POST['email']).'">'.hesk_htmlspecialchars($hesklang['customer_login_resend_verification_email']).'</a>';
        } else {
            $resend_link .= '<br><br>' . $hesklang['customer_login_resend_verification_email_too_early'];
        }
        hesk_process_messages($hesklang['customer_login_not_verified'].$resend_link, 'login.php');
    }

    if (intval($user['verified']) === 2) {
        //-- Pending approval
        hesk_process_messages($hesklang['customer_login_not_approved'], 'login.php', 'NOTICE');
    }

    if (hesk_isBannedEmail($user['email'])) {
        //-- Customer is banned from the helpdesk
        hesk_process_messages($hesklang['customer_accounts_email_banned'], 'login.php');
    }

    if (hesk_password_needs_rehash($user['pass'])) {
        $user['pass'] = hesk_password_hash($_POST['password']);
        hesk_dbQuery("UPDATE `".$hesk_settings['db_pfix']."customers` SET `pass`='".hesk_dbEscape($user['pass'])."' WHERE `id`=".intval($user['id']));
    }

    $mfa_enrollment = intval($user['mfa_enrollment']);
    if ($mfa_enrollment === 0) {
        // Do we require MFA? Force it if the user has an email
        if ($hesk_settings['require_mfa_customers'] && strlen($user['email'])) {
            $mfa_enrollment = 1;
        } else {
            unset($_SESSION['login_email']);
            hesk_clear_customer_pending_login();
            hesk_process_successful_customer_login($user);
            return;
        }
    }

    $message = $hesklang['mfa_verification_needed'] . '<br><br>';
    $mfa_verify_option = 1;
    hesk_start_customer_pending_login($user, $mfa_enrollment === 1 ? 'EMAIL' : 'AUTH-APP', hesk_POST('remember_user', 'NOTHANKS'));
    if ($mfa_enrollment === 1) {
        // Email
        $verification_code = generate_mfa_code();
        hash_and_store_mfa_verification_code($user['id'], $verification_code, 'CUSTOMER');
        send_mfa_email($user['name'], $user['email'], $verification_code);

        $message .= $hesklang['mfa_verification_needed_email'];
    } elseif ($mfa_enrollment === 2) {
        // Authenticator app
        $message .= $hesklang['mfa_verification_needed_auth_app'];
        $mfa_verify_option = 2;
    }
    hesk_process_messages($message, 'NOREDIRECT', 'INFO');

    if ( ! isset($_SESSION['a_iserror'])) {
        $_SESSION['a_iserror'] = array();
    }

    $hesk_settings['render_template'](TEMPLATE_PATH . "customer/account/mfa-needed.php", array(
        'messages' => hesk_get_messages(),
        'model' => [
            'token' => hesk_token_echo(0),
            'verifyMethod' => $mfa_verify_option === 1 ? 'EMAIL' : 'AUTH-APP',
            'email' => $user['email']
        ]
    ));
    hesk_cleanSessionVars('a_iserror');
    exit();
}

function validate_data() {
    global $hesk_settings, $hesklang;

    $hesk_error_buffer = array();

    $_SESSION['login_email'] = hesk_validateEmail( hesk_POST('email'), 'ERR', 0) or $hesk_error_buffer['email'] = $hesklang['customer_login_email_required'];

    if (empty($_POST['password'])) {
        $hesk_error_buffer['password'] = $hesklang['customer_login_password_required'];
    }

    // Check anti-SPAM question
    if ($hesk_settings['question_use'])
    {
        $question = hesk_input( hesk_POST('question') );

        if ( strlen($question) == 0)
        {
            $hesk_error_buffer['question'] = $hesklang['q_miss'];
        }
        elseif (hesk_mb_strtolower($question) != hesk_mb_strtolower($hesk_settings['question_ans']))
        {
            $hesk_error_buffer['question'] = $hesklang['q_wrng'];
        }
        else
        {
            $_SESSION['c_question'] = $question;
        }
    }

    if ($hesk_settings['secimg_use'] && !isset($_SESSION['img_verified']))
    {
        // Using reCAPTCHA?
        if ($hesk_settings['recaptcha_use'])
        {
            require(HESK_PATH . 'inc/recaptcha/recaptchalib_v2.php');

            $resp = null;
            $reCaptcha = new ReCaptcha($hesk_settings['recaptcha_private_key']);

            // Was there a reCAPTCHA response?
            if ( isset($_POST["g-recaptcha-response"]) )
            {
                $resp = $reCaptcha->verifyResponse(hesk_getClientIP(), hesk_POST("g-recaptcha-response") );
            }

            if ($resp != null && $resp->success)
            {
                $_SESSION['img_verified']=true;
            }
            else
            {
                $hesk_error_buffer['mysecnum']=$hesklang['recaptcha_error'];
            }
        }
        // Using PHP generated image
        else
        {
            $mysecnum = intval( hesk_POST('mysecnum', 0) );

            if ( empty($mysecnum) )
            {
                $hesk_error_buffer['mysecnum'] = $hesklang['sec_miss'];
            }
            else
            {
                require(HESK_PATH . 'inc/secimg.inc.php');
                $sc = new PJ_SecurityImage($hesk_settings['secimg_sum']);

                if ( isset($_SESSION['checksum']) && $sc->checkCode($mysecnum, $_SESSION['checksum']) )
                {
                    $_SESSION['img_verified'] = true;
                    unset($_SESSION['checksum']);
                }
                else
                {
                    $hesk_error_buffer['mysecnum'] = $hesklang['sec_wrng'];
                }
            }
        }
    }

    /* Any missing fields? */
    if (count($hesk_error_buffer)!=0)
    {
        $_SESSION['a_iserror'] = array_keys($hesk_error_buffer);

        $tmp = '';
        foreach ($hesk_error_buffer as $error)
        {
            $tmp .= "<li>$error</li>\n";
        }
        $hesk_error_buffer = $tmp;

        $hesk_error_buffer = $hesklang['pcer'].'<br /><br /><ul>'.$hesk_error_buffer.'</ul>';
        hesk_process_messages($hesk_error_buffer,'login.php');
    }
    elseif (isset($_SESSION['img_verified']))
    {
        unset($_SESSION['img_verified']);
    }
}

function handle_reset_password_request() {
    global $hesklang, $hesk_settings;

    // Are customers permitted to submit a reset request?
    if (!$hesk_settings['reset_pass']) {
        hesk_process_messages($hesklang['attempt'], 'login.php');
    }

    //region Security image + data validation
    $hesk_error_buffer = [];
    if ($hesk_settings['secimg_use'] && !isset($_SESSION['img_verified_reset']))
    {
        // Using reCAPTCHA?
        if ($hesk_settings['recaptcha_use'])
        {
            require(HESK_PATH . 'inc/recaptcha/recaptchalib_v2.php');

            $resp = null;
            $reCaptcha = new ReCaptcha($hesk_settings['recaptcha_private_key']);

            // Was there a reCAPTCHA response?
            if ( isset($_POST["g-recaptcha-response"]) )
            {
                $resp = $reCaptcha->verifyResponse(hesk_getClientIP(), hesk_POST("g-recaptcha-response") );
            }

            if ($resp != null && $resp->success)
            {
                $_SESSION['img_verified_reset']=true;
            }
            else
            {
                $hesk_error_buffer['mysecnum']=$hesklang['recaptcha_error'];
            }
        }
        // Using PHP generated image
        else
        {
            $mysecnum = intval( hesk_POST('mysecnum', 0) );

            if ( empty($mysecnum) )
            {
                $hesk_error_buffer['mysecnum'] = $hesklang['sec_miss'];
            }
            else
            {
                require(HESK_PATH . 'inc/secimg.inc.php');
                $sc = new PJ_SecurityImage($hesk_settings['secimg_sum']);

                if ( isset($_SESSION['checksumreset']) && $sc->checkCode($mysecnum, $_SESSION['checksumreset']) )
                {
                    $_SESSION['img_verified_reset'] = true;
                    unset($_SESSION['checksumreset']);
                }
                else
                {
                    $hesk_error_buffer['mysecnum'] = $hesklang['sec_wrng'];
                }
            }
        }
    }

    /* Any missing fields? */
    if (count($hesk_error_buffer)!=0)
    {
        $_SESSION['a_iserror'] = array_keys($hesk_error_buffer);
        $_SESSION['login_email'] = hesk_POST('reset-email');

        $tmp = '';
        foreach ($hesk_error_buffer as $error)
        {
            $tmp .= "<li>$error</li>\n";
        }
        $hesk_error_buffer = $tmp;

        $hesk_error_buffer = $hesklang['pcer'].'<br /><br /><ul>'.$hesk_error_buffer.'</ul>';
        hesk_process_messages($hesk_error_buffer,'login.php?submittedForgot=1');
    }
    elseif (isset($_SESSION['img_verified_reset']))
    {
        unset($_SESSION['img_verified_reset']);
    }

    // Make sure email is valid
    $email = hesk_validateEmail(hesk_POST('reset-email'), 'ERR' , 0);
    if (!$email) {
        hesk_process_messages($hesklang['enter_valid_email'], 'login.php?submittedForgot=1');
    }

    $model['email'] = hesk_emailCleanup($email);

    hesk_handle_customer_password_reset_request($model['email']);

    hesk_process_messages($hesklang['password_reset_link_sent'], 'NOREDIRECT', 'SUCCESS');
}

function hesk_clear_customer_pending_login() {
    unset($_SESSION['customer_login_pending']);

    if (isset($_SESSION['customer']['mfa_emails_sent'])) {
        unset($_SESSION['customer']['mfa_emails_sent']);
    }

    hesk_cleanSessionVars('mfa_emails_sent');
}

function hesk_start_customer_pending_login($user, $mfa_method, $remember_user) {
    $allowed_remember_values = array('AUTOLOGIN', 'JUSTUSER', 'NOTHANKS');
    if ( ! in_array($remember_user, $allowed_remember_values, true)) {
        $remember_user = 'NOTHANKS';
    }

    $_SESSION['customer_login_pending'] = array(
        'customer_id'    => intval($user['id']),
        'email'          => $user['email'],
        'mfa_method'     => $mfa_method,
        'remember_user'  => $remember_user,
        'session_verify' => hesk_activeSessionCreateTag($user['email'], $user['pass']),
        'created_at'     => time(),
        'expires_at'     => time() + 20 * 60,
    );
}

function hesk_get_customer_pending_login_or_fail() {
    global $hesk_settings, $hesklang;

    if (empty($_SESSION['customer_login_pending']) || ! is_array($_SESSION['customer_login_pending'])) {
        hesk_clear_customer_pending_login();
        hesk_process_messages(sprintf($hesklang['customer_wrong_user'], ''), 'login.php');
    }

    $pending = $_SESSION['customer_login_pending'];

    if (empty($pending['customer_id']) || empty($pending['session_verify']) || empty($pending['expires_at'])) {
        hesk_clear_customer_pending_login();
        hesk_process_messages(sprintf($hesklang['customer_wrong_user'], ''), 'login.php');
    }

    if (time() > intval($pending['expires_at'])) {
        hesk_clear_customer_pending_login();
        hesk_process_messages($hesklang['mfa_invalid_verification_code'], 'login.php');
    }

    $user_rs = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` WHERE `id` = ".intval($pending['customer_id'])." AND `verified` = 1 LIMIT 1");

    if (hesk_dbNumRows($user_rs) !== 1) {
        hesk_clear_customer_pending_login();
        hesk_process_messages(sprintf($hesklang['customer_wrong_user'], ''), 'login.php');
    }

    $user = hesk_dbFetchAssoc($user_rs);

    if (hesk_isBannedEmail($user['email']) || ! hesk_activeSessionValidate($user['email'], $user['pass'], $pending['session_verify'])) {
        hesk_clear_customer_pending_login();
        hesk_process_messages(sprintf($hesklang['customer_wrong_user'], ''), 'login.php');
    }

    return array($user, $pending);
}

function handle_mfa_verification() {
    global $hesk_settings, $hesklang;

    hesk_token_check('POST');
    hesk_limitInternalBfAttempts();

    list($user, $pending) = hesk_get_customer_pending_login_or_fail();

    $mfa_method = $pending['mfa_method'];

    if (($mfa_method === 'EMAIL' && is_mfa_email_code_valid($user['id'], hesk_POST('verification-code'), 'CUSTOMER')) ||
        ($mfa_method === 'AUTH-APP' && is_mfa_app_code_valid($user['id'], hesk_POST('verification-code'), null, 'CUSTOMER'))) {
        hesk_cleanBfAttempts();
        $remember_user = $pending['remember_user'];
        unset($_SESSION['login_email']);
        hesk_clear_customer_pending_login();
        hesk_process_successful_customer_login($user, false, false, $remember_user);
        return;
    }

    hesk_process_messages($hesklang['mfa_invalid_verification_code'], 'NOREDIRECT');
    $hesk_settings['render_template'](TEMPLATE_PATH . "customer/account/mfa-needed.php", array(
        'messages' => hesk_get_messages(),
        'model' => [
            'token' => hesk_token_echo(0),
            'verifyMethod' => $mfa_method,
            'email' => $user['email']
        ]
    ));
}

function handle_mfa_backup_code() {
    global $hesk_settings, $hesklang;

    hesk_token_check('POST');
    hesk_limitInternalBfAttempts();

    list($user, $pending) = hesk_get_customer_pending_login_or_fail();

    if (verify_mfa_backup_code($user['id'], hesk_POST('backup-code'), 'CUSTOMER')) {
        hesk_cleanBfAttempts();
        $remember_user = $pending['remember_user'];
        unset($_SESSION['login_email']);
        hesk_clear_customer_pending_login();
        hesk_process_successful_customer_login($user, false, false, $remember_user);
        return;
    }

    //-- Verification failed
    hesk_process_messages($hesklang['mfa_invalid_backup_code'], 'NOREDIRECT');
    $hesk_settings['render_template'](TEMPLATE_PATH . "customer/account/mfa-needed.php", array(
        'messages' => hesk_get_messages(),
        'model' => [
            'token' => hesk_token_echo(0),
            'verifyMethod' => $pending['mfa_method'],
            'email' => $user['email'],
            'showBackupCode' => true
        ]
    ));
}

function send_mfa_backup_email() {
    global $hesk_settings, $hesklang;

    hesk_token_check('POST');

    list($user, $pending) = hesk_get_customer_pending_login_or_fail();

    // Let's limit the "Send another email" to max 3
    if (isset($_SESSION['customer']['mfa_emails_sent'])) {
        if ($_SESSION['customer']['mfa_emails_sent'] >= 3) {
            hesk_clear_customer_pending_login();
            hesk_process_messages($hesklang['bf_int'], 'login.php');
        }
        $_SESSION['customer']['mfa_emails_sent']++;
    } else {
        $_SESSION['customer']['mfa_emails_sent'] = 1;
    }

    $_SESSION['customer_login_pending']['mfa_method'] = 'EMAIL';
    $pending['mfa_method'] = 'EMAIL';

    $verification_code = generate_mfa_code();
    hash_and_store_mfa_verification_code($user['id'], $verification_code, 'CUSTOMER');
    send_mfa_email($user['name'], $user['email'], $verification_code);

    $message = $hesklang['mfa_verification_needed_email'];
    hesk_process_messages($message, 'NOREDIRECT', 'INFO');

    $hesk_settings['render_template'](TEMPLATE_PATH . "customer/account/mfa-needed.php", array(
        'messages' => hesk_get_messages(),
        'model' => [
            'token' => hesk_token_echo(0),
            'verifyMethod' => $pending['mfa_method'],
            'email' => $user['email']
        ]
    ));
    exit();
}

function resend_verification_email() {
    global $hesklang, $hesk_settings;

    /* Limit brute force attempts */
    hesk_limitBfAttempts();

    $email = hesk_GET('email');
    if ($email === '' || !hesk_isValidEmail($email)) {
        hesk_process_messages($hesklang['customer_resend_verification_email_needed'], 'login.php');
        return;
    }

    $user_info_rs = hesk_dbQuery("SELECT `customer`.*,
            CASE
                WHEN `verification_email_sent_at` + INTERVAL ".intval($hesk_settings['customer_accounts_verify_email_cooldown'])." MINUTE < NOW() THEN 1
                ELSE 0
            END AS `can_request_new_verification_email`
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` AS `customer` 
        WHERE `email` = '".hesk_dbEscape($email)."' 
        AND `verified` = 0 
        LIMIT 1");

    if (hesk_dbNumRows($user_info_rs) !== 1) {
        hesk_process_messages($hesklang['customer_resend_verification_email_not_found'], 'login.php');
        return;
    }

    $user_info = hesk_dbFetchAssoc($user_info_rs);
    if (!intval($user_info['can_request_new_verification_email'])) {
        hesk_process_messages($hesklang['customer_login_resend_verification_email_too_early'], 'login.php');
        return;
    }

    hesk_sendCustomerRegistrationEmail($user_info, $user_info['verification_token']);
    hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."customers`
        SET `verification_email_sent_at` = NOW()
        WHERE `id` = ".intval($user_info['id']));

    if (isset($_SESSION['img_verified']))
    {
        unset($_SESSION['img_verified']);
    }
    hesk_process_messages($hesklang['customer_resend_verification_email_sent'], 'login.php', 'SUCCESS');
}
manage_mfa.php
wget 'https://lists2.roe3.org/hesk/manage_mfa.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */
use RobThree\Auth\TwoFactorAuth;
define('IN_SCRIPT',1);
define('HESK_PATH','./');

// Get all the required files and functions
require(HESK_PATH . 'hesk_settings.inc.php');
define('TEMPLATE_PATH', HESK_PATH . "theme/{$hesk_settings['site_theme']}/");
require(HESK_PATH . 'inc/common.inc.php');
require_once(HESK_PATH . 'inc/customer_accounts.inc.php');
require_once(HESK_PATH . 'inc/statuses.inc.php');
require_once(HESK_PATH . 'inc/mfa_functions.inc.php');

// Are we in maintenance mode?
hesk_check_maintenance();

// Are we in "Knowledgebase only" mode?
hesk_check_kb_only();
hesk_session_start('CUSTOMER');
hesk_load_database_functions();
hesk_dbConnect();

$user_context = hesk_isCustomerLoggedIn();

// Demo mode
if ( defined('HESK_DEMO') ) {
    hesk_process_messages($hesklang['ddemo'], 'profile.php', 'NOTICE');
}

if ( ! isset($_SESSION['customer']['mfa_enrollment'])) {
    $_SESSION['customer']['mfa_enrollment'] = 0;
}

hesk_check_user_elevation('manage_mfa.php', true);

$current_step = intval(hesk_POST('current-step'));
if ($current_step > 0) {
    hesk_token_check('POST');
    $tfa = build_tfa_instance();
    if ($current_step === 1) {
        handle_mfa_method_selection($tfa, $user_context);
    } elseif ($current_step === 2) {
        handle_mfa_verification($tfa, $user_context);
    }
    return;
} elseif (hesk_POST('delete_codes') === 'Y') {
    hesk_token_check();
    delete_mfa_backup_codes($_SESSION['customer']['id'], 'CUSTOMER');
    hesk_process_messages($hesklang['mfa_del_codes2'], 'NOREDIRECT', 'SUCCESS');
    $display_step = 1;
    $output_at_top = 1;
} elseif (hesk_POST('new_codes') === 'Y') {
    hesk_token_check();
    delete_mfa_backup_codes($_SESSION['customer']['id'], 'CUSTOMER');
    $new_mfa_backup_codes = generate_and_store_mfa_backup_codes($_SESSION['customer']['id'], true, 'CUSTOMER');
    $backup_codes = implode("\n", array_map(function($code, $key) { return str_pad(($key+1), 2, ' ', STR_PAD_LEFT) . '. ' . substr($code, 0, 4) . '-' . substr($code, 4); }, $new_mfa_backup_codes, array_keys($new_mfa_backup_codes)));
    hesk_process_messages($hesklang['mfa_new_codes2'] . '<p style="margin-top:10px">'.$hesklang['mfa_backup_codes_description'].'</p><pre style="margin-top:20px; font-family: monospace; font-size: 16px;">'.$backup_codes.'</pre>', 'NOREDIRECT', 'SUCCESS');
    $display_step = 1;
    $output_at_top = 1;
}

$hesk_settings['render_template'](TEMPLATE_PATH . 'customer/account/mfa/method.php', array(
    'messages' => hesk_get_messages(),
    'customerUserContext' => $user_context
));

function handle_mfa_method_selection($tfa, $user_context) {
    global $hesklang, $hesk_settings;

    //-- Either removing MFA or setting it up
    $mfa_method = intval(hesk_POST('mfa-method', 0));
    if ($mfa_method === 0) {
        if ($hesk_settings['require_mfa_customers']) {
            hesk_process_messages($hesklang['mfa_invalid_method'], 'manage_mfa.php');
        }

        hesk_remove_mfa_for_customer($_SESSION['customer']['id']);
        delete_mfa_codes($_SESSION['customer']['id'], 'CUSTOMER');
        delete_mfa_backup_codes($_SESSION['customer']['id'], 'CUSTOMER');
        hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."auth_tokens` WHERE `user_id` = ".intval($_SESSION['customer']['id'])." AND `user_type` = 'CUSTOMER'");
        hesk_setcookie('hesk_customer_remember', '');
        $_SESSION['customer']['mfa_enrollment'] = 0;

        $hesk_settings['render_template'](TEMPLATE_PATH . 'customer/account/mfa/complete.php', array(
            'messages' => hesk_get_messages(),
            'customerUserContext' => $user_context,
            'model' => [
                'backupCodes' => []
            ]
        ));
        return;
    }

    if ($mfa_method === 1) {
        $verification_code = generate_mfa_code();
        hash_and_store_mfa_verification_code($_SESSION['customer']['id'], $verification_code, 'CUSTOMER');
        $email_sent = send_mfa_email($_SESSION['customer']['name'], $_SESSION['customer']['email'], $verification_code);

        $hesk_settings['render_template'](TEMPLATE_PATH . 'customer/account/mfa/verify.php', array(
            'mfaMethod' => 'EMAIL',
            'model' => [
                'emailSent' => $email_sent
            ],
            'messages' => hesk_get_messages(),
            'customerUserContext' => $user_context
        ));
        return;
    }

    if ($mfa_method === 2) {
        $_SESSION['customer']['tfa_secret'] = $tfa->createSecret();
        $hesk_settings['render_template'](TEMPLATE_PATH . 'customer/account/mfa/verify.php', array(
            'mfaMethod' => 'AUTH-APP',
            'model' => [
                'secret' => $_SESSION['customer']['tfa_secret'],
                'qrCodeUri' => function_exists('curl_init') ? $tfa->getQRCodeImageAsDataUri($hesk_settings['hesk_title'], $_SESSION['customer']['tfa_secret']) : false,
            ],
            'messages' => hesk_get_messages(),
            'customerUserContext' => $user_context
        ));
        return;
    }

    hesk_process_messages($hesklang['mfa_invalid_method'], 'manage_mfa.php');
}

function handle_mfa_verification($tfa, $user_context) {
    global $hesklang, $hesk_settings;

    // Template returns either 'EMAIL' or 'AUTH-APP'; not 1 nor 2.
    $mfa_method = hesk_POST('mfa-method');
    if ($mfa_method === 'EMAIL') {
        //-- Email
        $verification_code = hesk_POST('verification-code');
        if (is_mfa_email_code_valid($_SESSION['customer']['id'], $verification_code, 'CUSTOMER')) {
            //-- Enable MFA for the user and delete the verification code
            hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."customers`
                SET `mfa_enrollment` = 1
                WHERE `id` = ".intval($_SESSION['customer']['id']));
            $_SESSION['customer']['mfa_enrollment'] = 1;
            hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."auth_tokens` WHERE `user_id` = ".intval($_SESSION['customer']['id'])." AND `user_type` = 'CUSTOMER'");
            hesk_setcookie('hesk_customer_remember', '');
            $_SESSION['customer']['mfa_backup_codes'] = generate_and_store_mfa_backup_codes($_SESSION['customer']['id'], true, 'CUSTOMER');

            $hesk_settings['render_template'](TEMPLATE_PATH . 'customer/account/mfa/complete.php', array(
                'messages' => hesk_get_messages(),
                'customerUserContext' => $user_context,
                'model' => [
                    'backupCodes' => $_SESSION['customer']['mfa_backup_codes']
                ]
            ));
        } else {
            //-- Invalid code entered
            hesk_process_messages($hesklang['mfa_invalid_verification_code'], 'NOREDIRECT');
            $hesk_settings['render_template'](TEMPLATE_PATH . 'customer/account/mfa/verify.php', array(
                'mfaMethod' => 'EMAIL',
                'model' => [
                    'emailSent' => false
                ],
                'messages' => hesk_get_messages(),
                'customerUserContext' => $user_context
            ));
        }
    } elseif ($mfa_method === 'AUTH-APP') {
        $secret = $_SESSION['customer']['tfa_secret'];
        if (is_mfa_app_code_valid($_SESSION['customer']['id'], hesk_POST('verification-code'), $secret, 'CUSTOMER')) {
            hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."customers`
                SET `mfa_enrollment` = 2,
                    `mfa_secret` = '".hesk_dbEscape($secret)."'
                WHERE `id` = ".intval($_SESSION['customer']['id']));
            $_SESSION['customer']['mfa_backup_codes'] = generate_and_store_mfa_backup_codes($_SESSION['customer']['id'], true, 'CUSTOMER');
            unset($_SESSION['customer']['tfa_secret']);
            $_SESSION['customer']['mfa_enrollment'] = 2;
            hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."auth_tokens` WHERE `user_id` = ".intval($_SESSION['customer']['id'])." AND `user_type` = 'CUSTOMER'");
            hesk_setcookie('hesk_customer_remember', '');
            $hesk_settings['render_template'](TEMPLATE_PATH . 'customer/account/mfa/complete.php', array(
                'messages' => hesk_get_messages(),
                'customerUserContext' => $user_context,
                'model' => [
                    'backupCodes' => $_SESSION['customer']['mfa_backup_codes']
                ]
            ));
        } else {
            //-- Invalid code entered
            hesk_process_messages($hesklang['mfa_invalid_verification_code'], 'NOREDIRECT');
            $hesk_settings['render_template'](TEMPLATE_PATH . 'customer/account/mfa/verify.php', array(
                'mfaMethod' => 'AUTH-APP',
                'model' => [
                    'secret' => $_SESSION['customer']['tfa_secret'],
                    'qrCodeUri' => function_exists('curl_init') ? $tfa->getQRCodeImageAsDataUri($hesk_settings['hesk_title'], $_SESSION['customer']['tfa_secret']) : false,
                ],
                'messages' => hesk_get_messages(),
                'customerUserContext' => $user_context
            ));
        }
    } else {
        hesk_process_messages($hesklang['mfa_invalid_method'], 'manage_mfa.php');
    }
}
my_tickets.php
wget 'https://lists2.roe3.org/hesk/my_tickets.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */
define('IN_SCRIPT',1);
define('HESK_PATH','./');

// Get all the required files and functions
require(HESK_PATH . 'hesk_settings.inc.php');
define('TEMPLATE_PATH', HESK_PATH . "theme/{$hesk_settings['site_theme']}/");
require(HESK_PATH . 'inc/common.inc.php');
require_once(HESK_PATH . 'inc/customer_accounts.inc.php');
require_once(HESK_PATH . 'inc/custom_fields.inc.php');
require_once(HESK_PATH . 'inc/statuses.inc.php');
require_once(HESK_PATH . 'inc/priorities.inc.php');

// Are we in maintenance mode?
hesk_check_maintenance();

// Are we in "Knowledgebase only" mode?
hesk_check_kb_only();
hesk_session_start('CUSTOMER');
hesk_load_database_functions();
hesk_dbConnect();

$user_context = hesk_isCustomerLoggedIn();

// Fix a bug before we find a better way to do it :)
if ($user_context === true) {
    $user_context = hesk_isCustomerLoggedIn();
}

$additional_sql_filters = '';
$page_size = intval(hesk_REQUEST('page-size', 20));
if ($page_size < 1) {
    $page_size = 20;
} elseif ($page_size > 500) {
    $page_size = 500;
}

$page_number = intval(hesk_REQUEST('page-number', 1));
if ($page_number < 1) {
    $page_number = 1;
}
$search_criteria = hesk_input(hesk_REQUEST('search', ''));
$search_by = hesk_input(hesk_REQUEST('search-by', ''));
$status = hesk_input(hesk_REQUEST('status', 'ALL'));
$order_by = hesk_input(hesk_REQUEST('order-by', ''));
$order_direction = hesk_input(hesk_REQUEST('order-direction', 'desc'));

if (!in_array($order_direction, ['asc','desc'])) {
    $order_direction = 'desc';
}

$sql_order_by = '`status_sort`, `lastchange`';

if ($order_by == 'priority') {
    $sql_order_by = "`priority_order`";
} elseif (! in_array($order_by, $hesk_settings['customer_ticket_list'])) {
    // If no sort or an invalid sort is requested, revert to the default order direction
    $order_by = '';
    $order_direction = 'desc';
} else {
    if ($order_by == 'name') {
        $sql_order_by = "`tickets`.`u_name`";
    } elseif ($order_by == 'email') {
        $sql_order_by = "`tickets`.`u_email`";
    } else {
        $sql_order_by = "`{$order_by}`";
    }
}

// Do we need category names?
if (in_array('category', $hesk_settings['customer_ticket_list']) && ! isset($hesk_settings['categories'])) {
    $hesk_settings['categories'] = array();
    $res2 = hesk_dbQuery('SELECT `id`, `name` FROM `'.hesk_dbEscape($hesk_settings['db_pfix']).'categories` ORDER BY `cat_order` ASC');
    while ($row=hesk_dbFetchAssoc($res2)) {
        $hesk_settings['categories'][$row['id']] = $row['name'];
    }
}

// Do we need staff names?
if (array_intersect(array('owner', 'lastreplier'), $hesk_settings['customer_ticket_list'])) {
    $admins = array();
    $res2 = hesk_dbQuery("SELECT `id`,`name`,`nickname` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."users`");
    while ($row = hesk_dbFetchAssoc($res2)) {
        $admins[$row['id']] = ($hesk_settings['staff_nicknames'] && $row['nickname'] != '') ? $row['nickname'] : $row['name'];
    }
} else {
    $admins = array();
}

if ($search_criteria !== '') {
    $criteria = '';
    switch ($search_by) {
        case 'subject':
            $criteria = "`tickets`.`subject` LIKE '%%".hesk_dbEscape(hesk_dbLike($search_criteria))."%%' COLLATE '".hesk_dbCollate()."'";
            break;
        case 'message':
            $criteria = "(`message` LIKE '%%".hesk_dbEscape(hesk_dbLike($search_criteria))."%%' COLLATE '".hesk_dbCollate()."'
            		OR `tickets`.`id` IN (
                        SELECT DISTINCT `replyto`
                        FROM  `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` AS `replies`
                        WHERE  `replies`.`message` LIKE '%%".hesk_dbEscape(hesk_dbLike($search_criteria))."%%' COLLATE '".hesk_dbCollate()."')
                    )";
            break;
        case 'trackid':
        default:
            $criteria = "`tickets`.`trackid` = '".hesk_dbEscape($search_criteria)."' OR `tickets`.`merged` LIKE '%%#".hesk_dbEscape(hesk_dbLike($search_criteria))."#%%'";
            break;
    }

    // Escaping % due to sprintf
    $additional_sql_filters = " AND ({$criteria}) ";
}
if ($status !== 'ALL') {
    $operator = $status === 'CLOSED' ? '=' : '<>';
    $additional_sql_filters .= " AND `tickets`.`status` {$operator} 3";
}

// Fetch tickets
$offset = ($page_number - 1) * $page_size;

// Get last replier name
if (in_array('lastreplier', $hesk_settings['customer_ticket_list'])) {
    $sql_lastreplier = "LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` AS `lastreplier_customer`
        ON `tickets`.`lastreplier` = '0'
        AND `lastreplier_customer`.`id` = (
            SELECT `customer_id`
            FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."replies`
            WHERE `replyto` = `tickets`.`id`
                AND `customer_id` IS NOT NULL
            ORDER BY `id` DESC
            LIMIT 1)
            ";
} else {
    $sql_lastreplier = '';
}

$sql_format = "SELECT %s,`priority_order` AS `vv`
 FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` AS `tickets`
    INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` AS `ticket_customers`
        ON `tickets`.`id` = `ticket_customers`.`ticket_id`
    LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."custom_priorities` AS `custom_priorities`
        ON `priority` = `custom_priorities`.`id`
    $sql_lastreplier
    WHERE `customer_id` = ".intval($user_context['id'])." "
    .$additional_sql_filters;

//region Open vs Closed Tickets
$open_vs_closed_count_rs = hesk_dbQuery("SELECT SUM(`open`) AS `open`, SUM(`closed`) AS `closed` FROM (
    SELECT COUNT(1) AS `open`, 0 AS `closed`
    FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` AS `tickets`
    INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` AS `ticket_customers`
        ON `tickets`.`id` = `ticket_customers`.`ticket_id`
    WHERE `customer_id` = ".intval($user_context['id'])."
    AND `status` <> 3
    ".$additional_sql_filters."
    UNION
    SELECT 0 AS `open`, COUNT(1) AS `closed`
    FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` AS `tickets`
    INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` AS `ticket_customers`
        ON `tickets`.`id` = `ticket_customers`.`ticket_id`
    WHERE `customer_id` = ".intval($user_context['id'])."
    AND `status` = 3
    ".$additional_sql_filters."
) AS `t1`");
$ticket_counts = [
    'open' => 0,
    'closed' => 0
];
if ($row = hesk_dbFetchAssoc($open_vs_closed_count_rs)) {
    $ticket_counts = $row;
}
//endregion

$sql_customer_count = '';
$sql_email_count = '';

/*
// Get customer count
if (in_array('name', $hesk_settings['customer_ticket_list'])) {
    $sql_customer_count = "SELECT COUNT(1) FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` AS `ticket_to_customer_names`
        INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` `customer_names`
            ON `ticket_to_customer_names`.`customer_id` = `customer_names`.`id`
        WHERE `ticket_id` = `tickets`.`id`";
}

// Get email count
if (in_array('email', $hesk_settings['customer_ticket_list'])) {
    $sql_email_count = "SELECT COUNT(1) FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` AS `ticket_to_customer_emails`
        INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` `customer_emails`
            ON `ticket_to_customer_emails`.`customer_id` = `customer_emails`.`id`
        WHERE `ticket_id` = `tickets`.`id`
            AND COALESCE(`customer_emails`.`email`, '') <> ''";
}
*/

//region Search Results
$tickets_rs = hesk_dbQuery(sprintf($sql_format,
    "CASE
        WHEN `status` = 2 THEN 1
        WHEN `status` = 3 THEN 999
        ELSE 2
    END AS `status_sort`,
    `tickets`.*
    ".($sql_customer_count ? ", ({$sql_customer_count}) AS `customer_count`" : '')."
    ".($sql_email_count ? ", ({$sql_email_count}) AS `email_count`" : '')."
    ".($sql_lastreplier ? ", `lastreplier_customer`.`name` AS `lastreplier_customername`" : '')."
    ").
    "ORDER BY {$sql_order_by} {$order_direction} LIMIT {$page_size} OFFSET {$offset}");
$tickets = [];
while ($ticket = hesk_dbFetchAssoc($tickets_rs)) {
    // Copied from ticket_list.inc.php
    switch ($hesk_settings['updatedformat'])
    {
        case 1:
            $ticket['lastchange'] = hesk_date($ticket['lastchange'], true, true, true, $hesk_settings['format_timestamp']);
            break;
        case 2:
            $ticket['lastchange'] = hesk_time_lastchange($ticket['lastchange']);
            break;
        case 3:
            $ticket['lastchange'] = hesk_date($ticket['lastchange'], true, true, true, $hesk_settings['format_date']);
            break;
        case 4:
            $ticket['lastchange'] = hesk_date($ticket['lastchange'], true, true, true, $hesk_settings['format_updated']);
            break;
        default:
            $mysql_time = hesk_dbTime();
            $ticket['lastchange'] = hesk_time_since( strtotime($ticket['lastchange']) );
    }

    $ticket['status_id'] = $ticket['status'];
    $ticket['status'] = hesk_get_ticket_status($ticket['status']);
    if ( ! isset($hesk_settings['priorities'][$ticket['priority']])) {
        $ticket['priority'] = array_keys($hesk_settings['priorities'])[0];
    }

    $tickets[] = $ticket;
}
//endregion

$hesk_settings['render_template'](TEMPLATE_PATH . 'customer/my-tickets.php', array(
    'customerUserContext' => $user_context,
    'messages' => hesk_get_messages(),
    'serviceMessages' => hesk_get_service_messages('c-main'),
    'tickets' => $tickets,
    'ticketCounts' => $ticket_counts,
    'admins' => $admins,
    'searchCriteria' => $search_criteria,
    'searchType' => $search_by,
    'status' => $status,
    'ordering' => [
        'orderBy' => $order_by,
        'orderDirection' => $order_direction,
    ],
    'paging' => [
        'pageNumber' => $page_number,
        'pageSize' => $page_size
    ]
));
print.php
wget 'https://lists2.roe3.org/hesk/print.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

define('IN_SCRIPT',1);
define('HESK_PATH','./');

/* Get all the required files and functions */
require(HESK_PATH . 'hesk_settings.inc.php');
define('TEMPLATE_PATH', HESK_PATH . "theme/{$hesk_settings['site_theme']}/");
require(HESK_PATH . 'inc/common.inc.php');
require_once(HESK_PATH . 'inc/customer_accounts.inc.php');
hesk_load_database_functions();

hesk_session_start('CUSTOMER');

// Do we have parameters in query string? If yes, store them in session and redirect
if ( isset($_GET['track']) || isset($_GET['e']) )
{
    $_SESSION['p_track'] = hesk_GET('track');
    $_SESSION['p_email'] = hesk_GET('e');

    header('Location: print.php');
    die();
}

/* Get the tracking ID */
$trackingID = hesk_cleanID('p_track') or die("$hesklang[int_error]: $hesklang[no_trackID]");

/* Connect to database */
hesk_dbConnect();

// Load custom fields
require_once(HESK_PATH . 'inc/custom_fields.inc.php');

// Load statuses
require_once(HESK_PATH . 'inc/statuses.inc.php');

// Perform additional checks for customers
// Are we in maintenance mode?
hesk_check_maintenance();

// Verify email address match
$my_email = hesk_getCustomerEmail(0, 'p_email');
hesk_verifyEmailMatch($trackingID, $my_email);

/* Clean ticket parameters from the session data, we don't need them anymore */
hesk_cleanSessionVars( array('p_track', 'p_email') );

/* Get ticket info */
$res = hesk_dbQuery("SELECT `t1`.* , `t2`.name AS `repliername`, `t2`.`nickname`
					FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` AS `t1` LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."users` AS `t2` ON `t1`.`replierid` = `t2`.`id`
					WHERE `trackid`='".hesk_dbEscape($trackingID)."' LIMIT 1");

if (hesk_dbNumRows($res) != 1)
{
	hesk_error($hesklang['ticket_not_found']);
}
$ticket = hesk_dbFetchAssoc($res);
$customers = hesk_get_customers_for_ticket($ticket['id']);

// Does staff have a nickname set? Use it.
if ($ticket['lastreplier'] && $hesk_settings['staff_nicknames'] && $ticket['nickname'] != '') {
    $ticket['repliername'] = $ticket['nickname'];
}

// Demo mode
if ( defined('HESK_DEMO') )
{
    foreach ($customers as $customer) {
        $customer['email'] = 'hidden@demo.com';
    }
	$ticket['ip']	 = '127.0.0.1';
}

/* Get category name and ID */
$res = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` WHERE `id`='{$ticket['category']}' LIMIT 1");

/* If this category has been deleted use the default category with ID 1 */
if (hesk_dbNumRows($res) != 1)
{
	$res = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` WHERE `id`='1' LIMIT 1");
}
$category = hesk_dbFetchAssoc($res);

/* Get replies */
$res  = hesk_dbQuery("SELECT `replies`.*, `reply_customer`.`name` AS `customer_name`, `reply_customer`.`email` AS `customer_email`, `reply_staff`.`name` AS `staff_name`, `reply_staff`.`nickname`
FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` AS `replies`
LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` AS `reply_customer`
    ON `replies`.`customer_id` = `reply_customer`.`id`
LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."users` AS `reply_staff`
    ON `replies`.`staffid` = `reply_staff`.`id`
WHERE `replyto`='{$ticket['id']}' ORDER BY `replies`.`id` ASC");

$replies = [];
while ($row = hesk_dbFetchAssoc($res)) {
    if (intval($row['staffid']) > 0) {
        $row['name'] = $row['staff_name'] === null ?
            $hesklang['staff_deleted'] :
            (($hesk_settings['staff_nicknames'] && $row['nickname'] != '') ? $row['nickname'] : $row['staff_name']);
    } else {
        $row['name'] = strlen($row['customer_name']) ? $row['customer_name'] : ( ! empty($row['customer_email']) ? $row['customer_email'] : $hesklang['pde']);
    }
    $replies[] = $row;
}

/* Get notes */
$notes = array();
if (!empty($_SESSION['id']))
{
    $res2 = hesk_dbQuery("SELECT t1.*, t2.`name` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."notes` AS t1 LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."users` AS t2 ON t1.`who` = t2.`id` WHERE `ticket`='{$ticket['id']}' ORDER BY t1.`id`");
    while ($note = hesk_dbFetchAssoc($res2))
    {
        $notes[] = $note;
    }
}

$ticket['notes'] = $notes;
$ticket['replies'] = $replies;
$ticket['categoryName'] = $category['name'];
$ticket['customers'] = $customers;

$tickets = array($ticket);
require_once(HESK_PATH . 'inc/print_template.inc.php');

print_sec_img.php
wget 'https://lists2.roe3.org/hesk/print_sec_img.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

define('IN_SCRIPT',1);
define('HESK_PATH','./');

/* Get all the required files and functions */
require(HESK_PATH . 'hesk_settings.inc.php');
require(HESK_PATH . 'inc/common.inc.php');
require(HESK_PATH . 'inc/secimg.inc.php');

hesk_session_start('CUSTOMER');

// We may have more than one instance of the anti-spam image on the page
$page = hesk_GET('p', '');

// Let's allow whitelisted page variables only
if ( ! in_array($page, array('', 'reset'))) {
    $page = '';
}

$_SESSION['secnum' . $page]   = mt_rand(10000,99999);
$_SESSION['checksum' . $page] = sha1($_SESSION['secnum' . $page] . $hesk_settings['secimg_sum']);

/* This will make sure the security image is not cached */
header("expires: -1");
header("cache-control: no-cache, no-store, must-revalidate, max-age=-1");
header("cache-control: post-check=0, pre-check=0", false);
header("pragma: no-store,no-cache");

$sc = new PJ_SecurityImage($hesk_settings['secimg_sum']);
$sc->printImage($_SESSION['secnum' . $page]);

exit();
profile.php
wget 'https://lists2.roe3.org/hesk/profile.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

define('IN_SCRIPT',1);
define('HESK_PATH','./');
define('HESK_NO_ROBOTS',1);

/* Get all the required files and functions */
require(HESK_PATH . 'hesk_settings.inc.php');
define('TEMPLATE_PATH', HESK_PATH . "theme/{$hesk_settings['site_theme']}/");
require(HESK_PATH . 'inc/common.inc.php');
require(HESK_PATH . 'inc/customer_accounts.inc.php');

// Are we in maintenance mode?
hesk_check_maintenance();

hesk_load_database_functions();
hesk_session_start('CUSTOMER');
hesk_dbConnect();

$customerUserContext = hesk_isCustomerLoggedIn();
hesk_purge_expired_email_change_requests();

$hesk_error_buffer = array();

if (hesk_REQUEST('action') !== '') {
    // What are changing?
    $action = hesk_REQUEST('action');
    if ( ! empty($action) && defined('HESK_DEMO') ) {
        hesk_process_messages($hesklang['ddemo'], 'NOREDIRECT', 'NOTICE');
    } elseif ($action === 'profile') {
        hesk_token_check('POST');
        handle_profile_update($hesk_error_buffer, $customerUserContext);
    } elseif ($action === 'password') {
        hesk_token_check('POST');
        handle_password_change($hesk_error_buffer);
    } elseif ($action === 'email') {
        hesk_token_check('POST');
        handle_email_change($hesk_error_buffer, $customerUserContext);
    } elseif ($action === 'email-resend') {
        hesk_token_check();
        hesk_resend_email_change_notification($customerUserContext);
    }

    if (count($hesk_error_buffer)) {
        $tmp = '';
        foreach ($hesk_error_buffer as $error) {
            $tmp .= "<li>$error</li>\n";
        }
        hesk_process_messages($hesklang['pcer'] . '<br /><br /><ul>' . $tmp . '</ul>', 'NOREDIRECT');
    }
}
$messages = hesk_get_messages();

//-- Fetch a new user context in case name has changed
$customerUserContext = hesk_isCustomerLoggedIn(false);

$pendingEmailChange = hesk_get_pending_email_change_for_user($customerUserContext['id']);
$hesk_settings['render_template'](TEMPLATE_PATH . 'customer/account/profile.php', array(
    'customerUserContext' => $customerUserContext,
    'pendingEmailChange' => $pendingEmailChange,
    'userCanChangeEmail' => $hesk_settings['customer_accounts_allow_email_changes'],
    'messages' => $messages,
    'serviceMessages' => hesk_get_service_messages('c-profile'),
    'validationFailures' => array_keys($hesk_error_buffer)
));

function handle_profile_update(&$hesk_error_buffer, $customerUserContext) {
    global $hesk_settings, $hesklang;

    $name = hesk_input(hesk_POST('name'));
    if (!$name) {
        $hesk_error_buffer['name'] = $hesklang['enter_your_name'];
    }

    $language_sql = '';
    if ($hesk_settings['can_sel_lang']) {
        $language = hesk_input( hesk_POST('language') ) or $language = $hesk_settings['language'];
        if (isset($hesk_settings['languages'][$language])) {
            $language_sql = ", `language` = '".hesk_dbEscape($language)."' ";
            if ($language != $hesk_settings['language']) {
                hesk_setLanguage($language);
                $customerUserContext['language'] = $language;
                hesk_setcookie('hesk_language', $language, time()+31536000, '/');
            }
        }
    }

    if (count($hesk_error_buffer) === 0) {
        hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` 
                SET
                    `name` = '".hesk_dbEscape($name)."'
                    $language_sql
                WHERE `id` = ".intval($_SESSION['customer']['id']));
        $_SESSION['customer']['name'] = $name;
        $customerUserContext['name'] = $name;
        hesk_process_messages($hesklang['customer_profile_saved'], 'NOREDIRECT', 'SUCCESS');
    }
}

function handle_password_change(&$hesk_error_buffer) {
    global $hesk_settings, $hesklang;

    // Current password
    $current_password = hesk_input(hesk_POST('current-password'));
    if (!$current_password) {
        $hesk_error_buffer['current-password'] = $hesklang['enter_pass'];
    } else {
        hesk_limitInternalBfAttempts();

        // Get current password hash from DB
        $result = hesk_dbQuery("SELECT `pass` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` WHERE `id` = ".intval($_SESSION['customer']['id'])." LIMIT 1");
        if (hesk_dbNumRows($result) != 1) {
            hesk_forceLogoutCustomer($hesklang['wrong_user']);
        }
        $user_row = hesk_dbFetchAssoc($result);

        // Validate current password
        if (hesk_password_verify($current_password, $user_row['pass'])) {
            hesk_cleanBfAttempts();
        } else {
            $hesk_error_buffer['current-password'] = $hesklang['wrong_pass'];
        }
    }

    // New password
    $new_password = hesk_input(hesk_POST('password'));
    if (!$new_password) {
        $hesk_error_buffer['password'] = $hesklang['e_new_pass'];
    } elseif (strlen($new_password) < 5) {
        $hesk_error_buffer['password'] = $hesklang['password_not_valid'];
    } elseif (isset($user_row) && hesk_password_verify($new_password, $user_row['pass'])) {
        $hesk_error_buffer['password'] = $hesklang['customer_edit_pass_same'];
    }

    // Confirm password
    $confirm_password = hesk_input( hesk_POST('confirm-password') );
    if ($new_password !== $confirm_password) {
        $hesk_error_buffer['confirm-password'] = $hesklang['passwords_not_same'];
    }

    if (count($hesk_error_buffer) === 0) {
        $newpass_hash = hesk_password_hash($new_password);
        hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` SET `pass` = '".hesk_dbEscape($newpass_hash)."' WHERE `id` = ".intval($_SESSION['customer']['id']));

        // Force login after password change
        hesk_forceLogoutCustomer($hesklang['pass_login'], 'profile.php', null, 'NOTICE');
    }
}

function handle_email_change(&$hesk_error_buffer, $customerUserContext) {
    global $hesk_settings, $hesklang;

    $email = hesk_validateEmail( hesk_POST('email'), 'ERR', 0);
    if (!$email) {
        $hesk_error_buffer['email'] = $hesklang['enter_valid_email'];
        return;
    }
    if (!$hesk_settings['customer_accounts_allow_email_changes']) {
        hesk_process_messages($hesklang['customer_change_email_disabled'], 'profile.php');
        return;
    }

    // Is the target email banned?
    if (hesk_isBannedEmail($email)) {
        $hesk_error_buffer['email'] = $hesklang['customer_change_email_banned'];
        return;
    }

    hesk_purge_expired_email_change_requests();

    if ($email === $customerUserContext['email']) {
        hesk_process_messages($hesklang['customer_profile_saved'], 'NOREDIRECT', 'SUCCESS');
        return;
    }

    // Make sure we don't have another active customer with this email, or someone else attempting to change their
    // email to this one
    if (hesk_get_customer_account_by_email($email, true) !== null ||
        hesk_get_pending_email_change_for_email($email, $customerUserContext['id'])) {
        $hesk_error_buffer['email'] = sprintf($hesklang['customer_registration_email_exists_no_reset_link'], $email);
        return;
    }

    // Has the user requested an email change too recently?
    $new_email = hesk_get_pending_email_change_for_user($customerUserContext['id']);
    if ($new_email !== null && $new_email['email_sent_too_recently']) {
        hesk_process_messages($hesklang['customer_login_resend_verification_email_too_early'], 'profile.php');
        return;
    }


    // All good; insert the change request and email them
    hesk_build_and_send_email($customerUserContext, $email);
    hesk_process_messages(sprintf($hesklang['customer_change_email_submitted'], $email), 'NOREDIRECT', 'SUCCESS');
}

function hesk_build_and_send_email($customerUserContext, $email) {
    global $hesklang, $hesk_settings;
    
    if (!function_exists('hesk_sendCustomerRegistrationEmail')) {
        require_once(HESK_PATH . 'inc/email_functions.inc.php');
    }

    hesk_purge_email_change_requests($customerUserContext['id']);
    $verification_token = hesk_insert_email_change_request($email, $customerUserContext['id']);

    // HACK: Email function uses the email on the customer object, so swap it out temporarily
    $original_email = $customerUserContext['email'];
    $customerUserContext['email'] = $email;
    hesk_sendCustomerRegistrationEmail($customerUserContext, $verification_token, 'customer_verify_new_email');
    $customerUserContext['email'] = $original_email;
}

function hesk_resend_email_change_notification($customerUserContext) {
    global $hesklang, $hesk_settings;

    if (!$hesk_settings['customer_accounts_allow_email_changes']) {
        hesk_process_messages($hesklang['customer_change_email_disabled'], 'profile.php');
        return;
    }

    $new_email = hesk_get_pending_email_change_for_user($customerUserContext['id']);

    if (is_null($new_email)) {
        hesk_process_messages($hesklang['customer_login_resend_verification_email_none'], 'profile.php');
        return;
    }

    if ($new_email['email_sent_too_recently']) {
        hesk_process_messages($hesklang['customer_login_resend_verification_email_too_early'], 'profile.php');
        return;
    }

    hesk_build_and_send_email($customerUserContext, $new_email['new_email']);
    hesk_process_messages(sprintf($hesklang['customer_change_email_submitted'], $new_email['new_email']), 'profile.php', 'SUCCESS');
}
rate.php
wget 'https://lists2.roe3.org/hesk/rate.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

define('IN_SCRIPT',1);
define('HESK_PATH','./');

// Get all the required files and functions
require(HESK_PATH . 'hesk_settings.inc.php');
require(HESK_PATH . 'inc/common.inc.php');
require(HESK_PATH . 'inc/customer_accounts.inc.php');
hesk_load_database_functions();

// Start a customer session and verify the request token
hesk_session_start('CUSTOMER');
hesk_token_check('GET');

// Is rating enabled?
if ( ! $hesk_settings['rating'])
{
	die($hesklang['rdis']);
}

// Rating value
$rating = intval( hesk_GET('rating', 0) );

// Rating can only be 1 or 5
if ($rating != 1 && $rating != 5)
{
	die($hesklang['attempt']);
}

// Reply ID
$reply_id = intval( hesk_GET('id', 0) ) or die($hesklang['attempt']);

// Ticket tracking ID
$trackingID = hesk_cleanID() or die($hesklang['attempt']);

// Connect to database
hesk_dbConnect();

// Get reply and ticket info to verify they match and enforce customer access
$result = hesk_dbQuery("SELECT `replies`.`replyto`, `replies`.`rating`, `replies`.`staffid`, `tickets`.`trackid`
	FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` AS `replies`
	INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` AS `tickets`
		ON `replies`.`replyto` = `tickets`.`id`
	WHERE `replies`.`id`='{$reply_id}'
	LIMIT 1");

// -> Reply and ticket found?
if (hesk_dbNumRows($result) != 1)
{
	die($hesklang['attempt']);
}

$reply = hesk_dbFetchAssoc($result);

// -> Does the tracking ID match?
if ($reply['trackid'] != $trackingID)
{
	die($hesklang['attempt']);
}

// -> Is this a staff reply?
if (empty($reply['staffid']))
{
	die($hesklang['attempt']);
}

// -> Does the current customer have access to this ticket?
$customers = hesk_get_customers_for_ticket($reply['replyto']);
$customer_emails = array_map(function($customer) { return $customer['email']; }, $customers);

if (hesk_verifyEmailMatch($trackingID, 0, $customer_emails, 0) !== true)
{
	die($hesklang['attempt']);
}

// OK, tracking ID matches. Now check if this reply has already been rated
if ( ! empty($reply['rating']))
{
	die($hesklang['ar']);
}

// Update reply rating
hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` SET `rating`='{$rating}' WHERE `id`='{$reply_id}'");

// Also update staff rating
hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` SET `rating`=((`rating`*(`ratingpos`+`ratingneg`))+{$rating})/(`ratingpos`+`ratingneg`+1), " .
			($rating == 5 ? '`ratingpos`=`ratingpos`+1 ' : '`ratingneg`=`ratingneg`+1 ') .
            "WHERE `id`='{$reply['staffid']}'");

header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");

header('Content-type: text/plain; charset=utf-8');
if ($rating == 5)
{
	echo $hesklang['rh'];
}
else
{
	echo $hesklang['rnh'];
}
exit();
?>
rate_kb.php
wget 'https://lists2.roe3.org/hesk/rate_kb.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

define('IN_SCRIPT',1);
define('HESK_PATH','./');

// Get all the required files and functions
require(HESK_PATH . 'hesk_settings.inc.php');
require(HESK_PATH . 'inc/common.inc.php');
define('TEMPLATE_PATH', HESK_PATH . "theme/{$hesk_settings['site_theme']}/");
require(TEMPLATE_PATH . 'customer/util/rating.php');

// Start a customer session so the KB rating request can be protected by the session CSRF token.
hesk_session_start('CUSTOMER');

// KB rating mutates article vote totals and must be protected even for anonymous customers.
if (!hesk_token_compare(hesk_GET('token'))) {
    die($hesklang['eto']);
}

// Is Knowledgebase enabled?
if ( ! $hesk_settings['kb_enable'])
{
	die($hesklang['kbdis']);
}

// Is rating enabled?
if ( ! $hesk_settings['kb_rating'])
{
	die($hesklang['rdis']);
}

// Rating value
$rating = intval( hesk_GET('rating', 0) );

// Rating can only be 1 or 5
if ($rating != 1 && $rating != 5)
{
	die($hesklang['attempt']);
}

// Article ID
$artid = intval( hesk_GET('id', 0) ) or die($hesklang['attempt']);

// Check cookies for already rated, rate and set cookie if not already
$_COOKIE['hesk_kb_rate'] = hesk_COOKIE('hesk_kb_rate');

// Connect to database
hesk_load_database_functions();
hesk_dbConnect();

// Do we require logged-in customers to view the help desk?
if ($hesk_settings['customer_accounts'] && $hesk_settings['customer_accounts_required'] == 2) {
    require(HESK_PATH . 'inc/customer_accounts.inc.php');
    hesk_isCustomerLoggedIn(true);
}

// Load KB functions so inherited private categories are respected
require(HESK_PATH . 'inc/knowledgebase_functions.inc.php');
$public_category_ids = count($hesk_settings['public_kb_categories_ids']) ? implode(',', $hesk_settings['public_kb_categories_ids']) : '0';

if (strpos($_COOKIE['hesk_kb_rate'],'a'.$artid.'%')===false)
{
    // Update rating, make sure it's a public article in a public category
    hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_articles` AS `t1`
    	LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_categories` AS `t2` ON t1.`catid` = t2.`id`
    	SET `rating`=((`rating`*`votes`)+{$rating})/(`votes`+1), t1.`votes`=t1.`votes`+1
    	WHERE t1.`id`='{$artid}' AND t1.`type`='0' AND t2.`type`='0' AND t2.`id` IN ({$public_category_ids})
    	");
}

// Get article from DB, make sure that article and category are public
$result = hesk_dbQuery("SELECT t1.`rating`, t1.`votes`
            FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_articles` AS `t1`
            LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_categories` AS `t2` ON `t1`.`catid` = `t2`.`id`
            WHERE `t1`.`id` = '{$artid}' AND `t1`.`type` = '0' AND `t2`.`type` = '0' AND `t2`.`id` IN ({$public_category_ids})
          ");

$article = hesk_dbFetchAssoc($result) or die($hesklang['kb_art_id']);

hesk_setcookie('hesk_kb_rate', $_COOKIE['hesk_kb_rate'].'a'.$artid.'%', time()+2592000);
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header('Content-type: text/plain; charset=utf-8');

echo hesk3_get_customer_rating($article['rating']);
if ($hesk_settings['kb_views'])
{
    echo ' <span class="lightgrey">('.number_format($article['votes'], 0, null, $hesklang['sep_1000']).')</span>';
}

exit();
readme.html
wget 'https://lists2.roe3.org/hesk/readme.html'
View Content
<html>
<head>
<meta http-equiv="Refresh" content="0; url=docs/index.html" />
</head>
<body>
<p>Documentation has moved here: <a href="docs/index.html">HESK Documentation</a>.</p>
</body>
</html>
register.php
wget 'https://lists2.roe3.org/hesk/register.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

use RobThree\Auth\TwoFactorAuth;

define('IN_SCRIPT',1);
define('HESK_PATH','./');
define('HESK_NO_ROBOTS',1);

/* Get all the required files and functions */
require(HESK_PATH . 'hesk_settings.inc.php');
define('TEMPLATE_PATH', HESK_PATH . "theme/{$hesk_settings['site_theme']}/");
require(HESK_PATH . 'inc/common.inc.php');
require(HESK_PATH . 'inc/customer_accounts.inc.php');

// Are we in maintenance mode?
hesk_check_maintenance();

hesk_load_database_functions();
hesk_session_start('CUSTOMER');

hesk_dbConnect();

// Are customer accounts enabled? If not, redirect
if (!$hesk_settings['customer_accounts']) {
    return hesk_process_messages($hesklang['customer_accounts_disabled'], 'index.php');
}

// Are customers allowed to create their own accounts? If not, redirect
if (!$hesk_settings['customer_accounts_customer_self_register']) {
    return hesk_process_messages($hesklang['customer_accounts_registration_disabled'], 'index.php');
}

// Is the user already logged in? If so, they shouldn't be needing this page at all.
if (hesk_isCustomerLoggedIn(false) !== null) {
    header('Location: ' . $hesk_settings['hesk_url'] . '/index.php');
    exit();
}

// Tell header to load reCaptcha API if needed
if ($hesk_settings['recaptcha_use'])
{
    define('RECAPTCHA',1);
}

// Are we creating a new account?
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // Demo mode
    if ( defined('HESK_DEMO') ) {
        hesk_process_messages($hesklang['ddemo'], 'register.php', 'NOTICE');
    }

    handle_registration();

    // If we've made it past this point, registration was successful
    hesk_process_messages($hesklang['customer_registration_successful'], 'NOREDIRECT', 'SUCCESS');
    $hesk_settings['render_template'](TEMPLATE_PATH . 'customer/account/register-success.php', [
        'messages' => hesk_get_messages(),
        'serviceMessages' => hesk_get_service_messages('c-ok', $hesk_settings['customer_accounts_required'] == 2),
        'model' => hesk_SESSION_array('userdata')
    ]);
    unset($_SESSION['iserror']);
    unset($_SESSION['userdata']);
    return;
}

$hesk_settings['render_template'](TEMPLATE_PATH . 'customer/account/register.php', [
    'validationFailures' => hesk_SESSION_array('iserror'),
    'messages' => hesk_get_messages(),
    'serviceMessages' => hesk_get_service_messages('c-register', $hesk_settings['customer_accounts_required'] == 2),
    'model' => hesk_SESSION_array('userdata')
]);
unset($_SESSION['iserror']);
unset($_SESSION['userdata']);

function handle_registration() {
    global $hesk_settings, $hesklang;

    $hesk_error_buffer = array();

    // Check anti-SPAM question
    if ($hesk_settings['question_use'])
    {
        $question = hesk_input( hesk_POST('question') );

        if ( strlen($question) == 0)
        {
            $hesk_error_buffer['question'] = $hesklang['q_miss'];
        }
        elseif (hesk_mb_strtolower($question) != hesk_mb_strtolower($hesk_settings['question_ans']))
        {
            $hesk_error_buffer['question'] = $hesklang['q_wrng'];
        }
        else
        {
            $_SESSION['c_question'] = $question;
        }
    }

    // Check anti-SPAM image
    if ($hesk_settings['secimg_use'] && ! isset($_SESSION['img_verified'])) {
        // Using reCAPTCHA?
        if ($hesk_settings['recaptcha_use']) {
            require(HESK_PATH . 'inc/recaptcha/recaptchalib_v2.php');

            $resp = null;
            $reCaptcha = new ReCaptcha($hesk_settings['recaptcha_private_key']);

            // Was there a reCAPTCHA response?
            if (isset($_POST["g-recaptcha-response"])) {
                $resp = $reCaptcha->verifyResponse(hesk_getClientIP(), hesk_POST("g-recaptcha-response"));
            }

            if ($resp != null && $resp->success) {
                $_SESSION['img_verified']=true;
            } else {
                $hesk_error_buffer['mysecnum']=$hesklang['recaptcha_error'];
            }
        } else {
            // Using PHP generated image
            $mysecnum = intval(hesk_POST('mysecnum', 0));

            if (empty($mysecnum)) {
                $hesk_error_buffer['mysecnum']=$hesklang['sec_miss'];
            } else {
                require(HESK_PATH . 'inc/secimg.inc.php');
                $sc = new PJ_SecurityImage($hesk_settings['secimg_sum']);
                if (isset($_SESSION['checksum']) && $sc->checkCode($mysecnum, $_SESSION['checksum'])) {
                    $_SESSION['img_verified']=true;
                    unset($_SESSION['checksum']);
                } else {
                    $hesk_error_buffer['mysecnum']=$hesklang['sec_wrng'];
                }
            }
        }
    }

    $name = hesk_input(hesk_POST('name'));
    if ($name) {
        $myuser['name'] = $name;
    } else {
        $hesk_error_buffer['name'] = $hesklang['enter_real_name'];
    }

    $email = hesk_validateEmail(hesk_POST('email'), 'ERR', 0);
    if ($email) {
        $myuser['email'] = $email;
    } else {
        $hesk_error_buffer['email'] = $hesklang['enter_valid_email'];
    }

    // Are we banned?
    if (hesk_isBannedEmail($email) || hesk_isBannedIP(hesk_getClientIP())) {
        // Don't bother validating the rest at this point
        hesk_process_messages($hesklang['customer_accounts_email_banned'], 'register.php');
        return;
    }

    $password = hesk_input(hesk_POST('password'));
    $password_length = strlen($password);

    if ($password_length < 5) {
        $hesk_error_buffer['password'] = $hesklang['password_not_valid'];
    } else {
        // TODO Should we care about passwords being "too long"?
        $confirm_password = hesk_input(hesk_POST('confirm-password'));
        if ($password !== $confirm_password) {
            $hesk_error_buffer['password'] = $hesklang['passwords_not_same'];
        }
    }

    // Do we have an existing customer record (could be either a duplicate, or someone who lost their verification email
    $existing_account = null;
    if (isset($myuser['email'])) {
        $existing_account = hesk_get_customer_account_by_email($myuser['email'], true);
        if ($existing_account !== null) {
            if ($existing_account['verified'] == 0) {
                $hesk_error_buffer['email'] = sprintf($hesklang['customer_registration_email_exists_pending_email_verification'], $myuser['email']);
            } elseif ($existing_account['verified'] == 1) {
                $hesk_error_buffer['email'] = sprintf($hesklang['customer_registration_email_exists'], $myuser['email']);
            } else {
                $hesk_error_buffer['email'] = sprintf($hesklang['customer_registration_email_exists_pending_approval'], $myuser['email']);
            }
        }
    }

    $_SESSION['userdata'] = $myuser;

    // Validation errors?
    if (count($hesk_error_buffer)) {
        $_SESSION['iserror'] = array_keys($hesk_error_buffer);

        $tmp = '';
        foreach ($hesk_error_buffer as $error) {
            $tmp .= "<li>$error</li>\n";
        }

        $hesk_error_buffer = $hesklang['pcer'] . '<br /><br /><ul>' . $tmp . '</ul>';
        hesk_process_messages($hesk_error_buffer, 'register.php');
    }

    // Generate a verification token for this user
    $verification_token = bin2hex(random_bytes(16));
    $hashed_password = hesk_password_hash($password);

    $myuser['language'] = HESK_DEFAULT_LANGUAGE;
    if ($hesk_settings['can_sel_lang']) {
        $myuser['language'] = $hesklang['LANGUAGE'];
    }

    if ($existing_account === null) {
        hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` (`pass`, `name`, 
            `email`, `language`, `verified`, `verification_token`, `verification_email_sent_at`) VALUES (
                '".hesk_dbEscape($hashed_password)."',
                '".hesk_dbEscape($myuser['name'])."',
                '".hesk_dbEscape($myuser['email'])."',
                '".hesk_dbEscape($myuser['language'])."',
                0,
                '".hesk_dbEscape($verification_token)."',
                NOW()
            )");
    }
    unset($_SESSION['img_verified']);

    // Send verification email to the user
    require_once(HESK_PATH . 'inc/email_functions.inc.php');
    hesk_sendCustomerRegistrationEmail($myuser, $verification_token);
}
reply_ticket.php
wget 'https://lists2.roe3.org/hesk/reply_ticket.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

define('IN_SCRIPT',1);
define('HESK_PATH','./');

/* Get all the required files and functions */
require(HESK_PATH . 'hesk_settings.inc.php');
require(HESK_PATH . 'inc/common.inc.php');

// Are we in maintenance mode?
hesk_check_maintenance();

hesk_load_database_functions();
require(HESK_PATH . 'inc/email_functions.inc.php');
require(HESK_PATH . 'inc/posting_functions.inc.php');
require_once(HESK_PATH . 'inc/customer_accounts.inc.php');

// We only allow POST requests to this file
if ( $_SERVER['REQUEST_METHOD'] != 'POST' )
{
	header('Location: index.php');
	exit();
}

// Check for POST requests larger than what the server can handle
if ( empty($_POST) && ! empty($_SERVER['CONTENT_LENGTH']) )
{
	hesk_error($hesklang['maxpost']);
}

hesk_session_start('CUSTOMER');
hesk_token_check('POST');

// Prevent flooding - multiple replies within a few seconds are probably not valid
if ($hesk_settings['flood'])
{
    if (isset($_SESSION['last_reply_timestamp']) && (time() - $_SESSION['last_reply_timestamp']) < $hesk_settings['flood'])
    {
        hesk_error($hesklang['e_flood']);
    }
    else
    {
        $_SESSION['last_reply_timestamp'] = time();
    }
}

$hesk_error_buffer = array();

// Tracking ID
$trackingID  = hesk_cleanID('orig_track') or die($hesklang['int_error'].': No orig_track');

// Email required to view ticket?
if (hesk_isCustomerLoggedIn(false)) {
    $my_email = $_SESSION['customer']['email'];
} else {
    $my_email = hesk_getCustomerEmail();
}

// Setup required session vars
$_SESSION['t_track'] = $trackingID;
$_SESSION['t_email'] = $my_email;

// Get message
$message = hesk_input( hesk_POST('message') );

// If the message was entered, further parse it
if ( strlen($message) )
{
	// Make links clickable
	$message = hesk_makeURL($message);

	// Turn newlines into <br />
	$message = nl2br($message);
}
else
{
	$hesk_error_buffer[] = $hesklang['enter_message'];
}

/* Connect to database */
hesk_dbConnect();

// Load statuses
require_once(HESK_PATH . 'inc/statuses.inc.php');

// Check if this IP is temporarily locked out
$res = hesk_dbQuery("SELECT `number` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."logins` WHERE `ip`='".hesk_dbEscape(hesk_getClientIP())."' AND `last_attempt` IS NOT NULL AND DATE_ADD(`last_attempt`, INTERVAL ".intval($hesk_settings['attempt_banmin'])." MINUTE ) > NOW() LIMIT 1");
if (hesk_dbNumRows($res) == 1)
{
	if (hesk_dbResult($res) >= $hesk_settings['attempt_limit'])
	{
		unset($_SESSION);
		hesk_error( sprintf($hesklang['yhbb'],$hesk_settings['attempt_banmin']) , 0);
	}
}

/* Get details about the original ticket */
$res = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` AS `ticket` WHERE `trackid`='{$trackingID}' LIMIT 1");
if (hesk_dbNumRows($res) != 1)
{
	hesk_error($hesklang['ticket_not_found']);
}
$ticket = hesk_dbFetchAssoc($res);
$customers = hesk_get_customers_for_ticket($ticket['id']);
$customer_emails = array_map(function($customer) { return $customer['email']; }, $customers);

/* If we require e-mail to view tickets check if it matches the one in database */
if (hesk_verifyEmailMatch($trackingID, $my_email, $customer_emails) !== true)
{
	hesk_error($hesklang['attempt']);
}

/* Ticket locked? */
if ($ticket['locked'])
{
	hesk_process_messages($hesklang['tislock2'],'ticket.php');
	exit();
}

/*
 * Closed tickets usually don't show a reply form. However, the customer may have
 * loaded an open ticket, started typing, and then staff resolved it before the
 * reply was submitted. If customers are allowed to reopen tickets and this
 * ticket isn't locked, accept the in-flight reply and reopen it implicitly.
 */
$reopening_closed_ticket = false;
if ($ticket['status'] == 3)
{
	if ( ! $hesk_settings['custopen'])
	{
		hesk_process_messages($hesklang['scno'],'ticket.php');
		exit();
	}

	$reopening_closed_ticket = true;
}

/* Attachments */
$use_legacy_attachments = hesk_POST('use-legacy-attachments', 0);
$attachments = array();
if ($hesk_settings['attachments']['use'])
{
    require(HESK_PATH . 'inc/attachments.inc.php');

    if ( ! $use_legacy_attachments)
    {
        // The user used the new drag-and-drop system. These files were already
        // uploaded to temporary storage and are identified by signed keys.
        $temp_attachment_names = hesk_POST_array('attachments');
        foreach ($temp_attachment_names as $temp_attachment_name)
        {
            $temp_attachment = hesk_getTemporaryAttachment($temp_attachment_name);

            if ($temp_attachment !== null)
            {
                $attachments[] = $temp_attachment;
            }
        }
    }
}
$myattachments='';

/* Any errors? */
if (count($hesk_error_buffer)!=0)
{
    $_SESSION['ticket_message'] = hesk_POST('message');

    // If this was a reply after re-opening a ticket, force the form at the top
    if ( hesk_POST('reopen') == 1)
    {
        $_SESSION['force_form_top'] = true;
    }

    // Remove any successfully uploaded attachments
    if ($hesk_settings['attachments']['use'])
    {
        if ($use_legacy_attachments)
        {
            hesk_removeAttachments($attachments);
        }
        else
        {
            $_SESSION['r_attachments'] = $attachments;
        }
    }

    $tmp = '';
    foreach ($hesk_error_buffer as $error)
    {
        $tmp .= "<li>$error</li>\n";
    }
    $hesk_error_buffer = $tmp;

    $hesk_error_buffer = $hesklang['pcer'].'<br /><br /><ul>'.$hesk_error_buffer.'</ul>';
    hesk_process_messages($hesk_error_buffer,'ticket.php');
}

// Prevent flooding ticket replies
$res = hesk_dbQuery("SELECT `staffid` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` WHERE `replyto`='{$ticket['id']}' AND `dt` > DATE_SUB(NOW(), INTERVAL 10 MINUTE) ORDER BY `id` ASC");
if (hesk_dbNumRows($res) > 0)
{
	$sequential_customer_replies = 0;
	while ($tmp = hesk_dbFetchAssoc($res))
	{
		$sequential_customer_replies = $tmp['staffid'] ? 0 : $sequential_customer_replies + 1;
	}
	if ($sequential_customer_replies > 10)
	{
		hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."logins` (`ip`, `number`) VALUES ('".hesk_dbEscape(hesk_getClientIP())."', ".intval($hesk_settings['attempt_limit'] + 1).")");
		hesk_error( sprintf($hesklang['yhbr'],$hesk_settings['attempt_banmin']) , 0);
	}
}

/* Process legacy attachments only after the ticket and customer have been authorized. */
if ($hesk_settings['attachments']['use'] && $use_legacy_attachments)
{
    for ($i = 1; $i <= $hesk_settings['attachments']['max_number']; $i++)
    {
        $att = hesk_uploadFile($i);
        if ($att !== false && !empty($att))
        {
            $attachments[$i] = $att;
        }
    }

    if (count($hesk_error_buffer)!=0)
    {
        $_SESSION['ticket_message'] = hesk_POST('message');

        // If this was a reply after re-opening a ticket, force the form at the top
        if ( hesk_POST('reopen') == 1)
        {
            $_SESSION['force_form_top'] = true;
        }

        // Remove any successfully uploaded attachments
        hesk_removeAttachments($attachments);

        $tmp = '';
        foreach ($hesk_error_buffer as $error)
        {
            $tmp .= "<li>$error</li>\n";
        }
        $hesk_error_buffer = $tmp;

        $hesk_error_buffer = $hesklang['pcer'].'<br /><br /><ul>'.$hesk_error_buffer.'</ul>';
        hesk_process_messages($hesk_error_buffer,'ticket.php');
    }
}

/* Insert attachments */
if ($hesk_settings['attachments']['use'] && !empty($attachments))
{
    // Delete temp attachment records and set the new filename
    if (!$use_legacy_attachments) {
        $attachments = hesk_migrateTempAttachments($attachments, $trackingID);
    }

    foreach ($attachments as $myatt)
    {
        hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."attachments` (`ticket_id`,`saved_name`,`real_name`,`size`) VALUES ('{$trackingID}','".hesk_dbEscape($myatt['saved_name'])."','".hesk_dbEscape($myatt['real_name'])."','".intval($myatt['size'])."')");
        $myattachments .= hesk_dbInsertID() . '#' . $myatt['real_name'] .',';
    }
}

// If staff hasn't replied yet, keep ticket status "New", otherwise set it to "Waiting reply from staff"
$closedby_sql = '';
if (hesk_can_customer_change_status($ticket['status']))
{
    $ticket['status'] = $ticket['status'] ? 1 : 0;
}
if ($reopening_closed_ticket)
{
    $closedby_sql = ' , `closedat`=NULL, `closedby`=NULL ';
}

/* Update ticket as necessary */
$res = hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` SET `lastchange`=NOW(), `status`='{$ticket['status']}', `replies`=`replies`+1, `lastreplier`='0' {$closedby_sql} WHERE `id`='{$ticket['id']}'");

// Insert reply into database
$my_customer = null;
foreach ($customers as $customer) {
	if ($customer['email'] === $my_email) {
		$my_customer = $customer;
		break;
	}
}

if (empty($my_customer)) {
    foreach ($customers as $customer) {
        if ($customer['customer_type'] == 'REQUESTER') {
            $my_customer = $customer;
            break;
        }
    }
}

hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` (`replyto`,`message`,`message_html`,`dt`,`attachments`, `customer_id`) VALUES ({$ticket['id']},'".hesk_dbEscape($message)."','".hesk_dbEscape($message)."',NOW(),'".hesk_dbEscape($myattachments)."', ".intval($my_customer['id']).")");


/*** Need to notify any staff? ***/

// --> Prepare reply message

// 1. Generate the array with ticket info that can be used in emails
$combined_emails = implode(';', $customer_emails);
$customer_names = array_map(function($customer) { return $customer['name']; }, $customers);
$combined_names = implode(',', $customer_names);

$info = array(
'email'			=> $combined_emails,
'category'		=> $ticket['category'],
'priority'		=> $ticket['priority'],
'owner'			=> $ticket['owner'],
'collaborators' => hesk_getTicketsCollaboratorIDs($ticket['id']),
'trackid'		=> $ticket['trackid'],
'status'		=> $ticket['status'],
'name'			=> $combined_names,
'subject'		=> $ticket['subject'],
'message'		=> stripslashes($message),
'attachments'	=> $myattachments,
'dt'			=> hesk_date($ticket['dt'], true),
'lastchange'	=> hesk_date(),
'due_date'      => hesk_format_due_date($ticket['due_date']),
'id'			=> $ticket['id'],
'time_worked'   => $ticket['time_worked'],
'last_reply_by' => $my_customer['name'],
);

// 2. Add custom fields to the array
foreach ($hesk_settings['custom_fields'] as $k => $v)
{
	$info[$k] = $v['use'] ? $ticket[$k] : '';
}

// 3. Add HTML message to the array
$info['message_html'] = $info['message'];

// 4. Make sure all values are properly formatted for email
$ticket = hesk_ticketToPlain($info, 1, 0);

// --> If ticket is assigned, notify the owner plus collaborators
if ($ticket['owner']) {
    hesk_notifyAssignedStaff(false, 'new_reply_by_customer', 'notify_reply_my', 'notify_collaborator_customer_reply');
}
// --> No owner assigned, find and notify appropriate staff, including collaborators
elseif ($ticket['collaborators']) {
    hesk_notifyStaff('new_reply_by_customer',"`notify_reply_unassigned`='1' OR (`notify_collaborator_customer_reply`='1' AND `id` IN (".implode(",", $ticket['collaborators'])."))");
}
// --> No owner assigned, find and notify appropriate staff, no collaborators
else {
    hesk_notifyStaff('new_reply_by_customer',"`notify_reply_unassigned`='1'");
}

/* Clear unneeded session variables */
hesk_cleanSessionVars('ticket_message');
hesk_cleanSessionVars('r_attachments');

/* Show the ticket and the success message */
hesk_process_messages($hesklang['reply_submitted_success'],'ticket.php','SUCCESS');
exit();
reset_password.php
wget 'https://lists2.roe3.org/hesk/reset_password.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

define('IN_SCRIPT',1);
define('HESK_PATH','./');
define('HESK_NO_ROBOTS',1);

/* Get all the required files and functions */
require(HESK_PATH . 'hesk_settings.inc.php');
define('TEMPLATE_PATH', HESK_PATH . "theme/{$hesk_settings['site_theme']}/");
require(HESK_PATH . 'inc/common.inc.php');
require(HESK_PATH . 'inc/customer_accounts.inc.php');

// Are we in maintenance mode?
hesk_check_maintenance();

hesk_load_database_functions();
hesk_session_start('CUSTOMER');

hesk_dbConnect();

// Is the user already logged in? If so, they shouldn't be needing this page at all.
if (hesk_isCustomerLoggedIn(false) !== null) {
    header('Location: ' . $hesk_settings['hesk_url'] . '/index.php');
    exit();
}

// Tell header to load reCaptcha API if needed
if ($hesk_settings['recaptcha_use'])
{
    define('RECAPTCHA',1);
}

// Are we setting a new password?
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    handle_new_password();

    // If we've made it past this point, password reset was successful
    unset($_SESSION['a_iserror']);
    unset($_SESSION['userdata']);
    hesk_process_messages($hesklang['customer_password_reset_successful'], 'login.php', 'SUCCESS');
}

if ( ! isset($_SESSION['a_iserror'])) {
    $_SESSION['a_iserror'] = array();
}

// Verify the provided hash.  Don't even provide a reset form if the hash is invalid.
$hash = hesk_REQUEST('hash', '');
$hash_valid = hesk_verify_customer_password_reset_hash($hash);
if (!$hash_valid['success']) {
    hesk_process_messages($hash_valid['content'], 'NOREDIRECT');
}

$hesk_settings['render_template'](TEMPLATE_PATH . 'customer/account/reset-password.php', [
    'validationFailures' => hesk_SESSION_array('a_iserror'),
    'messages' => hesk_get_messages(),
    'validHash' => $hash_valid['success'],
    'resetPasswordHash' => $hash
]);
unset($_SESSION['a_iserror']);
unset($_SESSION['userdata']);

function handle_new_password() {
    global $hesk_settings, $hesklang;

    $hesk_error_buffer = [];

    // Check anti-SPAM question
    if ($hesk_settings['question_use'])
    {
        $question = hesk_input( hesk_POST('question') );

        if ( strlen($question) == 0)
        {
            $hesk_error_buffer[] = $hesklang['q_miss'];
        }
        elseif (hesk_mb_strtolower($question) != hesk_mb_strtolower($hesk_settings['question_ans']))
        {
            $hesk_error_buffer[] = $hesklang['q_wrng'];
        }
        else
        {
            $_SESSION['c_question'] = $question;
        }
    }

    // Check anti-SPAM image
    if ($hesk_settings['secimg_use'] && !isset($_SESSION['img_verified'])) {
        // Using reCAPTCHA?
        if ($hesk_settings['recaptcha_use']) {
            require(HESK_PATH . 'inc/recaptcha/recaptchalib_v2.php');

            $resp = null;
            $reCaptcha = new ReCaptcha($hesk_settings['recaptcha_private_key']);

            // Was there a reCAPTCHA response?
            if (isset($_POST["g-recaptcha-response"])) {
                $resp = $reCaptcha->verifyResponse(hesk_getClientIP(), hesk_POST("g-recaptcha-response"));
            }

            if ($resp != null && $resp->success) {
                $_SESSION['img_verified']=true;
            } else {
                $hesk_error_buffer[]=$hesklang['recaptcha_error'];
            }
        } else {
            // Using PHP generated image
            $mysecnum = intval(hesk_POST('mysecnum', 0));

            if (empty($mysecnum)) {
                $hesk_error_buffer[]=$hesklang['sec_miss'];
            } else {
                require(HESK_PATH . 'inc/secimg.inc.php');
                $sc = new PJ_SecurityImage($hesk_settings['secimg_sum']);
                if (isset($_SESSION['checksum']) && $sc->checkCode($mysecnum, $_SESSION['checksum'])) {
                    $_SESSION['img_verified']=true;
                    unset($_SESSION['checksum']);
                } else {
                    $hesk_error_buffer[]=$hesklang['sec_wrng'];
                }
            }
        }
    }

    $password = hesk_input(hesk_POST('password'));
    $password_length = strlen($password);

    if ($password_length < 5) {
        $hesk_error_buffer['password'] = $hesklang['password_not_valid'];
    } else {
        // TODO Should we care about passwords being "too long"?
        $confirm_password = hesk_input(hesk_POST('confirm-password'));
        if ($password !== $confirm_password) {
            $hesk_error_buffer['password'] = $hesklang['passwords_not_same'];
        }
    }

    $hash = hesk_input(hesk_POST('hash'));
    $hash_valid = hesk_verify_customer_password_reset_hash($hash);
    $customer_id = 0;
    if (!$hash_valid['success']) {
        $hesk_error_buffer['hash'] = $hash_valid['content'];
    } else {
        $customer_id = intval($hash_valid['content']);

        // Is this password the same as their old one?
        $existing_passsword_rs = hesk_dbQuery("SELECT `pass` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers`
                WHERE `id` = ".$customer_id);
        $existing_password = hesk_dbFetchAssoc($existing_passsword_rs);
        if (hesk_password_verify($password, $existing_password['pass'])) {
            $hesk_error_buffer['password'] = $hesklang['customer_edit_pass_same'];
        }
    }

    // Validation errors?
    if (count($hesk_error_buffer)) {
        $_SESSION['a_iserror'] = array_keys($hesk_error_buffer);
        $tmp = '';
        foreach ($hesk_error_buffer as $error)
        {
            $tmp .= "<li>$error</li>\n";
        }
        $hesk_error_buffer = $tmp;
        $hesk_buffer_string = $hesklang['pcer'].'<br /><br /><ul>'.$hesk_error_buffer.'</ul>';
        hesk_process_messages($hesk_buffer_string, 'reset_password.php?hash='.$hash);
        return;
    }
    unset($_SESSION['img_verified']);
    $hashed_password = hesk_password_hash($password);

    // Update their password
    hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."customers`
            SET `pass` = '".hesk_dbEscape($hashed_password)."',
                `verification_token` = NULL,
                `verified` = 1
            WHERE `id` = ".$customer_id);

    // Purge existing reset hashes, remember-me tokens and pending MFA verification codes
    hesk_verify_customer_password_reset_hash($hash, true);
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."auth_tokens` WHERE `user_id` = ".$customer_id." AND `user_type` = 'CUSTOMER'");
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."mfa_verification_tokens` WHERE `user_id` = ".$customer_id." AND `user_type` = 'CUSTOMER'");
}
robots.txt
wget 'https://lists2.roe3.org/hesk/robots.txt'
View Content
User-agent: *
Disallow: /admin/
Disallow: /attachments/
Disallow: /inc/
Disallow: /language/
Disallow: /change_status.php
Disallow: /download_attachment.php
Disallow: /print.php
Disallow: /rate.php
Disallow: /submit_ticket.php
Disallow: /suggest_articles.php
Disallow: /suggest_email.php
Disallow: /ticket.php
submit_ticket.php
wget 'https://lists2.roe3.org/hesk/submit_ticket.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

define('IN_SCRIPT',1);
define('HESK_PATH','./');

// Get all the required files and functions
require(HESK_PATH . 'hesk_settings.inc.php');
define('TEMPLATE_PATH', HESK_PATH . "theme/{$hesk_settings['site_theme']}/");
require(HESK_PATH . 'inc/common.inc.php');
require_once(HESK_PATH . 'inc/customer_accounts.inc.php');

// Are we in maintenance mode?
hesk_check_maintenance();

// Are we in "Knowledgebase only" mode?
hesk_check_kb_only();

hesk_load_database_functions();
require(HESK_PATH . 'inc/email_functions.inc.php');
require(HESK_PATH . 'inc/posting_functions.inc.php');

// We only allow POST requests to this file
if ( $_SERVER['REQUEST_METHOD'] != 'POST' )
{
	header('Location: index.php?a=add');
	exit();
}

// Check for POST requests larger than what the server can handle
if ( empty($_POST) && ! empty($_SERVER['CONTENT_LENGTH']) )
{
	hesk_error($hesklang['maxpost']);
}

// Try to detect some simple SPAM bots
if ( ! isset($_POST['hx']) || $_POST['hx'] != 3 || ! isset($_POST['hy']) || $_POST['hy'] != '' || isset($_POST['phone']) )
{
	header('HTTP/1.1 403 Forbidden');
	exit();
}

// Block obvious spammers trying to inject email headers
if ( preg_match("/\n|\r|\t|%0A|%0D|%08|%09/", hesk_POST('name') . hesk_POST('subject') ) )
{
	header('HTTP/1.1 403 Forbidden');
    exit();
}

hesk_session_start('CUSTOMER');

// A security check - not needed here, but uncomment if you require it
hesk_token_check();

// Prevent submitting multiple tickets by reloading submit_ticket.php page
if (isset($_SESSION['already_submitted']))
{
	hesk_forceStop();
}

// Connect to database
hesk_dbConnect();

// Do we require logged-in customers to view the help desk?
$customer = hesk_isCustomerLoggedIn($hesk_settings['customer_accounts'] && $hesk_settings['customer_accounts_required']);

// Fix a bug before we find a better way to do it :)
if ($customer === true) {
    $customer = hesk_isCustomerLoggedIn();
}

$hesk_error_buffer = array();

// Check anti-SPAM question
if ($hesk_settings['question_use'])
{
	$question = hesk_input( hesk_POST('question') );

	if ( strlen($question) == 0)
	{
		$hesk_error_buffer['question'] = $hesklang['q_miss'];
	}
	elseif (hesk_mb_strtolower($question) != hesk_mb_strtolower($hesk_settings['question_ans']))
	{
		$hesk_error_buffer['question'] = $hesklang['q_wrng'];
	}
	else
	{
		$_SESSION['c_question'] = $question;
	}
}

// Check anti-SPAM image
if ($hesk_settings['secimg_use'] && ! isset($_SESSION['img_verified']))
{
	// Using reCAPTCHA?
	if ($hesk_settings['recaptcha_use'])
	{
		require(HESK_PATH . 'inc/recaptcha/recaptchalib_v2.php');

		$resp = null;
		$reCaptcha = new ReCaptcha($hesk_settings['recaptcha_private_key']);

		// Was there a reCAPTCHA response?
		if ( isset($_POST["g-recaptcha-response"]) )
		{
			$resp = $reCaptcha->verifyResponse(hesk_getClientIP(), hesk_POST("g-recaptcha-response") );
		}

		if ($resp != null && $resp->success)
		{
			$_SESSION['img_verified']=true;
		}
		else
		{
			$hesk_error_buffer['mysecnum']=$hesklang['recaptcha_error'];
		}
	}
	// Using PHP generated image
	else
	{
		$mysecnum = intval( hesk_POST('mysecnum', 0) );

		if ( empty($mysecnum) )
		{
			$hesk_error_buffer['mysecnum']=$hesklang['sec_miss'];
		}
		else
		{
			require(HESK_PATH . 'inc/secimg.inc.php');
			$sc = new PJ_SecurityImage($hesk_settings['secimg_sum']);
			if ( isset($_SESSION['checksum']) && $sc->checkCode($mysecnum, $_SESSION['checksum']) )
			{
				$_SESSION['img_verified']=true;
                unset($_SESSION['checksum']);
			}
			else
			{
				$hesk_error_buffer['mysecnum']=$hesklang['sec_wrng'];
			}
		}
	}
}

if (! isset($customer)) {
    $customer = hesk_isCustomerLoggedIn(false);
}
$email_available = true;
if ($customer) {
    $tmpvar['name'] = hesk_addslashes($customer['name']);
    $tmpvar['email'] = hesk_addslashes($customer['email']);
    $tmpvar['customer_id'] = intval($customer['id']);
} else {
    $tmpvar['name']	 = hesk_input( hesk_POST('name') ) or $hesk_error_buffer['name']=$hesklang['enter_your_name'];

    if ($hesk_settings['require_email'])
    {
        $tmpvar['email'] = hesk_validateEmail( hesk_POST('email'), 'ERR', 0) or $hesk_error_buffer['email']=$hesklang['enter_valid_email'];
    }
    else
    {
        $tmpvar['email'] = hesk_validateEmail( hesk_POST('email'), 'ERR', 0);

        // Not required, but must be valid if it is entered
        if ($tmpvar['email'] == '')
        {
            $email_available = false;

            if (strlen(hesk_POST('email')))
            {
                $hesk_error_buffer['email'] = $hesklang['not_valid_email'];
            }

            // No need to confirm the email
            $hesk_settings['confirm_email'] = 0;
            $_POST['email2'] = '';
            $_SESSION['c_email'] = '';
            $_SESSION['c_email2'] = '';
        }
    }

    if ($tmpvar['email'] !== '' && $hesk_settings['customer_accounts'] > 0) {
        // Ensure that no one is registered under this email
        $customer_exists = hesk_dbQuery("SELECT 1 FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers`
            WHERE `email` = '".hesk_dbEscape($tmpvar['email'])."' AND `verified` > 0");
        if (hesk_dbNumRows($customer_exists) > 0) {
            $hesk_error_buffer['email'] = sprintf($hesklang['submit_ticket_customer_email_exists'], urlencode($tmpvar['email']));
        }
    }

    if ($hesk_settings['confirm_email'])
    {
        $tmpvar['email2'] = hesk_validateEmail( hesk_POST('email2'), 'ERR', 0) or $hesk_error_buffer['email2']=$hesklang['confemail2'];

        // Anything entered as email confirmation?
        if ($tmpvar['email2'] != '')
        {
            // Do we have multiple emails?
            if ($hesk_settings['multi_eml'] && count( array_diff( explode(',', strtolower($tmpvar['email']) ), explode(',', strtolower($tmpvar['email2']) ) ) ) == 0)
            {
                $_SESSION['c_email2'] =  hesk_POST('email2');
            }
            // Single email address match
            elseif ( ! $hesk_settings['multi_eml'] && strtolower($tmpvar['email']) == strtolower($tmpvar['email2']) )
            {
                $_SESSION['c_email2'] =  hesk_POST('email2');
            }
            else
            {
                // Invalid match
                $tmpvar['email2'] = '';
                $_POST['email2'] = '';
                $_SESSION['c_email2'] = '';
                $_SESSION['isnotice'][] = 'email';
                $hesk_error_buffer['email2']=$hesklang['confemaile'];
            }
        }
        else
        {
            $_SESSION['c_email2'] =  hesk_POST('email2');
        }
    }
}

$tmpvar['followers'] = $hesk_settings['multi_eml'] ? hesk_validateFollowers(hesk_POST('follower_email')) : [];
$tmpvar['category'] = intval( hesk_POST('category') ) or $hesk_error_buffer['category']=$hesklang['sel_app_cat'];

// Verify followers, remove duplicates
if ($hesk_settings['multi_eml']) {
    // Make sure Requester is not also among Followers
    foreach (array_keys($tmpvar['followers'], strtolower($tmpvar['email']), true) as $key) {
        unset($tmpvar['followers'][$key]);
    }
    $tmpvar['followers'] = array_values($tmpvar['followers']);
}

// Do we have a default due date?
$default_due_date_info = hesk_getCategoryDueDateInfo($tmpvar['category']);
if ($default_due_date_info !== null) {
    $current_date = new DateTime('today midnight');
    $current_date->add(DateInterval::createFromDateString("+{$default_due_date_info['amount']} {$default_due_date_info['unit']}s"));
    $tmpvar['due_date'] = hesk_datepicker_format_date($current_date->getTimestamp());
}

// Do we allow customer to select priority?
if ($hesk_settings['cust_urgency'])
{
	$tmpvar['priority'] = hesk_POST('priority');
}
// Priority will be selected based on the category selected
else
{
	$res = hesk_dbQuery("SELECT `priority` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` WHERE `id`=".intval($tmpvar['category']));
	if ( hesk_dbNumRows($res) == 1 )
	{
		$tmpvar['priority'] = intval( hesk_dbResult($res) );
	}
	else
	{
		$tmpvar['priority'] = 3;
	}
}

// Make sure customers can select this category
if ( ! isset($hesk_settings['priorities'][$tmpvar['priority']]) || ! $hesk_settings['priorities'][$tmpvar['priority']]['can_customers_select']) {
    foreach ($hesk_settings['priorities'] as $k => $v) {
        if ($v['can_customers_select']) {
            $tmpvar['priority'] = $k;
            break;
        }
    }
}

if ($hesk_settings['require_subject'] == -1)
{
    $tmpvar['subject'] = '';
}
else
{
    $tmpvar['subject'] = hesk_input( hesk_POST('subject') );

    if ($hesk_settings['require_subject'] == 1 && $tmpvar['subject'] == '')
    {
        $hesk_error_buffer['subject'] = $hesklang['enter_ticket_subject'];
    }
}

if ($hesk_settings['require_message'] == -1)
{
    $tmpvar['message'] = '';
}
else
{
    $tmpvar['message'] = hesk_input( hesk_POST('message') );

    if ($hesk_settings['require_message'] == 1 && $tmpvar['message'] == '')
    {
        $hesk_error_buffer['message'] = $hesklang['enter_message'];
    }
}

// Is category a valid choice?
if ($tmpvar['category'])
{
	hesk_verifyCategory();

	// Is auto-assign of tickets disabled in this category?
	if ( empty($hesk_settings['category_data'][$tmpvar['category']]['autoassign']) )
	{
		$hesk_settings['autoassign'] = false;
	}
}

// Custom fields
foreach ($hesk_settings['custom_fields'] as $k=>$v)
{
	if ($v['use']==1 && hesk_is_custom_field_in_category($k, $tmpvar['category']) )
    {
        if ($v['type'] == 'checkbox')
        {
			$tmpvar[$k]='';

        	if (isset($_POST[$k]) && is_array($_POST[$k]))
            {
				foreach ($_POST[$k] as $myCB)
				{
					$tmpvar[$k] .= ( is_array($myCB) ? '' : hesk_input($myCB) ) . '<br />';;
				}
				$tmpvar[$k]=substr($tmpvar[$k],0,-6);
            }
            else
            {
            	if ($v['req'])
                {
					$hesk_error_buffer[$k]=$hesklang['fill_all'].': '.$v['name'];
                }
            	$_POST[$k] = '';
            }

			$_SESSION["c_$k"]=hesk_POST_array($k);

        }
        elseif ($v['type'] == 'date')
        {
        	$tmpvar[$k] = hesk_POST($k);
            $_SESSION["c_$k"] = '';

            if ($date = hesk_datepicker_get_date($tmpvar[$k], false, 'UTC'))
            {
                $_SESSION["c_$k"] = $tmpvar[$k];

                $date->setTime(0, 0);
                $dmin = strlen($v['value']['dmin']) ? new DateTime($v['value']['dmin'] . ' t00:00:00 UTC') : false;
                $dmax = strlen($v['value']['dmax']) ? new DateTime($v['value']['dmax'] . ' t00:00:00 UTC') : false;


	            if ($dmin && $dmin->format('Y-m-d') > $date->format('Y-m-d'))
	            {
					$hesk_error_buffer[$k] = sprintf($hesklang['d_emin'], $v['name'], hesk_translate_date_string($dmin->format($hesk_settings['format_datepicker_php'])));
	            }
	            elseif ($dmax && $dmax->format('Y-m-d') < $date->format('Y-m-d'))
	            {
					$hesk_error_buffer[$k] = sprintf($hesklang['d_emax'], $v['name'], hesk_translate_date_string($dmax->format($hesk_settings['format_datepicker_php'])));
	            }
                else
                {
					$tmpvar[$k] = $date->getTimestamp();
                }
			}
            else
            {
				if ($v['req'])
				{
					$hesk_error_buffer[$k]=$hesklang['fill_all'].': '.$v['name'];
				}
            }
        }
        elseif ($v['type'] == 'email')
        {
			$tmp = $hesk_settings['multi_eml'];
            $hesk_settings['multi_eml'] = $v['value']['multiple'];
			$tmpvar[$k] = hesk_validateEmail( hesk_POST($k), 'ERR', 0);
            $hesk_settings['multi_eml'] = $tmp;

            if ($tmpvar[$k] != '')
            {
				$_SESSION["c_$k"] = hesk_input($tmpvar[$k]);
            }
            else
            {
            	$_SESSION["c_$k"] = '';

                if ($v['req'])
                {
            		$hesk_error_buffer[$k] = $v['value']['multiple'] ? sprintf($hesklang['cf_noem'], $v['name']) : sprintf($hesklang['cf_noe'], $v['name']);
                }
            }
        }
		elseif ($v['req'])
        {
        	$tmpvar[$k]=hesk_makeURL(nl2br(hesk_input( hesk_POST($k) )));
            if ($tmpvar[$k] == '')
            {
            	$hesk_error_buffer[$k]=$hesklang['fill_all'].': '.$v['name'];
            }
			$_SESSION["c_$k"]=hesk_POST($k);
        }
		else
        {
        	$tmpvar[$k]=hesk_makeURL(nl2br(hesk_input( hesk_POST($k) )));
			$_SESSION["c_$k"]=hesk_POST($k);
        }
	}
    else
    {
    	$tmpvar[$k] = '';
    }
}

// Check bans
$clean_followers = [];
foreach ($tmpvar['followers'] as $follower) {
    if (!hesk_isBannedEmail($follower)) {
        $clean_followers[] = $follower;
    }
}
$tmpvar['followers'] = $clean_followers;

if ($email_available && ! isset($hesk_error_buffer['email']) && hesk_isBannedEmail($tmpvar['email']) || hesk_isBannedIP(hesk_getClientIP()) )
{
	hesk_error($hesklang['baned_e']);
}

// Check maximum open tickets limit
$below_limit = true;
if ($email_available && $hesk_settings['max_open'] && ! isset($hesk_error_buffer['email']) )
{
	$res = hesk_dbQuery("SELECT COUNT(*) 
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` AS `tickets`
        INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` AS `ticket_to_customer`
            ON `tickets`.`id` = `ticket_to_customer`.`ticket_id`
        INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` AS `customers`
            ON `ticket_to_customer`.`customer_id` = `customers`.`id` 
        WHERE `status` IN ('0', '1', '2', '4', '5') 
        AND " . hesk_dbFormatEmail($tmpvar['email']));
	$num = hesk_dbResult($res);

	if ($num >= $hesk_settings['max_open'])
    {
    	$hesk_error_buffer = array( 'max_open' => sprintf($hesklang['maxopen'], $num, $hesk_settings['max_open']) );
        $below_limit = false;
    }
}

// If we reached max tickets let's save some resources
$use_legacy_attachments = hesk_POST('use-legacy-attachments', 0);
if ($below_limit)
{
	// Generate tracking ID
	$tmpvar['trackid'] = hesk_createID();

	// Attachments
	if ($hesk_settings['attachments']['use'])
	{
	    require_once(HESK_PATH . 'inc/attachments.inc.php');

	    $attachments = array();
        $trackingID  = $tmpvar['trackid'];


        if ($use_legacy_attachments) {
            // The user went to the fallback file upload system.
            for ($i = 1; $i <= $hesk_settings['attachments']['max_number']; $i++) {
                $att = hesk_uploadFile($i);
                if ($att !== false && !empty($att)) {
                    $attachments[$i] = $att;
                }
            }
        } else {
            // The user used the new drag-and-drop system.
            $temp_attachment_names = hesk_POST_array('attachments');
            foreach ($temp_attachment_names as $temp_attachment_name) {
                $temp_attachment = hesk_getTemporaryAttachment($temp_attachment_name);

                if ($temp_attachment !== null) {
                    $attachments[] = $temp_attachment;
                }
            }
        }
	}
	$tmpvar['attachments'] = '';
}

// If we have any errors lets store info in session to avoid re-typing everything
if (count($hesk_error_buffer))
{
	$_SESSION['iserror'] = array_keys($hesk_error_buffer);

    $_SESSION['c_name']     = hesk_POST('name');
    $_SESSION['c_email']    = hesk_POST('email');
    $_SESSION['c_priority'] = hesk_POST('priority');
    $_SESSION['c_subject']  = hesk_POST('subject');
    $_SESSION['c_message']  = hesk_POST('message');
    $_SESSION['c_followers'] = hesk_POST('follower_email');

    $tmp = '';
    foreach ($hesk_error_buffer as $error)
    {
        $tmp .= "<li>$error</li>\n";
    }


	if ($below_limit && $hesk_settings['attachments']['use'])
    {
        if ($use_legacy_attachments) {
            // Remove any successfully uploaded attachments
            hesk_removeAttachments($attachments);
        } else {
            // Send back the list of already-attached items
            $_SESSION['c_attachments'] = $attachments;
        }

    }

    $hesk_error_buffer = $hesklang['pcer'] . '<br /><br /><ul>' . $tmp . '</ul>';
    hesk_process_messages($hesk_error_buffer, 'index.php?a=add&category='.$tmpvar['category']);
}

$tmpvar['message'] = hesk_makeURL($tmpvar['message']);
$tmpvar['message'] = nl2br($tmpvar['message']);
$tmpvar['message_html'] = $tmpvar['message'];

// Track suggested knowledgebase articles
if ($hesk_settings['kb_enable'] && $hesk_settings['kb_recommendanswers'] && isset($_POST['suggested']) && is_array($_POST['suggested']) )
{
	$tmpvar['articles'] = implode(',', array_unique( array_map('intval', $_POST['suggested']) ) );
}

// Log ticket submission in ticket history
if ( ! empty($tmpvar['name'])) {
    $customer_name = $tmpvar['name'];
} else {
    $customer_name = $hesklang['customer'];
}
$tmpvar['history'] = sprintf($hesklang['thist15'], hesk_date(), $customer_name);

// Auto assign tickets if aplicable
$tmpvar['owner'] = 0;
$autoassign_owner = hesk_autoAssignTicket($tmpvar['category']);
if ($autoassign_owner)
{
	$tmpvar['owner']    = $autoassign_owner['id'];
    $tmpvar['history'] .= sprintf($hesklang['thist10'], hesk_date(), addslashes($autoassign_owner['name']).' ('.$autoassign_owner['user'].')');
    $tmpvar['assignedby'] = -1;
}

// Insert attachments
if ($hesk_settings['attachments']['use'] && ! empty($attachments) )
{
    // Delete temp attachment records and set the new filename
    if (!$use_legacy_attachments) {
        $attachments = hesk_migrateTempAttachments($attachments, $trackingID);
    }

    foreach ($attachments as $myatt)
    {
        hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."attachments` (`ticket_id`,`saved_name`,`real_name`,`size`) VALUES ('".hesk_dbEscape($tmpvar['trackid'])."','".hesk_dbEscape($myatt['saved_name'])."','".hesk_dbEscape($myatt['real_name'])."','".intval($myatt['size'])."')");
        $tmpvar['attachments'] .= hesk_dbInsertID() . '#' . $myatt['real_name'] .',';
    }
}

// Get or create a customer for the name/email combo
if (!isset($tmpvar['customer_id'])) {
    $tmpvar['customer_id'] = hesk_get_or_create_customer($tmpvar['name'], $tmpvar['email']);
}
$tmpvar['follower_ids'] = [];
$removed_followers = [];

// If customer accounts are required, you can only add registered users
if ($hesk_settings['customer_accounts'] && $hesk_settings['customer_accounts_required']) {
    foreach ($tmpvar['followers'] as $key => $follower) {
        if (($valid_follower = hesk_get_customer_id_by_email($follower, true)) !== null) {
            $tmpvar['follower_ids'][] = $valid_follower;
        } else {
            unset($tmpvar['followers'][$key]);
            $removed_followers[] = $follower;
        }
    }
} else {
    foreach ($tmpvar['followers'] as $follower) {
        $tmpvar['follower_ids'][] = hesk_get_or_create_follower($follower);
    }
}

$tmpvar['followers'] = array_values($tmpvar['followers']);

if (count($removed_followers)) {
    hesk_process_messages($hesklang['followers_removed'] . '<ul><li>' . implode('</li><li>', $removed_followers) . '</li></ul>', 'NOREDIRECT', 'NOTICE');
}

// Insert ticket to database
$ticket = hesk_newTicket($tmpvar);

// Notify the customer
if ($hesk_settings['notify_new'] && ($email_available || count($tmpvar['followers']) > 0))
{
	hesk_notifyCustomer();
}

// Need to notify staff?
// --> From autoassign?
if ($tmpvar['owner'] && $autoassign_owner['notify_assigned'])
{
	hesk_notifyAssignedStaff($autoassign_owner, 'ticket_assigned_to_you', 'notify_assigned', false);
}
// --> No autoassign, find and notify appropriate staff
elseif ( ! $tmpvar['owner'] )
{
	hesk_notifyStaff('new_ticket_staff', " `notify_new_unassigned` = '1' ");
}

// Next ticket show suggested articles again
$_SESSION['ARTICLES_SUGGESTED']=false;
$_SESSION['already_submitted']=1;

// Need email to view ticket? If yes, remember it by default
if ($hesk_settings['email_view_ticket'])
{
	hesk_setcookie('hesk_myemail', $tmpvar['email'], strtotime('+1 year'));
}

// Unset temporary variables
unset($tmpvar);
hesk_cleanSessionVars('tmpvar');
hesk_cleanSessionVars('c_category');
hesk_cleanSessionVars('c_priority');
hesk_cleanSessionVars('c_subject');
hesk_cleanSessionVars('c_message');
hesk_cleanSessionVars('c_question');
hesk_cleanSessionVars('c_attachments');
hesk_cleanSessionVars('c_followers');
hesk_cleanSessionVars('img_verified');

if ( ! $hesk_settings['remember_custom_field_values']) {
    foreach ($hesk_settings['custom_fields'] as $k=>$v) {
        hesk_cleanSessionVars("c_$k");
    }
}

$messages = hesk_get_messages();
$user_context = hesk_isCustomerLoggedIn(false);
$hesk_settings['render_template'](TEMPLATE_PATH . 'customer/create-ticket/create-ticket-confirmation.php', array(
    'trackingId' => $ticket['trackid'],
    'emailProvided' => $email_available,
    'messages' => $messages,
    'serviceMessages' => hesk_get_service_messages('t-ok'),
	'customerLoggedIn' => $user_context !== null,
	'customerUserContext' => $user_context
));

exit();


function hesk_forceStop()
{
	global $hesklang;
	?>
	<html>
	<head>
	<meta http-equiv="Refresh" content="0; url=index.php?a=add" />
	</head>
	<body>
	<p><a href="index.php?a=add"><?php echo $hesklang['c2c']; ?></a>.</p>
	</body>
	</html>
	<?php
    exit();
} // END hesk_forceStop()
?>
suggest_articles.php
wget 'https://lists2.roe3.org/hesk/suggest_articles.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

define('IN_SCRIPT',1);
define('HESK_PATH','./');

/* Get all the required files and functions */
require(HESK_PATH . 'hesk_settings.inc.php');
require(HESK_PATH . 'inc/common.inc.php');
hesk_load_database_functions();

/* Print XML header */
header('Content-Type: text/html; charset='.$hesklang['ENCODING']);

// Is Knowledgebase enabled and are suggestions/search suggestions enabled?
if ( ! $hesk_settings['kb_enable'] || ( ! $hesk_settings['kb_recommendanswers'] && ! $hesk_settings['kb_search'])) {
    print json_encode(array());
    exit();
}

/* Get the search query composed of the subject and message */
$query = hesk_REQUEST('q') or die('');

hesk_dbConnect();

// Do we require logged-in customers to view the help desk?
if ($hesk_settings['customer_accounts'] && $hesk_settings['customer_accounts_required'] == 2) {
    require(HESK_PATH . 'inc/customer_accounts.inc.php');
    hesk_session_start('CUSTOMER');
    if (! hesk_isCustomerLoggedIn(false)) {
        die('');
    }
}

require(HESK_PATH . 'inc/knowledgebase_functions.inc.php');

/* Get relevant articles from the database */
$res = hesk_dbQuery("SELECT t1.`id`, t1.`subject`, LEFT(t1.`content`, ".max(200, $hesk_settings['kb_substrart'] * 2).") AS `content`, MATCH(`subject`,`content`,`keywords`) AGAINST ('".hesk_dbEscape($query)."') AS `score`
					FROM `".hesk_dbEscape($hesk_settings['db_pfix']).'kb_articles` AS t1
					LEFT JOIN `'.hesk_dbEscape($hesk_settings['db_pfix'])."kb_categories` AS t2 ON t1.`catid` = t2.`id`
                    WHERE t1.`type`='0' AND t2.`type`='0'
                    AND `t2`.`id` IN (".implode(',', $hesk_settings['public_kb_categories_ids']).")
                    AND MATCH(`subject`,`content`,`keywords`) AGAINST ('".hesk_dbEscape($query)."')
					LIMIT ".intval($hesk_settings['kb_search_limit']));
$num = hesk_dbNumRows($res);

$articles = array();
/* Return found articles */
$max_score = 0;

while ($article = hesk_dbFetchAssoc($res))
{
    if ($article['score'] > $max_score)
    {
        $max_score = $article['score'];
    }

    if ($max_score && ($article['score'] / $max_score) < 0.25)
    {
        break;
    }

    $txt = strip_tags($article['content']);
    if (hesk_mb_strlen($txt) > $hesk_settings['kb_substrart'])
    {
        $txt = hesk_mb_substr($txt, 0, $hesk_settings['kb_substrart']).'...';
    }

    $articles[] = array(
        'id' => $article['id'],
        'subject' => $article['subject'],
        'contentPreview' => $txt,
        'hiddenInputValue' => $article['id'].'|'.stripslashes( hesk_input($article['subject']) )
    );
}

print json_encode($articles);
suggest_email.php
wget 'https://lists2.roe3.org/hesk/suggest_email.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

define('IN_SCRIPT',1);
define('HESK_PATH','./');

// Get all the required files and functions
require(HESK_PATH . 'hesk_settings.inc.php');
require(HESK_PATH . 'inc/common.inc.php');

// Feature enabled?
if ( ! $hesk_settings['detect_typos'])
{
	die('');
}

// Print XML header
header('Content-Type: application/json; charset='.$hesklang['ENCODING']);
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");

// Get the search query composed of the subject and message
$address = hesk_REQUEST('e') or die('');
$email_field = hesk_REQUEST('ef') or die('');
$display_div = hesk_REQUEST('dd') or die('');
$div = 1;

$suggestions = array();
// Do we allow multiple emails? If yes, check all
if ($hesk_settings['multi_eml'] || hesk_REQUEST('am'))
{
	// Make sure the format is correct
	$address = preg_replace('/\s/','',$address);
	$address = str_replace(';',',',$address);

	// Loops through emails and check for typos
	$div = 1;
	$all = explode(',',$address);
	foreach ($all as $address)
	{
		if ( ($suggest = hesk_emailTypo($address)) !== false )
		{
			$suggestions[] = hesk_emailTypoShow($address, $suggest, $div);
			$div++;
		}
	}
}
// If multiple emails are not allowed, check just first one
elseif ( ($suggest = hesk_emailTypo($address)) !== false )
{
	$suggestions[] = hesk_emailTypoShow($address, $suggest);
}

print json_encode($suggestions, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);
exit();


function hesk_emailTypoShow($address, $suggest, $div = '')
{
	global $hesk_settings, $hesklang, $email_field, $display_div;

    $safe_suggest = hesk_htmlspecialchars($suggest);

	return array(
        'id' => "emailtypo" . preg_replace('/[^A-Za-z0-9_-]/', '', $display_div . $div),
        'suggestText' => sprintf($hesklang['didum'], str_replace('@', '@<b>', $safe_suggest . '</b>')),
        'formattedSuggestedEmail' => hesk_htmlspecialchars(hesk_emailTypoJsString($suggest)),
        'originalAddress' => hesk_htmlspecialchars(hesk_emailTypoJsString(preg_quote($address, '/'))),
        'noResponseText' => $hesklang['nole'],
        'yesResponseText' => $hesklang['yfix']
    );
} // END hesk_emailTypoShow()


function hesk_emailTypoJsString($value)
{
    return addcslashes((string) $value, "\\'\"\r\n</");
} // END hesk_emailTypoJsString()


function hesk_emailTypo($address)
{
	global $hesk_settings;

	// Remove anything more than a single address
	$address = str_replace(strstr($address,','),'',$address);
	$address = str_replace(strstr($address,';'),'',$address);
	$address = strtolower(trim($address));

	// Get email domain
    $domain = substr(strrchr($address, '@'), 1);

	// If no domain return false
	if ( ! $domain)
    {
    	return false;
    }

	// If we have an exact match return false
    if ( in_array($domain, $hesk_settings['email_providers']) )
    {
    	return false;
    }


	$shortest = -1;
    $closest  = '';

	foreach ($hesk_settings['email_providers'] as $provider)
    {
		$similar =  levenshtein($domain, $provider, 2, 1, 3);

		if ($similar < 1)
        {
        	return false;
        }

	    if ($similar < $shortest || $shortest < 0)
        {
	        $closest  = $provider;
	        $shortest = $similar;
	    }
    }

    if ($shortest < 4)
    {
    	return str_replace($domain, $closest, $address);
    }
    else
    {
    	return false;
    }
}  // END hesk_emailTypo()
?>
ticket.php
wget 'https://lists2.roe3.org/hesk/ticket.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

define('IN_SCRIPT',1);
define('HESK_PATH','./');
define('HESK_NO_ROBOTS',1);

/* Get all the required files and functions */
require(HESK_PATH . 'hesk_settings.inc.php');
define('TEMPLATE_PATH', HESK_PATH . "theme/{$hesk_settings['site_theme']}/");
require(HESK_PATH . 'inc/common.inc.php');
require(HESK_PATH . 'inc/customer_accounts.inc.php');

// Are we in maintenance mode?
hesk_check_maintenance();

// Connect to the database and start a session
hesk_load_database_functions();
hesk_dbConnect();
hesk_session_start('CUSTOMER');

// Do we require logged-in customers to view the help desk?
hesk_isCustomerLoggedIn($hesk_settings['customer_accounts'] && $hesk_settings['customer_accounts_required']);

$hesk_error_buffer = array();
$do_remember = '';
$display = 'none';

if ($hesk_settings['customer_accounts']) {
    $user_context = hesk_isCustomerLoggedIn(false);
} else {
    $user_context = null;
}


/* A message from ticket reminder? */
if ( ! empty($_GET['remind']) )
{
    $display = 'block';
    $my_email = hesk_emailCleanup( hesk_validateEmail( hesk_GET('e'), 'ERR' , 0) );
    print_form();
}

// Do we have parameters in query string? If yes, store them in session and redirect
if ( isset($_GET['track']) || isset($_GET['e']) || isset($_GET['f']) || isset($_GET['r']) )
{
    $_SESSION['t_track'] = hesk_GET('track');
    $_SESSION['t_email'] = hesk_getCustomerEmail(1);
    $_SESSION['t_form']  = hesk_GET('f');
    $_SESSION['t_remember'] = strlen($do_remember) ? 'Y' : hesk_GET('r');

    $redirect_to = 'ticket.php';
    if (isset($_GET['language']))
    {
        $redirect_language = hesk_input(hesk_GET('language'));
        if ($redirect_language && isset($hesk_settings['languages'][$redirect_language]))
        {
            $redirect_to .= '?language=' . rawurlencode($redirect_language);
        }
    }

    header('Location: ' . $redirect_to);
    die();
}

/* Was this accessed by the form or link? */
$is_form = hesk_SESSION('t_form');

/* Get the tracking ID */
$trackingID = hesk_cleanID('', hesk_SESSION('t_track'));

/* Email required to view ticket? */
$my_email = $user_context !== null ?
    $user_context['email'] :
    hesk_getCustomerEmail(1, 't_email', 1);


/* Remember email address? */
$do_remember = strlen($do_remember) || strlen(hesk_SESSION('t_remember'));

/* Clean ticket parameters from the session data, we don't need them anymore */
hesk_cleanSessionVars( array('t_track', 't_email', 't_form', 't_remember') );

/* Any errors? Show the form */
if ($is_form)
{
    if ($user_context !== null) {
        // Logged in customers should go to My Tickets
        header('Location: '.HESK_PATH.'my_tickets.php');
        return;
    }

	if ( empty($trackingID) )
    {
    	$hesk_error_buffer[] = $hesklang['eytid'];
    }

    if ($hesk_settings['email_view_ticket'] && $hesk_settings['require_email'] && empty($my_email) )
    {
    	$hesk_error_buffer[] = $hesklang['enter_valid_email'];
    }

    $tmp = count($hesk_error_buffer);
    if ($tmp == 1)
    {
    	$hesk_error_buffer = implode('',$hesk_error_buffer);
		hesk_process_messages($hesk_error_buffer,'NOREDIRECT');
        print_form();
    }
    elseif ($tmp == 2)
    {
    	$hesk_error_buffer = $hesklang['pcer'].'<br /><br /><ul><li>'.$hesk_error_buffer[0].'</li><li>'.$hesk_error_buffer[1].'</li></ul>';
		hesk_process_messages($hesk_error_buffer,'NOREDIRECT');
        print_form();
    }
}
elseif (empty($trackingID))
{
    if ($user_context !== null) {
        // Logged in customers should go to My Tickets
        header('Location: '.HESK_PATH.'my_tickets.php');
        return;
    }

	print_form();
}
elseif (empty($my_email) && $hesk_settings['email_view_ticket'])
{
    if ($user_context !== null) {
        // Logged in customers should go to My Tickets
        header('Location: '.HESK_PATH.'my_tickets.php');
        return;
    }

    if ($hesk_settings['require_email']) {
        print_form();
    } else {
        $my_email = '';
    }
}

/* Limit brute force attempts */
hesk_limitBfAttempts();

// Load custom fields
require_once(HESK_PATH . 'inc/custom_fields.inc.php');

// Load statuses
require_once(HESK_PATH . 'inc/statuses.inc.php');

// Load priorities
require_once(HESK_PATH . 'inc/priorities.inc.php');

/* Get ticket info */
$res = hesk_dbQuery( "SELECT `t1`.* , `t2`.name AS `repliername`, `t2`.`nickname` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` AS `t1` LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."users` AS `t2` ON `t1`.`replierid` = `t2`.`id` WHERE `trackid`='".hesk_dbEscape($trackingID)."' LIMIT 1");

/* Ticket found? */
if (hesk_dbNumRows($res) != 1)
{
	/* Ticket not found, perhaps it was merged with another ticket? */
	$res = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `merged` LIKE '%#".hesk_dbEscape($trackingID)."#%' LIMIT 1");

	if (hesk_dbNumRows($res) == 1)
	{
    	/* OK, found in a merged ticket. Get info */
        $ticket = hesk_dbFetchAssoc($res);
        $customers = hesk_get_customers_for_ticket($ticket['id']);
        $customer_emails = array_map(function($customer) { return $customer['email']; }, $customers);

		/* If we require e-mail to view tickets check if it matches the one from merged ticket */
		if ( hesk_verifyEmailMatch($ticket['trackid'], $my_email, $customer_emails, 0) )
        {
        	hesk_process_messages( sprintf($hesklang['tme'], $trackingID, $ticket['trackid']) ,'NOREDIRECT','NOTICE');
            $trackingID = $ticket['trackid'];
        }
        else
        {
        	hesk_process_messages( sprintf($hesklang['tme1'], $trackingID, $ticket['trackid']) . '<br /><br />' . sprintf($hesklang['tme2'], $ticket['trackid']) ,'NOREDIRECT','NOTICE');
            $trackingID = $ticket['trackid'];
            print_form();
        }
	}
    else
    {
    	/* Nothing found, error out */
	    hesk_process_messages($hesklang['ticket_not_found'],'NOREDIRECT');
	    print_form();
    }
}
else
{
	/* We have a match, get ticket info */
	$ticket = hesk_dbFetchAssoc($res);
    $customers = hesk_get_customers_for_ticket($ticket['id']);
    $customer_emails = array_map(function($customer) { return $customer['email']; }, $customers);

	/* If we require e-mail to view tickets check if it matches the one in database */
    $match = hesk_verifyEmailMatch($trackingID, $my_email, $customer_emails);
    if ($match === 'EMAIL_REQUIRED') {
        hesk_process_messages($hesklang['e_c_email'],'NOREDIRECT');
        print_form();
    } elseif ($match === 'EMAIL_FALSE') {
        hesk_process_messages($hesklang['enmdb'],'NOREDIRECT');
        print_form();
    }
}

/* Ticket exists, clean brute force attempts */
hesk_cleanBfAttempts();

/* Remember email address? */
if ($is_form)
{
	if ($do_remember)
	{
		hesk_setcookie('hesk_myemail', $my_email, strtotime('+1 year'));
	}
	elseif ( isset($_COOKIE['hesk_myemail']) )
	{
		hesk_setcookie('hesk_myemail', '');
	}
}

/* Set last replier name */
if ($ticket['lastreplier']) {
    if (empty($ticket['repliername'])) {
        $ticket['repliername'] = $hesklang['staff'];
    } elseif ($hesk_settings['staff_nicknames'] && $ticket['nickname'] != '') {
        $ticket['repliername'] = $ticket['nickname'];
    }
} else {
    $last_replier = hesk_getReplierNameArray($ticket);
    $ticket['repliername'] = $last_replier['nickname'];
}

// If IP is unknown (tickets via email pipe/pop3 fetching) assume current visitor IP as customer IP
if ($ticket['ip'] == '' || $ticket['ip'] == 'Unknown' || $ticket['ip'] == $hesklang['unknown'])
{
	hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` SET `ip` = '".hesk_dbEscape(hesk_getClientIP())."', `lastchange`=`lastchange` WHERE `id`=".intval($ticket['id']));
}

if ( ! isset($hesk_settings['priorities'][$ticket['priority']])) {
    $ticket['priority'] = array_keys($hesk_settings['priorities'])[0];
}

/* Get category name and ID */
$result = hesk_dbQuery("SELECT `name` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` WHERE `id`='".intval($ticket['category'])."' LIMIT 1");

/* If this category has been deleted use the default category with ID 1 */
if (hesk_dbNumRows($result) != 1)
{
	$result = hesk_dbQuery("SELECT `name` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` WHERE `id`='1' LIMIT 1");
}

$category = hesk_dbFetchAssoc($result);

/* Get replies */
$result  = hesk_dbQuery("SELECT `replies`.*, `customers`.`name` AS `customer_name`, `customers`.`email` AS `customer_email`, `users`.`name` AS `staff_name`, `users`.`nickname`
    FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` AS `replies`
    LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` AS `customers`
        ON `customers`.`id` = `replies`.`customer_id`
    LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."users` AS `users`
        ON `users`.`id` = `replies`.`staffid`
    WHERE `replyto`='".intval($ticket['id'])."'
    ORDER BY `id` ".($hesk_settings['new_top'] ? 'DESC' : 'ASC') );
$replies = hesk_dbNumRows($result);
$repliesArray = array();
$unread_replies = array();
while ($row = hesk_dbFetchAssoc($result)) {
    if ($row['staffid']) {
        $row['name'] = $row['staff_name'] === null ?
            $hesklang['staff_deleted'] :
            (($hesk_settings['staff_nicknames'] && $row['nickname'] != '') ? $row['nickname'] : $row['staff_name']);

        if (!$row['read']) {
            $unread_replies[] = $row['id'];
        }
    } else {
        if ($row['customer_name'] === null || $row['customer_name'] == '') {
            if ($row['customer_email'] !== null && strlen($row['customer_email'])) {
                $row['name'] = $row['customer_email'];
            } else {
                $row['name'] = $hesklang['anon_name'];
            }
        } else {
            $row['name'] = $row['customer_name'];
        }
    }

    $repliesArray[] = $row;
}
/* If needed update unread replies as read for staff to know */
if (count($unread_replies))
{
    hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` SET `read` = '1' WHERE `id` IN ('".implode("','", $unread_replies)."')");
}

// Demo mode
if ( defined('HESK_DEMO') )
{
    foreach ($customers as $customer) {
        $customer['email'] = 'hidden@demo.com';
    }
}
$ticket['customers'] = $customers;

if (count($ticket['customers']) === 0) {
    // If a ticket has 0 customers, it was anonymized
    $ticket['customers'] = [
        [
            'name' => $hesklang['anon_name'],
            'email' => $hesklang['anon_email'],
            'customer_type' => 'REQUESTER'
        ]
    ];
}

$messages = hesk_get_messages();

$custom_fields_before_message = array();
$custom_fields_after_message = array();
foreach ($hesk_settings['custom_fields'] as $k=>$v) {
    if ($v['use']==1 && (strlen($ticket[$k]) || hesk_is_custom_field_in_category($k, $ticket['category'])))
    {
        $custom_field = array(
            'name' => $v['name'],
            'name:' => $v['name:'],
            'value' => $ticket[$k],
            'type' => $v['type']
        );

        if ($v['type'] == 'date') {
            $custom_field['date_format'] = $v['value']['date_format'];
        }


        if ($v['place'] == 1) {
            $custom_fields_after_message[] = $custom_field;
        } else {
            $custom_fields_before_message[] = $custom_field;
        }
    }
}

$hesk_settings['render_template'](TEMPLATE_PATH . 'customer/view-ticket/view-ticket.php', array(
    'customerUserContext' => $user_context,
    'messages' => $messages,
    'serviceMessages' => hesk_get_service_messages('t-view'),
    'ticketJustReopened' => isset($_SESSION['force_form_top']),
    'ticket' => $ticket,
    'trackingID' => $trackingID,
    'numberOfReplies' => $replies,
    'replies' => $repliesArray,
    'category' => $category,
    'email' => $my_email,
    'customFieldsBeforeMessage' => $custom_fields_before_message,
    'customFieldsAfterMessage' => $custom_fields_after_message
));
unset($_SESSION['force_form_top']);

/* Clear unneeded session variables */
hesk_cleanSessionVars('ticket_message');

/*** START FUNCTIONS ***/

function print_form()
{
	global $hesk_settings, $hesklang;
    global $hesk_error_buffer, $my_email, $trackingID, $do_remember, $display, $user_context;

	/* Print header */
	$hesk_settings['tmp_title'] = $hesk_settings['hesk_title'] . ' - ' . $hesklang['view_ticket'];

	$messages = hesk_get_messages();

	$hesk_settings['render_template'](TEMPLATE_PATH . 'customer/view-ticket/form.php', array(
        'customerUserContext' => $user_context,
        'messages' => $messages,
        'serviceMessages' => hesk_get_service_messages('t-form'),
        'trackingId' => $trackingID,
        'email' => $my_email,
        'rememberEmail' => $do_remember,
        'displayForgotTrackingIdForm' => !empty($_GET['forgot']),
        'submittedForgotTrackingIdForm' => $display === 'block'
    ));

exit();
} // End print_form()
upload_attachment.php
wget 'https://lists2.roe3.org/hesk/upload_attachment.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

define('IN_SCRIPT', 1);
define('HESK_PATH', './');
define('HESK_NO_ROBOTS',1);

require_once(HESK_PATH . 'hesk_settings.inc.php');
require_once(HESK_PATH . 'inc/common.inc.php');

// Demo mode?
if ( defined('HESK_DEMO') ) {
    http_response_code(400);
    exit();
}

hesk_load_database_functions();
hesk_dbConnect();
hesk_session_start('CUSTOMER');

if ($hesk_settings['customer_accounts'] && $hesk_settings['customer_accounts_required']) {
    require_once(HESK_PATH . 'inc/customer_accounts.inc.php');

    if ( ! hesk_isCustomerLoggedIn(false)) {
        header('Content-Type: application/json; charset=utf-8');
        http_response_code(200);
        print json_encode(array(
            'status' => 'failure',
            'status_code' => 401,
            'message' => $hesklang['customer_must_be_logged_in_to_view']
        ));
        exit();
    }
}

require_once(HESK_PATH . 'inc/upload_attachment.inc.php');
verify_registration.php
wget 'https://lists2.roe3.org/hesk/verify_registration.php'
View Content
<?php
/**
 *
 * This file is part of HESK - PHP Help Desk Software.
 *
 * (c) Copyright Klemen Stirn. All rights reserved.
 * https://www.hesk.com
 *
 * For the full copyright and license agreement information visit
 * https://www.hesk.com/eula.php
 *
 */

define('IN_SCRIPT',1);
define('HESK_PATH','./');
define('HESK_NO_ROBOTS',1);

/* Get all the required files and functions */
require(HESK_PATH . 'hesk_settings.inc.php');
define('TEMPLATE_PATH', HESK_PATH . "theme/{$hesk_settings['site_theme']}/");
require(HESK_PATH . 'inc/common.inc.php');
require(HESK_PATH . 'inc/customer_accounts.inc.php');

// Are customer accounts enabled?
if (!$hesk_settings['customer_accounts']) {
    header('Location: index.php');
    exit();
}

// Are we in maintenance mode?
hesk_check_maintenance();

hesk_load_database_functions();
hesk_session_start('CUSTOMER');

hesk_dbConnect();

// If we don't have both an email *and* a verification token, don't attempt to do anything
if (!isset($_GET['email']) || !isset($_GET['verificationToken'])) {
    header('Location: index.php');
    exit();
}
$email = $_GET['email'];
$verificationToken = $_GET['verificationToken'];

$verification_result = hesk_verify_customer_account($email, $verificationToken);
$verification_type = 'NEW';

if (!$verification_result && $hesk_settings['customer_accounts_allow_email_changes']) {
    // Maybe they're changing their email?
    $verification_result = hesk_verify_email_change_request($email, $verificationToken);
    $verification_type = 'EMAIL UPDATE';
}

if (!$verification_result) {
    hesk_process_messages($hesklang['customer_registration_verify_failure'], 'NOREDIRECT');
    $hesk_settings['render_template'](TEMPLATE_PATH . 'customer/account/verify-registration.php', [
        'messages' => hesk_get_messages()
    ]);
    return;
}

hesk_merge_customer_accounts($email);


if ($hesk_settings['customer_accounts_admin_approvals'] && $verification_type === 'NEW') {
    hesk_mark_account_needing_approval($email);

    // Notify staff members?
    // Don't send an email every time, only when we reach a treshold number of pending approvals
    $notify_treshold = array(
        1, 5, 10, 50, 100, 500, 1000
    );
    $res = hesk_dbQuery("SELECT COUNT(*) FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` WHERE `verified` = 2");
    $num = hesk_dbResult($res);
    if (in_array($num, $notify_treshold)) {
        require(HESK_PATH . 'inc/email_functions.inc.php');
        hesk_notifyStaffOfPendingApprovals($num);
    }

    hesk_process_messages($hesklang['customer_registration_verify_approval_needed'],
        'NOREDIRECT',
        'NOTICE');
    $hesk_settings['render_template'](TEMPLATE_PATH . 'customer/account/verify-registration.php', [
        'messages' => hesk_get_messages()
    ]);
} else {
    $_SESSION['login_email'] = $email;
    $message = $verification_type === 'NEW' ?
        $hesklang['customer_registration_verify_success'] :
        $hesklang['customer_change_email_verify_success'];
    hesk_process_messages($message, 'login.php', 'SUCCESS');
}