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`).

html2text
htmlpurifier
jscolor
mail
recaptcha
tabs
tecnick
timer
tiny_mce
treemenu
zip
admin_functions.inc.php
wget 'https://lists2.roe3.org/hesk/inc/admin_functions.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

// Possible fields to be displayed in ticket list
$hesk_settings['possible_ticket_list'] = array(
'id' => $hesklang['id'],
'trackid' => $hesklang['trackID'],
'dt' => $hesklang['submitted'],
'lastchange' => $hesklang['last_update'],
'category' => $hesklang['category'],
'name' => $hesklang['customer'],
'email' => $hesklang['email'],
'subject' => $hesklang['subject'],
'status' => $hesklang['status'],
'owner' => $hesklang['owner'],
'replies' => $hesklang['replies'],
'staffreplies' => $hesklang['replies'] . ' (' . $hesklang['staff'] .')',
'lastreplier' => $hesklang['last_replier'],
'time_worked' => $hesklang['ts'],
'due_date' => $hesklang['due_date']
);

define('HESK_NO_ROBOTS', true);
define('IS_ADMIN_PAGE', true);

/*** FUNCTIONS ***/


function hesk_show_column($column)
{
	global $hesk_settings;

	return in_array($column, $hesk_settings['ticket_list']) ? true : false;

} // END hesk_show_column()


function hesk_getHHMMSS($in)
{
	$in = hesk_getTime($in);
    return explode(':', $in);
} // END hesk_getHHMMSS();


function hesk_getTime($in)
{
	$in = trim($in);

	/* If everything is OK this simple check should return true */
    if ( preg_match('/^([0-9]{2,3}):([0-5][0-9]):([0-5][0-9])$/', $in) )
    {
    	return $in;
    }

	/* No joy, let's try to figure out the correct values to use... */
    $h = 0;
    $m = 0;
    $s = 0;

    /* How many parts do we have? */
    $parts = substr_count($in, ':');

    switch ($parts)
    {
    	/* Only two parts, let's assume minutes and seconds */
		case 1:
	        list($m, $s) = explode(':', $in);
	        break;

        /* Three parts, so explode to hours, minutes and seconds */
        case 2:
	        list($h, $m, $s) = explode(':', $in);
	        break;

        /* Something other was entered, let's assume just minutes */
        default:
	        $m = $in;
    }

	/* Make sure all inputs are integers */
	$h = intval($h);
    $m = intval($m);
    $s = intval($s);

	/* Convert seconds to minutes if 60 or more seconds */
    if ($s > 59)
    {
    	$m = floor($s / 60) + $m;
        $s = intval($s % 60);
    }

	/* Convert minutes to hours if 60 or more minutes */
    if ($m > 59)
    {
    	$h = floor($m / 60) + $h;
        $m = intval($m % 60);
    }

    /* MySQL accepts max time value of 838:59:59 */
    if ($h > 838)
    {
    	return '838:59:59';
    }    

	/* That's it, let's send out formatted time string */
    return str_pad($h, 2, "0", STR_PAD_LEFT) . ':' . str_pad($m, 2, "0", STR_PAD_LEFT) . ':' . str_pad($s, 2, "0", STR_PAD_LEFT);

} // END hesk_getTime();


function hesk_mergeTickets($merge_these, $merge_into)
{
	global $hesk_settings, $hesklang, $hesk_db_link;

    /* Target ticket must not be in the "merge these" list */
    if ( in_array($merge_into, $merge_these) )
    {
        $merge_these = array_diff($merge_these, array( $merge_into ) );
    }

    /* At least 1 ticket needs to be merged with target ticket */
    if ( count($merge_these) < 1 )
    {
    	$_SESSION['error'] = $hesklang['merr1'];
    	return false;
    }

    /* Make sure target ticket exists */
	$res = hesk_dbQuery("SELECT `id`,`trackid`,`category` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `id`='".intval($merge_into)."' LIMIT 1");
	if (hesk_dbNumRows($res) != 1)
	{
    	$_SESSION['error'] = $hesklang['merr2'];
		return false;
	}
	$ticket = hesk_dbFetchAssoc($res);

	/* Make sure user has access to ticket category */
	if ( ! hesk_okCategory($ticket['category'], 0) )
	{
    	$_SESSION['error'] = $hesklang['merr3'];
		return false;
	}

    /* Set some variables for later */
    $sec_worked = 0;
    $history = '';
    $merged = '';

	/* Get messages, replies, notes and attachments of tickets that will be merged */
    foreach ($merge_these as $this_id)
    {
		/* Validate ID */
    	if ( is_array($this_id) )
        {
        	continue;
        }
    	$this_id = intval($this_id) or hesk_error($hesklang['id_not_valid']);

        /* Get required ticket information */
        $res = hesk_dbQuery("SELECT `tickets`.`id` AS `id`,`trackid`,`category`,`ticket_customers`.`customer_id` AS `customer_id`,`message`,`message_html`,`dt`,`time_worked`,`attachments` 
            FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` AS `tickets`
            LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` AS `ticket_customers`
                ON `tickets`.`id` = `ticket_customers`.`ticket_id`
                AND `ticket_customers`.`customer_type` = 'REQUESTER' 
            WHERE `tickets`.`id`='".intval($this_id)."' LIMIT 1");
		if (hesk_dbNumRows($res) != 1)
		{
			continue;
		}
        $row = hesk_dbFetchAssoc($res);

        /* Has this user access to the ticket category? */
        if ( ! hesk_okCategory($row['category'], 0) )
        {
        	continue;
        }

        /* Insert ticket message as a new reply to target ticket */
        $customer_id = $row['customer_id'] !== null ? intval($row['customer_id']) : 'NULL';
		hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` (`replyto`,`customer_id`,`message`,`message_html`,`dt`,`attachments`) VALUES ('".intval($ticket['id'])."',".$customer_id.",'".hesk_dbEscape(addslashes($row['message']))."','".hesk_dbEscape(addslashes($row['message_html']))."','".hesk_dbEscape($row['dt'])."','".hesk_dbEscape($row['attachments'])."')");

		/* Update attachments  */
		hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."attachments` SET `ticket_id`='".hesk_dbEscape($ticket['trackid'])."' WHERE `ticket_id`='".hesk_dbEscape($row['trackid'])."'");

        /* Get old ticket replies and insert them as new replies */
        $res = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` WHERE `replyto`='".intval($row['id'])."' ORDER BY `id` ASC");
        while ( $reply = hesk_dbFetchAssoc($res) )
        {
            $customer_id = $reply['customer_id'] !== null ? intval($reply['customer_id']) : 'NULL';
			hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` (`replyto`,`message`,`message_html`,`dt`,`attachments`,`staffid`,`customer_id`,`rating`,`read`) VALUES ('".intval($ticket['id'])."','".hesk_dbEscape(addslashes($reply['message']))."','".hesk_dbEscape(addslashes($reply['message_html']))."','".hesk_dbEscape($reply['dt'])."','".hesk_dbEscape($reply['attachments'])."','".intval($reply['staffid'])."',".$customer_id.",'".intval($reply['rating'])."','".intval($reply['read'])."')");
        }

		/* Delete replies to the old ticket */
		hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` WHERE `replyto`='".intval($row['id'])."'");

        /* Get old ticket notes and insert them as new notes */
        $res = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."notes` WHERE `ticket`='".intval($row['id'])."' ORDER BY `id` ASC");
        while ( $note = hesk_dbFetchAssoc($res) )
        {
			hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."notes` (`ticket`,`who`,`dt`,`message`,`attachments`) VALUES ('".intval($ticket['id'])."','".intval($note['who'])."','".hesk_dbEscape($note['dt'])."','".hesk_dbEscape(addslashes($note['message']))."','".hesk_dbEscape($note['attachments'])."')");
        }

		/* Delete replies to the old ticket */
		hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."notes` WHERE `ticket`='".intval($row['id'])."'");

        /* Insert old ticket's requester and followers to the new tickets as followers, assuming they're not already on it */
        hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` (`ticket_id`, `customer_id`, `customer_type`)
            SELECT ".intval($ticket['id']).", `customer_id`, 'FOLLOWER'
            FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` AS `outer_ticket_to_customer`
            WHERE `ticket_id` = ".intval($row['id'])."
                AND NOT EXISTS (
                    SELECT 1
                    FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` AS `inner_ticket_to_customer`
                    WHERE `inner_ticket_to_customer`.`ticket_id` = ".intval($ticket['id'])."
                    AND `inner_ticket_to_customer`.`customer_id` = `outer_ticket_to_customer`.`customer_id` 
                )");
        /* Delete old ticket's customer mappings */
        hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer`
            WHERE `ticket_id` = ".intval($row['id']));

	    /* Delete old ticket */
		hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `id`='".intval($row['id'])."'");

		/* Log that ticket has been merged */
		$history .= sprintf($hesklang['thist13'],hesk_date(),$row['trackid'],addslashes($_SESSION['name']).' ('.$_SESSION['user'].')');

        /* Add old ticket ID to target ticket "merged" field */
        $merged .= '#' . $row['trackid'];

		/* Convert old ticket "time worked" to seconds and add to $sec_worked variable */
		list ($hr, $min, $sec) = explode(':', $row['time_worked']);
		$sec_worked += (((int)$hr) * 3600) + (((int)$min) * 60) + ((int)$sec);
    }

	/* Convert seconds to HHH:MM:SS */
	$sec_worked = hesk_getTime('0:'.$sec_worked);

	// Get number of replies
	$total			= 0;
	$staffreplies	= 0;

	$res = hesk_dbQuery("SELECT COUNT(*) as `cnt`, (CASE WHEN `staffid` = 0 THEN 0 ELSE 1 END) AS `staffcnt` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` WHERE `replyto`=".intval($ticket['id'])." GROUP BY `staffcnt`");
	while ( $row = hesk_dbFetchAssoc($res) )
	{
		$total += $row['cnt'];
		$staffreplies += ($row['staffcnt'] ? $row['cnt'] : 0);
	}

	$replies_sql = " `replies`={$total}, `staffreplies`={$staffreplies} , ";

	// Get first staff reply
	if ($staffreplies)
	{
		$res = hesk_dbQuery("SELECT `dt`, `staffid` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` WHERE `replyto`=".intval($ticket['id'])." AND `staffid`>0 ORDER BY `dt` ASC LIMIT 1");
		$reply = hesk_dbFetchAssoc($res);
		$replies_sql .= " `firstreply`='".hesk_dbEscape($reply['dt'])."', `firstreplyby`=".intval($reply['staffid'])." , ";
	}

    /* Update history (log) and merged IDs of target ticket */
	hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` SET $replies_sql `time_worked`=ADDTIME(`time_worked`, '".hesk_dbEscape($sec_worked)."'), `merged`=CONCAT(`merged`,'".hesk_dbEscape($merged . '#')."'), `history`=CONCAT(`history`,'".hesk_dbEscape($history)."') WHERE `id`='".intval($merge_into)."'");

    return true;

} // END hesk_mergeTickets()


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

	// Demo mode
	if ( defined('HESK_DEMO') )
	{
		return true;
	}
	// Remove the part that forces saving as default - we don't need it every time
    $default_list = str_replace('&def=1','',$_SERVER['QUERY_STRING']);

    // Update database
	$res = hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` SET `default_list`='".hesk_dbEscape($default_list)."' WHERE `id`='".intval($_SESSION['id'])."'");

    // Update session values so the changes take effect immediately
    $_SESSION['default_list'] = $default_list;

    return true;
    
} // END hesk_updateStaffDefaults()


function hesk_makeJsString($in)
{
	return addslashes(preg_replace("/\s+/",' ',$in));
} // END hesk_makeJsString()


function hesk_json_encode_for_js($in)
{
    $json = json_encode((string) $in, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT);
    return $json === false ? '""' : $json;
} // END hesk_json_encode_for_js()


function hesk_json_encode_jsString($str)
{
    $str  = str_replace('<br />', '', (string) $str);
    $from = array('/\<a href="mailto\:([^"]*)"\>([^\<]*)\<\/a\>/i', '/\<a href="([^"]*)" target="_blank"\>([^\<]*)\<\/a\>/i');
    $to   = array("$1", "$1");
    return hesk_json_encode_for_js(preg_replace($from, $to, $str));
} // END hesk_json_encode_jsString()


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

	$res = hesk_dbQuery("SELECT COUNT(*) FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."mail` WHERE `to`='".intval($_SESSION['id'])."' AND `read`='0' AND `deletedby`!='".intval($_SESSION['id'])."' ");
	$num = hesk_dbResult($res,0,0);

	return $num;
} // END hesk_checkNewMail()


function hesk_dateToString($dt, $returnName=1, $returnTime=0, $returnMonth=0, $from_database=false)
{
	global $hesk_settings, $hesklang;

	$dt = strtotime($dt);

	// Adjust MySQL time if different from PHP time
	if ($from_database)
	{
		if ( ! defined('MYSQL_TIME_DIFF') )
		{
			define('MYSQL_TIME_DIFF', time()-hesk_dbTime() );
		}

		if (MYSQL_TIME_DIFF != 0)
		{
			$dt += MYSQL_TIME_DIFF;
		}
	}

	list($y,$m,$n,$d,$G,$i,$s) = explode('-', date('Y-n-j-w-G-i-s', $dt) );

	$m = $hesklang['m'.$m];
	$d = $hesklang['d'.$d];

	if ($returnName)
	{
		return "$d, $m $n, $y";
	}

    if ($returnTime)
    {
    	return "$d, $m $n, $y $G:$i:$s";
    }

    if ($returnMonth)
    {
    	return "$m $y";
    }

	return "$m $n, $y";
} // End hesk_dateToString()


function hesk_getCategoriesArray($kb = 0) {
	global $hesk_settings, $hesklang, $hesk_db_link;

	$categories = array();
    if ($kb)
    {
    	$result = hesk_dbQuery('SELECT `id`, `name` FROM `'.hesk_dbEscape($hesk_settings['db_pfix']).'kb_categories` ORDER BY `cat_order` ASC');
    }
    else
    {
		$result = hesk_dbQuery('SELECT `id`, `name` FROM `'.hesk_dbEscape($hesk_settings['db_pfix']).'categories` ORDER BY `cat_order` ASC');
    }

	while ($row=hesk_dbFetchAssoc($result))
	{
		$categories[$row['id']] = $row['name'];
	}

    return $categories;
} // END hesk_getCategoriesArray()


function hesk_getHTML($in)
{
	global $hesk_settings, $hesklang;

	$replace_from = array("\t","<?","?>","$","<%","%>");
	$replace_to   = array("","&lt;?","?&gt;","\$","&lt;%","%&gt;");

	$in = trim($in);
	$in = str_replace($replace_from,$replace_to,$in);
	$in = preg_replace('/\<script(.*)\>(.*)\<\/script\>/Uis',"<script$1></script>",$in);
	$in = preg_replace('/\<\!\-\-(.*)\-\-\>/Uis',"<!-- comments have been removed -->",$in);

	if (HESK_SLASH === true)
	{
		$in = addslashes($in);
	}
    $in = str_replace('\"','"',$in);

	return $in;
} // END hesk_getHTML()


function hesk_autoLogin($noredirect=0)
{
	global $hesk_settings, $hesklang, $hesk_db_link;

    if (!$hesk_settings['autologin']) {
        return false;
    }

    if (empty($remember = hesk_COOKIE('hesk_remember')) || substr_count($remember, ':') !== 1) {
        return false;
    }

    // Login cookies exist, now lets limit brute force attempts
    hesk_limitBfAttempts();

    // Admin login URL
    $url = $hesk_settings['hesk_url'] . '/' . $hesk_settings['admin_dir'] . '/index.php?a=login&notice=1';

    // Get and verify authentication tokens
    list($selector, $authenticator) = explode(':', $remember);
    $authenticator = base64_decode($authenticator);
    if (strlen($authenticator) > 256) {
        hesk_setcookie('hesk_remember', '');
        header('Location: '.$url);
        exit();
    }

    $result = hesk_dbQuery('SELECT * FROM `'.$hesk_settings['db_pfix']."auth_tokens` WHERE `selector` = '".hesk_dbEscape($selector)."' AND `expires` > NOW() AND `user_type` = 'STAFF' LIMIT 1");
    if (hesk_dbNumRows($result) != 1) {
        hesk_setcookie('hesk_remember', '');
        header('Location: '.$url);
        exit();
	}

    $auth = hesk_dbFetchAssoc($result);

    if ( ! hash_equals($auth['token'], hash('sha256', $authenticator))) {
        hesk_setcookie('hesk_remember', '');
        header('Location: '.$url);
        exit();
    }

    // Token OK, let's regenerate session ID and get user data
    hesk_session_regenerate_id();

    $result = hesk_dbQuery('SELECT * FROM `'.$hesk_settings['db_pfix']."users` WHERE `id` = ".intval($auth['user_id'])." AND `active` = 1 LIMIT 1");
    if (hesk_dbNumRows($result) != 1) {
        hesk_setcookie('hesk_remember', '');
        header('Location: '.$url);
        exit();
    }

    $row = hesk_dbFetchAssoc($result);

    // Auto-login must not preserve a previously elevated session
    hesk_cleanSessionVars(array('elevated', 'elevator_target'));

    foreach ($row as $k => $v) {
        if ($k == 'pass') {
            continue;
        }
        $_SESSION[$k] = $v;
    }

    $user = $row['user'];
    define('HESK_USER', $user);

    // Each token should only be used once, so update the old one with a new one
    $selector = base64_encode(random_bytes(9));
    $authenticator = random_bytes(33);
    hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."auth_tokens` SET `selector`='".hesk_dbEscape($selector)."', `token` = '".hesk_dbEscape(hash('sha256', $authenticator))."', `created` = NOW() WHERE `id` = ".intval($auth['id']));
    hesk_setcookie('hesk_remember', $selector.':'.base64_encode($authenticator), strtotime('+1 year'));

	// Set a tag that will be used to expire sessions after username or password change
	$_SESSION['session_verify'] = hesk_activeSessionCreateTag($user, $row['pass']);

	/* Login successful, clean brute force attempts */
	hesk_cleanBfAttempts();

	/* Get allowed categories */
	if (empty($_SESSION['isadmin']))
	{
	    $_SESSION['categories']=explode(',',$_SESSION['categories']);
	}

    /* Close any old tickets here so Cron jobs aren't necessary */
	if ($hesk_settings['autoclose'])
    {
    	$revision = sprintf($hesklang['thist3'],hesk_date(),$hesklang['auto']);
    	$dt  = date('Y-m-d H:i:s',time() - $hesk_settings['autoclose']*86400);

		// Notify customer of closed ticket?
		if ($hesk_settings['notify_closed'])
		{
			// Get list of tickets
			$result = hesk_dbQuery("SELECT * FROM `".$hesk_settings['db_pfix']."tickets` WHERE `status` = '2' AND `lastchange` <= '".hesk_dbEscape($dt)."' ");
			if (hesk_dbNumRows($result) > 0)
			{
				global $ticket;

				// Load required functions?
				if ( ! function_exists('hesk_notifyCustomer') )
				{
					require(HESK_PATH . 'inc/email_functions.inc.php');
				}

				while ($ticket = hesk_dbFetchAssoc($result))
				{
					$ticket['dt'] = hesk_date($ticket['dt'], true);
					$ticket['lastchange'] = hesk_date($ticket['lastchange'], true);
					$ticket = hesk_ticketToPlain($ticket, 1, 0);
					hesk_notifyCustomer('ticket_closed');
				}
			}
		}

		// Update ticket statuses and history in database
		hesk_dbQuery("UPDATE `".$hesk_settings['db_pfix']."tickets` SET `status`='3', `closedat`=NOW(), `closedby`='-1', `history`=CONCAT(`history`,'".hesk_dbEscape($revision)."') WHERE `status` = '2' AND `lastchange` <= '".hesk_dbEscape($dt)."' ");
    }

	/* If session expired while a HESK page is open just continue using it, don't redirect */
    if ($noredirect)
    {
    	return true;
    }

	/* Redirect to the destination page */
	header('Location: ' . hesk_verifyGoto() );
	exit();
} // END hesk_autoLogin()


function hesk_isLoggedIn()
{
	global $hesk_settings;

	$referer = hesk_input($_SERVER['REQUEST_URI']);
	$referer = str_replace('&amp;','&',$referer);

	// Admin login URL
	$url = $hesk_settings['hesk_url'] . '/' . $hesk_settings['admin_dir'] . '/index.php?a=login&notice=1&goto='.urlencode($referer);

    // A password reset link must never be treated as a full staff login.
    // profile.php handles reset-only sessions before calling hesk_isLoggedIn().
    if ( ! empty($_SESSION['password_reset']))
    {
        hesk_session_stop();
        header('Location: '.$url);
        exit();
    }

    if ( empty($_SESSION['id']) || empty($_SESSION['session_verify']))
    {
    	if ($hesk_settings['autologin'] && hesk_autoLogin(1) )
        {
			// Users online
        	if ($hesk_settings['online'])
            {
            	require(HESK_PATH . 'inc/users_online.inc.php');
                hesk_initOnline($_SESSION['id']);
            }

        	return true;
        }

		hesk_session_stop();
        header('Location: '.$url);
        exit();
    }
    else
    {
        // hesk_session_regenerate_id();

		// Let's make sure access data is up-to-date
		$res = hesk_dbQuery( "SELECT `id`,`user`, `pass`, `isadmin`, `categories`, `heskprivileges`, `signature` FROM `".$hesk_settings['db_pfix']."users` WHERE `id` = '".intval($_SESSION['id'])."' AND `active` = 1 LIMIT 1" );

		// Exit if user not found
		if (hesk_dbNumRows($res) != 1)
		{
			hesk_session_stop();
			header('Location: '.$url);
			exit();
		}

		// Fetch results from database
		$me = hesk_dbFetchAssoc($res);

		// Verify this session is still valid
		if ( ! hesk_activeSessionValidate($me['user'], $me['pass'], $_SESSION['session_verify']) )
		{
			hesk_session_stop();
			header('Location: '.$url);
			exit();
		}

		// Update session variables as needed
		if ($me['isadmin'] == 1)
		{
			$_SESSION['isadmin'] = 1;
		}
		else
		{
			$_SESSION['isadmin'] = 0;
			$_SESSION['categories'] = explode(',', $me['categories']);
			$_SESSION['heskprivileges'] = $me['heskprivileges'];
            hesk_setPermissionGroupAccess($me['id']);
		}

        $_SESSION['signature'] = $me['signature'];

		// Users online
		if ($hesk_settings['online'])
		{
			require(HESK_PATH . 'inc/users_online.inc.php');
            hesk_initOnline($_SESSION['id']);
		}

        return true;
    }

} // END hesk_isLoggedIn()

function hesk_setPermissionGroupAccess($user_id) {
    global $hesk_settings;

    // Set permissions obtained via permission groups
    $permission_group_categories_rs = hesk_dbQuery("SELECT DISTINCT `category_id` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_group_categories`
        WHERE `group_id` IN (
            SELECT `group_id` 
            FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_group_members` 
            WHERE `user_id` = ".intval($user_id).
        ")");
    while ($row = hesk_dbFetchAssoc($permission_group_categories_rs)) {
        $_SESSION['categories'][] = $row['category_id'];
    }
    $_SESSION['categories'] = array_unique($_SESSION['categories']);
    $permission_group_features_rs = hesk_dbQuery("SELECT DISTINCT `feature` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_group_features`
        WHERE `group_id` IN (
            SELECT `group_id` 
            FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_group_members` 
            WHERE `user_id` = ".intval($user_id).
        ")");
    $_SESSION['heskprivileges'] = explode(',', $_SESSION['heskprivileges']);
    while ($row = hesk_dbFetchAssoc($permission_group_features_rs)) {
        $_SESSION['heskprivileges'][] = $row['feature'];
    }
    $_SESSION['heskprivileges'] = implode(',', array_unique($_SESSION['heskprivileges']));
}


function hesk_Pass2Hash($plaintext) {
    // This is a LEGACY function, only used to check and update legacy passwords
    // Use hesk_password_hash/hesk_password_verify functions instead!
    $majorsalt  = '';
    $len = strlen($plaintext);
    for ($i=0;$i<$len;$i++)
    {
        $majorsalt .= sha1(substr($plaintext,$i,1));
    }
    $corehash = sha1($majorsalt);
    return $corehash;
} // END hesk_Pass2Hash()


function hesk_formatDate($dt, $from_database=true)
{
    $dt=hesk_date($dt, $from_database);
	$dt=str_replace(' ','<br />',$dt);
    return $dt;
} // End hesk_formatDate()


function hesk_jsString($str)
{
    $str  = addslashes($str);
    $str  = str_replace('<br />' , '' , $str);
    $from = array("/\r\n|\n|\r/", '/\<a href="mailto\:([^"]*)"\>([^\<]*)\<\/a\>/i', '/\<a href="([^"]*)" target="_blank"\>([^\<]*)\<\/a\>/i');
    $to   = array("\\r\\n' + \r\n'", "$1", "$1");
    return preg_replace($from,$to,$str);
} // END hesk_jsString()


function hesk_myOwnership($consider_collaborators = false)
{
    // Admins can see all tickets
    if ( ! empty($_SESSION['isadmin']) )
    {
        return '1';
    }

    // For staff, let's check permissions
    $can_view_unassigned = hesk_checkPermission('can_view_unassigned',0);
    $can_view_ass_others = hesk_checkPermission('can_view_ass_others',0);
    $can_view_ass_by     = hesk_checkPermission('can_view_ass_by', 0);

    // Can view all tickets, regrdless of ownership
    if ($can_view_unassigned == 1 && $can_view_ass_others == 1) {
        return '1';
    }

    $sql_ownership = '';

    // Can view assigned to me + unassigned
    if ($can_view_unassigned == 1 && $can_view_ass_others == 0 && $can_view_ass_by == 0) {
        $sql_ownership .= " `owner` IN ('0', '" . intval($_SESSION['id']) . "') ";
    }

    // Can view assigned to me + unassigned + tickets I assigned to others
    elseif ($can_view_unassigned == 1 && $can_view_ass_others == 0 && $can_view_ass_by == 1) {
        $sql_ownership .= " (`owner` IN ('0', '" . intval($_SESSION['id']) . "') OR `assignedby` = " . intval($_SESSION['id']) . ") ";
    }

    // Can view assigned to me + assigned to others
    elseif ($can_view_unassigned == 0 && $can_view_ass_others == 1) {
        $sql_ownership .= " `owner` != 0 ";
    }

    // Can view assigned to me + tickets I assigned to others
    elseif ($can_view_unassigned == 0 && $can_view_ass_others == 0 && $can_view_ass_by == 1) {
        $sql_ownership .= " (`owner` = " . intval($_SESSION['id']) . " OR `assignedby` = " . intval($_SESSION['id']) . ") ";
    }

    // Can only view assigned to me
    elseif ($can_view_unassigned == 0 && $can_view_ass_others == 0 && $can_view_ass_by == 0) {
        $sql_ownership .= " `owner` = " . intval($_SESSION['id']) . " ";
    }

    // Must be an internal error
    else {
        die('Invalid view attempt (1)');
    }

    // Add a collaborator check for certain use cases
    if ($consider_collaborators) {
        return " ($sql_ownership OR `w`.`user_id`= " . intval($_SESSION['id']) . ") ";
    } else {
        return $sql_ownership;
    }

} // END hesk_myOwnership()


function hesk_myCategories($what='category')
{
    if ( ! empty($_SESSION['isadmin']) )
    {
        return '1';
    }
    else
    {
        return " `".hesk_dbEscape($what)."` IN ('" . implode("','", array_map('intval', $_SESSION['categories']) ) . "')";
    }
} // END hesk_myCategories()


function hesk_verifyStaffTicketAccess($trackingID, $ticket = null)
{
    global $hesk_settings, $hesklang;

    // Staff must be allowed to view tickets in general
    hesk_checkPermission('can_view_tickets');

    // Use an already-loaded ticket when possible to avoid another query
    if ( ! is_array($ticket) ||
         ! isset($ticket['id'], $ticket['trackid'], $ticket['category'], $ticket['owner'], $ticket['assignedby']) ||
         $ticket['trackid'] != $trackingID)
    {
        $res = hesk_dbQuery("SELECT `id`, `trackid`, `category`, `owner`, `assignedby` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `trackid`='".hesk_dbEscape($trackingID)."' LIMIT 1");

        if (hesk_dbNumRows($res) != 1)
        {
            hesk_error($hesklang['ticket_not_found']);
        }

        $ticket = hesk_dbFetchAssoc($res);
    }
    $collaborators = hesk_getTicketsCollaboratorIDs($ticket['id']);
    $am_I_collaborator = in_array($_SESSION['id'], $collaborators);

    // Permission to view this ticket based on owner / assignment / collaboration
    if ($ticket['owner'] && $ticket['owner'] != $_SESSION['id'] && ! hesk_checkPermission('can_view_ass_others', 0) && ! $am_I_collaborator)
    {
        // Maybe this user is allowed to view tickets he/she assigned?
        if ( ! hesk_checkPermission('can_view_ass_by', 0) || $ticket['assignedby'] != $_SESSION['id'])
        {
            hesk_error($hesklang['ycvtao']);
        }
    }

    if ( ! $ticket['owner'] && ! hesk_checkPermission('can_view_unassigned', 0) && ! $am_I_collaborator)
    {
        hesk_error($hesklang['ycovtay']);
    }

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

    $category = hesk_dbFetchAssoc($result);

    // Is this user allowed to view tickets inside this category?
    hesk_okCategory($category['id']);

    return $ticket;

} // END hesk_verifyStaffTicketAccess()


function hesk_okCategory($cat,$error=1,$user_isadmin=false,$user_cat=false)
{
	global $hesklang;

	/* Checking for current user or someone else? */
    if ($user_isadmin === false)
    {
		$user_isadmin = $_SESSION['isadmin'];
    }

    if ($user_cat === false)
    {
		$user_cat = $_SESSION['categories'];
    }

    /* Is admin? */
    if ($user_isadmin)
    {
        return true;
    }
    /* Staff with access? */
    elseif (in_array($cat,$user_cat))
    {
        return true;
    }
    /* No access */
    else
    {
        if ($error)
        {
        	hesk_error($hesklang['not_authorized_tickets']);
        }
        else
        {
        	return false;
        }
    }

} // END hesk_okCategory()


function hesk_checkPermission($feature,$showerror=1) {
	global $hesklang;

    /* Admins have full access to all features */
    if ( isset($_SESSION['isadmin']) && $_SESSION['isadmin'])
    {
        return true;
    }

    /* Check other staff for permissions */
    if ( isset($_SESSION['heskprivileges']) && strpos($_SESSION['heskprivileges'], $feature) === false)
    {
    	if ($showerror)
        {
        	hesk_error($hesklang['no_permission'].'<p>&nbsp;</p><p align="center"><a href="index.php">'.$hesklang['click_login'].'</a>');
        }
        else
        {
        	return false;
        }
    }
    else
    {
        return true;
    }

} // END hesk_checkPermission()


function hesk_purge_cache($type = '', $expire_after_seconds = 0)
{
    global $hesk_settings;

    if ( function_exists('opcache_reset') )
    {
        opcache_reset();
    }

    $cache_dir = dirname(dirname(__FILE__)).'/'.$hesk_settings['cache_dir'].'/';

    if ( ! is_dir($cache_dir))
    {
        return false;
    }

    switch ($type)
    {
        case 'export':
            $files = glob($cache_dir.'hesk_export_*', GLOB_NOSORT);
            break;
        case 'status':
            $files = glob($cache_dir.'status_*', GLOB_NOSORT);
            break;
        case 'priority':
            $files = glob($cache_dir.'priority_*', GLOB_NOSORT);
            break;
        case 'cf':
            $files = glob($cache_dir.'cf_*', GLOB_NOSORT);
            break;
        case 'kb':
            $files = array($cache_dir.'kb.cache.php');
            break;
        default:
            hesk_rrmdir(rtrim($cache_dir, '/'), true);
            return true;
    }

    if (is_array($files))
    {
        array_walk($files, 'hesk_unlink_callable', $expire_after_seconds);
    }

    return true;

} // END hesk_purge_cache()


function hesk_rrmdir($dir, $keep_top_level=false)
{
    if ( ! is_dir($dir)) {
        return false;
    }

    $files = $keep_top_level ? array_diff(scandir($dir), array('.','..','index.htm')) : array_diff(scandir($dir), array('.','..'));

    foreach ($files as $file)
    {
        (is_dir("$dir/$file")) ? hesk_rrmdir("$dir/$file") : @unlink("$dir/$file");
    }

    return $keep_top_level ? true : @rmdir($dir);

} // END hesk_rrmdir()


function hesk_deleteTicketsForCustomer($customer_id) {
    global $hesk_settings;

    $sql = "SELECT `tickets`.`id`, `tickets`.`trackid` 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 `ticket_to_customer`.`ticket_id` = `tickets`.`id`
        WHERE `ticket_to_customer`.`customer_id` = ".intval($customer_id)."
        AND `ticket_to_customer`.`customer_type` = 'REQUESTER'";

    $tickets_rs = hesk_dbQuery($sql);
    while ($ticket = hesk_dbFetchAssoc($tickets_rs)) {
        hesk_fullyDeleteTicket($ticket['id'], $ticket['trackid']);
    }
}


function hesk_fullyDeleteTicket($ticket_id, $ticket_trackid)
{
    global $hesk_settings;

    /* Delete attachment files */
    $res = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."attachments` WHERE `ticket_id`='".hesk_dbEscape($ticket_trackid)."'");
    if (hesk_dbNumRows($res))
    {
        $hesk_settings['server_path'] = dirname(dirname(__FILE__));

        while ($file = hesk_dbFetchAssoc($res))
        {
            hesk_unlink($hesk_settings['server_path'].'/'.$hesk_settings['attach_dir'].'/'.$file['saved_name']);
        }
    }

    /* Delete attachments info from the database */
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."attachments` WHERE `ticket_id`='".hesk_dbEscape($ticket_trackid)."'");

    /* Delete the ticket */
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `id`='".intval($ticket_id)."'");

    /* Delete replies to the ticket */
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` WHERE `replyto`='".intval($ticket_id)."'");

    /* Delete ticket notes */
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."notes` WHERE `ticket`='".intval($ticket_id)."'");

    /* Delete ticket reply drafts */
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."reply_drafts` WHERE `ticket`=".intval($ticket_id));

    /* Delete ticket/customer mappings */
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` WHERE `ticket_id` = ".intval($ticket_id));

    /* Delete bookmarks */
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."bookmarks` WHERE `ticket_id` = ".intval($ticket_id));

    /* Delete ticket/email ID mappings */
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."email_id_to_ticket` WHERE `ticket_id` = ".intval($ticket_id));

    return true;
}


function hesk_isTicketBookmarked($ticket_id, $user_id)
{
    global $hesk_settings, $hesklang, $hesk_db_link;

    $result = hesk_dbQuery('SELECT `id` FROM `'.hesk_dbEscape($hesk_settings['db_pfix']).'bookmarks` WHERE `ticket_id`='.intval($ticket_id).' AND `user_id`='.intval($user_id).' LIMIT 1');

    return hesk_dbNumRows($result);
} // END hesk_isTicketBookmarked()


function hesk_json_exit($status = 'Error', $data = '') {
    $json_data = [
        'status' => $status,
        'data'   => $data,
    ];
    echo json_encode($json_data);
    exit;
} // END hesk_json_exit()


function hesk_mergeCustomers($merge_these, $merge_into)
{
    global $hesk_settings, $hesklang, $hesk_db_link;

    // Target customer must not be in the "merge these" list
    if ( in_array($merge_into, $merge_these) ) {
        $merge_these = array_diff($merge_these, array( $merge_into ) );
    }

    // At least 1 customer needs to be merged with target customer
    if ( count($merge_these) < 1 ) {
        $_SESSION['error'] = $hesklang['merge_more_error'];
        return false;
    }

    $merge_into = intval($merge_into);

    // Make sure target customer exists
    $res = hesk_dbQuery("SELECT `id` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` WHERE `id`={$merge_into} LIMIT 1");
    if (hesk_dbNumRows($res) != 1) {
        $_SESSION['error'] = $hesklang['merge_target_error'];
        return false;
    }

    foreach ($merge_these as $this_id)
    {
        // Validate ID
        if ( is_array($this_id) ) {
            continue;
        }
        $this_id = intval($this_id) or hesk_error($hesklang['id_not_valid']);

        // Update customer tickets to new customer
        hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` SET `customer_id`={$merge_into} WHERE `customer_id`={$this_id}");

        // Migrate ticket replies to new customer
        hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` SET `customer_id`={$merge_into} WHERE `customer_id`={$this_id}");

        // Remove old customer information
        hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` WHERE `id`={$this_id}");
    }

    return true;
} // END hesk_mergeCustomers()


function hesk_getUserIdsWithAccessToFeatureAndCategory($feature = null, $category = null)
{
    global $hesk_settings;

    // Need to provide at least one of the two
    if ($feature === null && $category === null) {
        return [];
    }

    $access_parts = [];

    if ($feature !== null) {
        $feature = hesk_dbEscape($feature);
        $access_parts[] = "(
            FIND_IN_SET('{$feature}', `user`.`heskprivileges`) > 0
            OR EXISTS (
                SELECT 1
                FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_group_members` AS `member_feature`
                INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_group_features` AS `feature`
                    ON `member_feature`.`group_id` = `feature`.`group_id`
                WHERE `user`.`id` = `member_feature`.`user_id`
                    AND `feature`.`feature` = '{$feature}'
            )
        )";
    }

    if ($category !== null) {
        $category = intval($category);
        $access_parts[] = "(
            FIND_IN_SET('{$category}', `user`.`categories`) > 0
            OR EXISTS (
                SELECT 1
                FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_group_members` AS `member_category`
                INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_group_categories` AS `category`
                    ON `member_category`.`group_id` = `category`.`group_id`
                WHERE `user`.`id` = `member_category`.`user_id`
                    AND `category`.`category_id` = {$category}
            )
        )";
    }

    $access_sql = implode(' AND ', $access_parts);

    $users_rs = hesk_dbQuery("SELECT `user`.`id`
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."users` AS `user`
        WHERE `active` = 1 AND (`isadmin` = '1' OR ({$access_sql}))");
    $users = [];
    while ($row = hesk_dbFetchAssoc($users_rs)) {
        $users[] = intval($row['id']);
    }

    return $users;
} // END hesk_getUserIdsWithAccessToFeatureAndCategory()


function hesk_getCategoriesForUser($user_id)
{
    global $hesk_settings;

    $categories = [];
    $sanitized_user_id = intval($user_id);
    $base_categories_rs = hesk_dbQuery("SELECT `categories` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."users` WHERE `id`='{$sanitized_user_id}' AND `active` = 1 LIMIT 1");
    while ($row = hesk_dbFetchAssoc($base_categories_rs)) {
        $categories = explode(',', $row['categories']);
    }
    $categories = array_map(function($category) { return intval($category); }, $categories);

    $pg_categories = hesk_dbQuery("SELECT DISTINCT `category_id`
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_group_categories`
        WHERE `group_id` IN (
            SELECT `group_id`
            FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_group_members`
            WHERE `user_id` = {$sanitized_user_id}
        )");
    while ($row = hesk_dbFetchAssoc($pg_categories)) {
        $category_id = intval($row['category_id']);
        if (!in_array($category_id, $categories)) {
            $categories[] = $category_id;
        }
    }

    return $categories;
} // END hesk_getCategoriesForUser()


function hesk_getFeaturesForUser($user_id)
{
    global $hesk_settings;

    $features = [];
    $sanitized_user_id = intval($user_id);
    $base_features_rs = hesk_dbQuery("SELECT `heskprivileges` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."users` WHERE `id`='{$sanitized_user_id}' AND `active` = 1 LIMIT 1");
    while ($row = hesk_dbFetchAssoc($base_features_rs)) {
        $features = explode(',', $row['heskprivileges']);
    }

    $pg_features = hesk_dbQuery("SELECT DISTINCT `feature`
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_group_features`
        WHERE `group_id` IN (
            SELECT `group_id`
            FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_group_members`
            WHERE `user_id` = {$sanitized_user_id}
        )");
    while ($row = hesk_dbFetchAssoc($pg_features)) {
        if (!in_array($row['feature'], $features)) {
            $features[] = $row['feature'];
        }
    }

    return $features;
} // END hesk_getFeaturesForUser()
admin_settings_demo.inc.php
wget 'https://lists2.roe3.org/hesk/inc/admin_settings_demo.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

// Override sensitive settings in the demo mode
$hesk_settings['db_host']               = $hesklang['hdemo'];
$hesk_settings['db_name']               = $hesklang['hdemo'];
$hesk_settings['db_user']               = $hesklang['hdemo'];
$hesk_settings['db_pass']               = $hesklang['hdemo'];
$hesk_settings['db_pfix']               = $hesklang['hdemo'];
$hesk_settings['url_key']               = $hesklang['hdemo'];
$hesk_settings['smtp_host_name']        = $hesklang['hdemo'];
$hesk_settings['smtp_user']             = $hesklang['hdemo'];
$hesk_settings['smtp_password']         = $hesklang['hdemo'];
$hesk_settings['pop3_host_name']        = $hesklang['hdemo'];
$hesk_settings['pop3_user']             = $hesklang['hdemo'];
$hesk_settings['pop3_password']         = $hesklang['hdemo'];
$hesk_settings['imap_host_name']        = $hesklang['hdemo'];
$hesk_settings['imap_user']             = $hesklang['hdemo'];
$hesk_settings['imap_password']         = $hesklang['hdemo'];
$hesk_settings['recaptcha_public_key']  = $hesklang['hdemo'];
$hesk_settings['recaptcha_private_key'] = $hesklang['hdemo'];
admin_settings_language.inc.php
wget 'https://lists2.roe3.org/hesk/inc/admin_settings_language.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

?>
<button type="button" class="btn btn--blue-border show_language" ripple="ripple">
<?php echo $hesklang['click_to_show_available_language']; ?> <div class="ripple--container"></div></button>
<div class="inner_content_lan">
    <div class="lan-msg"></div>
</div>
<input type="hidden" name="current_hesk_version" id="current_hesk_version" value="<?php echo $hesk_settings['hesk_version'];?>">
<input type="hidden" name="install_lan" id="install_lan" value="<?php echo $hesklang['install_lan']; ?>">
<input type="hidden" name="remove_lan" id="remove_lan" value="<?php echo $hesklang['remove_lan']; ?>">
<input type="hidden" name="upgrade_lan" id="upgrade_lan" value="<?php echo $hesklang['upgrade_lan']; ?>">

<input type="hidden" id="ajax_action" value="0">
<input type="hidden" id="hesk_ajax_token" value="<?php hesk_token_echo(); ?>">

<div class="append_lan_html"></div>
<script>
    var title_lan,description_lan,completed_lan,success_install_lan_txt,error_install_lan_txt,success_remove_lan_txt,error_remove_lan_txt,remove_default_language_error,no_language_availble;
    var upgrade_lan,success_upgrade_lan_txt,error_upgrade_lan_txt,success_txt,error_txt;
    var close_html = '<a href="javascript:;" class="close" data-dismiss="alert" aria-label="<?php echo $hesklang['close_button_text']; ?>">×</a>';
    title_lan = "<?php echo $hesklang['title_lan'] ?>";
    description_lan = "<?php echo $hesklang['description_lan'] ?>";
    completed_lan = "<?php echo $hesklang['completed_lan'] ?>";
    success_install_lan_txt = "<?php echo $hesklang['success_install_lan_txt']; ?>" + close_html;
    error_install_lan_txt = "<?php echo $hesklang['error_install_lan_txt'].". <a target='_blank' href='https://www.hesk.com/knowledgebase/?article=89'>".$hesklang['click_for_manual_step']."</a>" ?>" + close_html;
    success_remove_lan_txt = "<?php echo $hesklang['success_remove_lan_txt'] ?>" + close_html;
    error_remove_lan_txt = "<?php $hesklang['error_remove_lan_txt'] ?>" + close_html;
    remove_default_language_error = "<?php echo $hesklang['sm_error'].": ".$hesklang['remove_default_language_error'] ?>" + close_html;
    no_language_availble = "<?php echo $hesklang['sm_error'].": ".$hesklang['no_language_availble'] .". <a target='_blank' href='https://www.hesk.com/knowledgebase/?article=89'>".$hesklang['click_for_manual_step']."</a>" ?>" + close_html;
    upgrade_lan = "<?php echo $hesklang['upgrade_lan'] ?>";
    success_upgrade_lan_txt = "<?php echo $hesklang['success_upgrade_lan_txt']; ?>" + close_html;
    error_upgrade_lan_txt = "<?php echo $hesklang['error_upgrade_lan_txt'].". <a target='_blank' href='https://www.hesk.com/knowledgebase/?article=89'>".$hesklang['click_for_manual_step']."</a>" ?>" + close_html;
    success_txt = "<?php echo $hesklang['sm_success'].": "?>";
    error_txt = "<?php echo $hesklang['sm_error'].": "?>";

    var unsaved_action = 0;

    // Show available languages
    $("body").on("click",".show_language",function(){
        if($("#ajax_action").val() == "0"){
            $("#overlay_loader").fadeIn(300);
            $.ajax({
                url:"ajax/language_list.php",
                type: "post",
                dataType: 'json',
                data: {},
                success:function(result){
                    $("#overlay_loader").fadeOut(300);
                    //Append language layout
                    if(result.status == "Success"){
                        $(".append_lan_html").html("");
                        $(".append_lan_html").append(result.data);
                        $("#ajax_action").val("1");
                        $(".show_language").addClass("d-none");
                        handleError();
                    }else{
                        $(".lan-msg").html(no_language_availble);
                        $(".lan-msg").addClass("error-msg");
                    }
                }
            });
        }
    });

    // Install a Language
    $("body").on("click",".install_language",function(){

        <?php if (defined('HESK_DEMO')): ?>
            $(".lan-msg").removeClass("warning-msg");
            $(".lan-msg").html("").fadeOut(300);
            $(".lan-msg").html("<?php echo str_replace('"', '\"', $hesklang['sdemo']); ?>");
            $(".lan-msg").addClass("warning-msg").fadeIn(300);
            return false;
        <?php endif; ?>

        $("#overlay_loader").fadeIn(300);
        var tag = $(this).attr("data-tag");
        var title = $(this).attr("data-description");
        var tl = $(this).attr("data-title");
        $.ajax({
            url:"ajax/language_download.php",
            type: "post",
            dataType: 'json',
            data: {action: "install",tag:$(this).attr("data-tag"),version:$(this).attr("data-version"),token:$("#hesk_ajax_token").val()},
            success:function(result){
                $("#overlay_loader").fadeOut(300);
                $(".lan-msg").removeClass("success-msg");
                $(".lan-msg").removeClass("error-msg");
                $(".lan-msg").html("");
                if(result.status == "Success"){
                    $(".lan-msg").html(success_txt+tl+" "+success_install_lan_txt);
                    $(".lan-msg").addClass("success-msg").fadeIn(300);
                    //Show/hide install remove button
                    $("."+tag+"_install").removeClass('d-inline-flex').addClass('d-none');
                    $("."+tag+"_remove").removeClass('d-none').addClass('d-inline-flex');
                    $("."+tag+"_upgrade").removeClass('d-none').addClass('d-inline-flex');;
                    //Auto enable multiple languages checkbox
                    /*if ($('input[name=s_can_sel_lang]').is(':not(:checked)')){
                        $('input[name=s_can_sel_lang]').prop("checked",true);
                    }*/
                    //Append install language to select option
                    $('select[name=s_language]').next().next('ul').append('<li data-option="' + tag+'|'+title+ '">' + title + '</li>');
                    $('select[name=s_language]').append('<option value="' + tag+'|'+title+ '">' + title + '</option>');
                }
                if(result.status == "Error"){
                    $(".lan-msg").html(error_txt+tl+" "+error_install_lan_txt);
                    $(".lan-msg").addClass("error-msg").fadeIn(300);
                }
            }
        });
    });
    // Remove Language
    $("body").on("click",".remove_language",function(){

        <?php if (defined('HESK_DEMO')): ?>
            $(".lan-msg").removeClass("warning-msg");
            $(".lan-msg").html("").fadeOut(300);
            $(".lan-msg").html("<?php echo str_replace('"', '\"', $hesklang['sdemo']); ?>");
            $(".lan-msg").addClass("warning-msg").fadeIn(300);
            return false;
        <?php endif; ?>

        var tag = $(this).attr("data-tag");
        var title = $(this).attr("data-description");
        var tl = $(this).attr("data-title");
        var selected_lan = $('select[name=s_language] option:selected').text();
        //Checked for default language
        if(selected_lan == title){
            $(".lan-msg").html(remove_default_language_error);
            $(".lan-msg").addClass("error-msg");
            return false;
        }

        $("#overlay_loader").fadeIn(300);
        
        $.ajax({
            url:"ajax/language_download.php",
            type: "post",
            dataType: 'json',
            data: {action: "remove",tag:tag,token:$("#hesk_ajax_token").val()},
            success:function(result){
                $("#overlay_loader").fadeOut(300);
                $(".lan-msg").removeClass("success-msg");
                $(".lan-msg").removeClass("error-msg");
                $(".lan-msg").html("");
                if(result.status == "Success"){
                    $(".lan-msg").html(success_txt+tl+" "+success_remove_lan_txt);
                    $(".lan-msg").addClass("success-msg").fadeIn(300);
                    //Show/hide install remove button
                    $("."+tag+"_install").removeClass('d-none').addClass("d-inline-flex");
                    $("."+tag+"_remove").addClass("d-none").removeClass("d-inline-flex");
                    $("."+tag+"_upgrade").addClass("d-none").removeClass("d-inline-flex");
                    //Delete remove language from select option
                    $('select[name=s_language]').next().next('ul').find('li[data-option="'+tag+'|'+title+'"]').remove();
                    $('select[name=s_language]').find('option[value="'+tag+'|'+title+'"]').remove();
                }
                if(result.status == "Error"){
                    $(".lan-msg").html(error_txt+tl+" "+error_remove_lan_txt);
                    $(".lan-msg").addClass("error-msg").fadeIn(300);
                }
                //Auto disable multiple languages checkbox
                /*var k = 0;
                $( ".remove_language" ).each(function( index ) {
                    if($(this).css("display") == "inline-flex"){
                        k = k + 1;
                    }
                });
                if(k==1){
                    $('input[name=s_can_sel_lang]').prop("checked",false);
                }*/
                //Auto enable multiple languages checkbox
            }
        });
    });
    //Upgrade Language
    $("body").on("click",".upgrade_language",function(){

        <?php if (defined('HESK_DEMO')): ?>
            $(".lan-msg").removeClass("warning-msg");
            $(".lan-msg").html("").fadeOut(300);
            $(".lan-msg").html("<?php echo str_replace('"', '\"', $hesklang['sdemo']); ?>");
            $(".lan-msg").addClass("warning-msg").fadeIn(300);
            return false;
        <?php endif; ?>

        $("#overlay_loader").fadeIn(300);
        var tag = $(this).attr("data-tag");
        var title = $(this).attr("data-description");
        var tl = $(this).attr("data-title");
        $.ajax({
            url:"ajax/language_download.php",
            type: "post",
            dataType: 'json',
            data: {action: "upgrade",tag:$(this).attr("data-tag"),version:$(this).attr("data-version"),token:$("#hesk_ajax_token").val()},
            success:function(result){
                $("#overlay_loader").fadeOut(300);
                $(".lan-msg").removeClass("success-msg");
                $(".lan-msg").removeClass("error-msg");
                $(".lan-msg").html("");
                if(result.status == "Success"){
                    $(".lan-msg").html(success_txt+tl+" "+success_upgrade_lan_txt);
                    $(".lan-msg").addClass("success-msg").fadeIn(300);
                }
                if(result.status == "Error"){
                    $(".lan-msg").html(error_txt+tl+" "+error_upgrade_lan_txt);
                    $(".lan-msg").addClass("error-msg").fadeIn(300);
                }
            }
        });
    });

    $("body").on("click",".close",function(){
        handleError();
    })

    function handleError(){
        setTimeout(function(){
            $(".lan-msg").html("");
            $(".lan-msg").removeClass("success-msg");
            $(".lan-msg").removeClass("error-msg");
            $(".lan-msg").removeClass("warning-msg");
        });
    }

    <?php
    /*
    $(document).on('change', ':input,select,textarea,:checkbox,:radio', function(){
        unsaved_action = 1;
    });

    $(window).on('beforeunload', function() {
        if(unsaved_action == 1){
            return false;
        }
    });
    */
    ?>
</script>

admin_settings_status.inc.php
wget 'https://lists2.roe3.org/hesk/inc/admin_settings_status.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

?>
<div class="settings__status">
    <h3><?php echo $hesklang['check_status']; ?></h3>
    <ul class="settings__status_list">
        <li>
            <div class="list--name"><?php echo $hesklang['v']; ?></div>
            <div class="list--status">
                <?php echo $hesk_settings['hesk_version']; ?>
                <?php
                if ($hesk_settings['check_updates']) {
                    $latest = hesk_checkVersion();

                    if ($latest === true) {
                        echo '<br><span class="text-success">' . $hesklang['hud'] . '</span> ';
                    } elseif ($latest != -1) {
                        // Is this a beta/dev version?
                        if (strpos($hesk_settings['hesk_version'], 'beta') || strpos($hesk_settings['hesk_version'], 'dev') || strpos($hesk_settings['hesk_version'], 'RC')) {
                            echo '<br><span class="text-warning">' . $hesklang['beta'] . '</span> '; ?><br><a href="https://www.hesk.com/update.php?v=<?php echo $hesk_settings['hesk_version']; ?>" target="_blank"><?php echo $hesklang['check4updates']; ?></a><?php
                        } else {
                            echo '<br><span class="text-warning text-bold">' . $hesklang['hnw'] . '</span> '; ?><br><a href="https://www.hesk.com/update.php?v=<?php echo $hesk_settings['hesk_version']; ?>" target="_blank"><?php echo $hesklang['getup']; ?></a><?php
                        }
                    } else {
                        ?><br><a href="https://www.hesk.com/update.php?v=<?php echo $hesk_settings['hesk_version']; ?>" target="_blank"><?php echo $hesklang['check4updates']; ?></a><?php
                    }
                } else {
                    ?><br><a href="https://www.hesk.com/update.php?v=<?php echo $hesk_settings['hesk_version']; ?>" target="_blank"><?php echo $hesklang['check4updates']; ?></a><?php
                }
                ?>
            </div>
        </li>
        <li>
            <div class="list--name"><?php echo $hesklang['hlic']; ?></div>
            <div class="list--status"><?php "\x47".chr(536870912>>23)."D\132\x7c\60".chr(973078528>>23)."\141".chr(067).chr(0101)."\163".".\x60\x5b\x77"."!\x7d".chr(0173)."\137"."%\75\152\x41\116\x66";if(!file_exists(dirname(dirname(__FILE__))."\x2f\x68\x65\x73\153"."_".chr(905969664>>23)."\x69\143"."e\156\x73".chr(847249408>>23)."\56\160\x68\160")){echo"\x3c\x73\x70".chr(813694976>>23).chr(922746880>>23)."\x20\x63".chr(905969664>>23)."\x61\163"."s\75\x22\164".chr(847249408>>23).chr(1006632960>>23)."\164"."-w".chr(0141)."\162".chr(0156)."\151"."ng\x20\x74".chr(847249408>>23)."\x78\164".chr(377487360>>23)."bol\x64\x22\76".$hesklang["\x68\154"."i\x63".chr(0137)."f\x72\x65\145"]."\x3c\x2f\x73"."p\141\156".chr(076)."\74\142\162".chr(076)."\x3c\x61\x20\150\x72\x65\x66\x3d\x22\150"."t\x74\x70\x73\72"."//".chr(0167)."ww\56\x68".chr(847249408>>23)."\x73\153".chr(385875968>>23)."\143\x6f\x6d"."/\147"."et".chr(057)."\x68"."es".chr(897581056>>23)."\63\x2d"."lic\x65\x6e"."s\145\x2d".chr(0163)."e".chr(973078528>>23)."\164\151".chr(0156)."\x67\163\x22\x20\x74"."a\x72\147".chr(0145).chr(0164)."\x3d\x22\137\x62"."l\141".chr(922746880>>23)."\153\x22".chr(076).$hesklang["\x68\x6c\x69\x63".chr(796917760>>23)."\142\x75"."yl"]."\x3c"."/".chr(0141)."\x3e";}else{echo"\x3c\163\x76"."g\x20\143\x6c\141".chr(0163).chr(964689920>>23)."\x3d\x22\x69".chr(0143)."\157"."n\x20".chr(880803840>>23)."c\157".chr(0156).chr(377487360>>23)."a\156\157"."n\x79\155"."iz".chr(847249408>>23)."\x20\x69\143"."o\x6e\x2d"."succ".chr(0145)."s".chr(0163)."\x22\76"."\xa\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x75\x73\145\x20\170"."li".chr(0156)."\x6b".":".chr(872415232>>23).chr(0162)."\145".chr(0146)."=\x22".HESK_PATH."\x69\x6d\147".chr(057)."\163\x70\162\x69\x74".chr(0145).chr(056)."\163\x76".chr(0147)."\43"."i\x63".chr(931135488>>23)."\x6e".chr(055)."a".chr(922746880>>23)."\157".chr(922746880>>23)."\x79".chr(0155)."\151\172"."e\x22\x3e\x3c\x2f\x75\x73".chr(847249408>>23)."\76".chr(109051904>>23)."\xa\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20".chr(074).chr(057)."\163\166\x67".">\xd\xa\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\74\163"."p\141\156\x20"."c\154\x61\163\163\75\x22\164".chr(847249408>>23)."x\164".chr(377487360>>23)."\x73\165\143\143\x65".chr(0163)."\163\x22".">".$hesklang["\x68\x6c\151"."c\x5f".chr(939524096>>23)."\x61"."i\x64"]."\x3c".chr(057)."\x73\x70\141".chr(922746880>>23).chr(076);}"\x77\73"."p".chr(0166)."g".chr(847249408>>23)."u\x47\x7e\110"."ZT".chr(587202560>>23)."\107".chr(067)."\x78\145\143\x2a\x3d\x37".chr(075)."$\x56"; ?></div>
        </li>
        <li>
            <div class="list--name"><?php echo $hesklang['phpv']; ?></div>
            <div class="list--status"><?php echo defined('HESK_DEMO') ? $hesklang['hdemo'] : PHP_VERSION . ' ' . (function_exists('mysqli_connect') ? '(MySQLi)' : '(MySQL)'); ?></div>
        </li>
        <li>
            <div class="list--name"><?php echo $hesklang['mysqlv']; ?></div>
            <div class="list--status"><?php echo defined('HESK_DEMO') ? $hesklang['hdemo'] : hesk_dbResult( hesk_dbQuery('SELECT VERSION() AS version') ); ?></div>
        </li>
        <li>
            <div class="list--name">/hesk_settings.inc.php</div>
            <div class="list--status">
                <?php
                if (is_writable(HESK_PATH . 'hesk_settings.inc.php')) {
                    $enable_save_settings = 1;
                    echo '<span class="text-success">'.$hesklang['exists'].'</span>, <span class="text-success">'.$hesklang['writable'].'</span>';
                } else {
                    echo '<span class="text-success">'.$hesklang['exists'].'</span><br><span class="text-danger">'.$hesklang['not_writable'].'</span></div></li><li><div style="text-align:justify">'.$hesklang['e_settings'];
                }
                ?>
            </div>
        </li>
        <li>
            <div class="list--name">/<?php echo $hesk_settings['attach_dir']; ?></div>
            <div class="list--status">
                <?php
                if (is_dir(HESK_PATH . $hesk_settings['attach_dir'])) {
                    echo '<span class="text-success">'.$hesklang['exists'].'</span>, ';
                    if (is_writable(HESK_PATH . $hesk_settings['attach_dir'])) {
                        $enable_use_attachments = 1;
                        echo '<span class="text-success">'.$hesklang['writable'].'</span>';
                    } else {
                        echo '<br><span class="text-danger">'.$hesklang['not_writable'].'</span></div></li><li><div style="text-align:justify">'.$hesklang['e_attdir'];
                    }
                } else {
                    echo '<span class="text-danger">'.$hesklang['no_exists'].'</span><br><span class="text-danger">'.$hesklang['not_writable'].'</span></div></li><li><div style="text-align:justify">'.$hesklang['e_attdir'];
                }
                ?>
            </div>
        </li>
        <li>
            <div class="list--name">/<?php echo $hesk_settings['cache_dir']; ?></div>
            <div class="list--status">
                <?php
                if (is_dir(HESK_PATH . $hesk_settings['cache_dir'])) {
                    echo '<span class="text-success">'.$hesklang['exists'].'</span>, ';
                    if (is_writable(HESK_PATH . $hesk_settings['cache_dir'])) {
                        $enable_use_attachments = 1;
                        echo '<span class="text-success">'.$hesklang['writable'].'</span>';
                    } else {
                        echo '<br><span class="text-danger">'.$hesklang['not_writable'].'</span></div></li><li><div style="text-align:justify">'.$hesklang['e_cdir'];
                    }
                } else {
                    echo '<span class="text-danger">'.$hesklang['no_exists'].'</span><br><span class="text-danger">'.$hesklang['not_writable'].'</span></div></li><li><div style="text-align:justify">'.$hesklang['e_cdir'];
                }
                ?>
            </div>
        </li>
    </ul>
</div>
<?php

function hesk_checkVersion()
{
    global $hesk_settings;

    if ($latest = hesk_getLatestVersion() )
    {
        if ( strlen($latest) > 12 )
        {
            return -1;
        }
        elseif ($latest == $hesk_settings['hesk_version'])
        {
            return true;
        }
        else
        {
            return $latest;
        }
    }
    else
    {
        return -1;
    }

} // END hesk_checkVersion()


function hesk_getLatestVersion()
{
    global $hesk_settings;

    // Do we have a cached version file?
    if ( file_exists(HESK_PATH . $hesk_settings['cache_dir'] . '/__latest.txt') )
    {
        if ( preg_match('/^(\d+)\|([\d.]+)+$/', @file_get_contents(HESK_PATH . $hesk_settings['cache_dir'] . '/__latest.txt'), $matches) && (time() - intval($matches[1])) < 3600  )
        {
            return $matches[2];
        }
    }

    // No cached file or older than 3600 seconds, try to get an update
    $hesk_version_url = 'http://hesk.com/version';

    // Try using cURL
    if ( function_exists('curl_init') )
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $hesk_version_url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 6);
        $latest = curl_exec($ch);
        hesk_curl_close($ch);
        return hesk_cacheLatestVersion($latest);
    }

    // Try using a simple PHP function instead
    if ($latest = @file_get_contents($hesk_version_url) )
    {
        return hesk_cacheLatestVersion($latest);
    }

    // Can't check automatically, will need a manual check
    return false;

} // END hesk_getLatestVersion()


function hesk_cacheLatestVersion($latest)
{
    global $hesk_settings;

    @file_put_contents(HESK_PATH . $hesk_settings['cache_dir'] . '/__latest.txt', time() . '|' . $latest);

    return $latest;

} // END hesk_cacheLatestVersion()
assignment_search.inc.php
wget 'https://lists2.roe3.org/hesk/inc/assignment_search.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

if ( ! isset($can_view_ass_others)) {
    $can_view_ass_others = hesk_checkPermission('can_view_ass_others',0);
    $can_view_ass_by = hesk_checkPermission('can_view_ass_by',0);
    $can_view_unassigned = hesk_checkPermission('can_view_unassigned',0);
}

// Assignment
// -> SELF
$s_my[$fid] = empty($_GET['s_my']) ? 0 : 1;
// -> OTHERS
$s_ot[$fid] = empty($_GET['s_ot']) ? 0 : 1;
// -> UNASSIGNED
$s_un[$fid] = empty($_GET['s_un']) ? 0 : 1;

// Remember the original assignment filter selection. Quick links override
// $s_my/$s_ot/$s_un below for the active view, but the main
// "Filtered tickets" tab should still point back to the user's
// selected assignment filters.
$hesk_assignment_filter_selected = isset($_GET['s_my']) || isset($_GET['s_ot']) || isset($_GET['s_un']);
$s_my_filter[$fid] = $hesk_assignment_filter_selected ? $s_my[$fid] : 1;
$s_ot_filter[$fid] = $hesk_assignment_filter_selected ? $s_ot[$fid] : 1;
$s_un_filter[$fid] = $hesk_assignment_filter_selected ? $s_un[$fid] : 1;

// -> Collaborate
$s_co[$fid] = 1;

// Overwrite by quick links? Ignore for ticket searches
if ( ! isset($is_quick_link))
{
    $is_quick_link = false;
}
// Quick link: assigned to me
elseif ($is_quick_link == 'my')
{
    $s_my[$fid] = 1;
    $s_ot[$fid] = 0;
    $s_un[$fid] = 0;
    $s_co[$fid] = 0;
}
// Quick link: tickets where I am collaborator
elseif ($is_quick_link == 'cbm')
{
    $s_my[$fid] = 0;
    $s_ot[$fid] = 0;
    $s_un[$fid] = 0;
    $s_co[$fid] = 1;
}
// Quick link: assigned to other
elseif ($is_quick_link == 'ot')
{
    $s_my[$fid] = 0;
    $s_ot[$fid] = 1;
    $s_un[$fid] = 0;
    $s_co[$fid] = 0;
}
// Quick link: unassigned
elseif ($is_quick_link == 'un')
{
    $s_my[$fid] = 0;
    $s_ot[$fid] = 0;
    $s_un[$fid] = 1;
    $s_co[$fid] = 0;
}

// Is assignment selection the same as a quick link?
if ($is_quick_link === false && (($can_view_ass_others || $can_view_ass_by) || $can_view_unassigned))
{
    if ($s_my[$fid] == 1 && $s_ot[$fid] == 0 && $s_un[$fid] == 0 && $s_co[$fid] == 1)
    {
        $is_quick_link = 'my';
        $s_co[$fid] = 0;
    }
    elseif ($s_my[$fid] == 0 && $s_ot[$fid] == 1 && $s_un[$fid] == 0 && $s_co[$fid] == 1)
    {
        $is_quick_link = 'ot';
        $s_co[$fid] = 0;
    }
    elseif ($s_my[$fid] == 0 && $s_ot[$fid] == 0 && $s_un[$fid] == 1 && $s_co[$fid] == 1)
    {
        $is_quick_link = 'un';
        $s_co[$fid] = 0;
    }
}

// -> Setup SQL based on selected ticket assignments

/* Make sure at least one is chosen */
if ( ! $s_my[$fid] && ! $s_ot[$fid] && ! $s_un[$fid] && $is_quick_link != 'cbm')
{
	$s_my[$fid] = 1;
	$s_ot[$fid] = 1;
	$s_un[$fid] = 1;
	$s_co[$fid] = 1;
	if (!defined('MAIN_PAGE'))
	{
		hesk_show_notice($hesklang['e_nose']);
	}
}

// Can view tickets assigned to others?
if ( ! $can_view_ass_others && ! $can_view_ass_by) {
    $s_ot[$fid] = 0;
}

// Can view unassigned tickets?
if ( ! $can_view_unassigned) {
    $s_un[$fid] = 0;
}

// Apply the same permission limits to the remembered main filter selection.
if ( ! $can_view_ass_others && ! $can_view_ass_by) {
    $s_ot_filter[$fid] = 0;
}

if ( ! $can_view_unassigned) {
    $s_un_filter[$fid] = 0;
}

if ( ! $s_my_filter[$fid] && ! $s_ot_filter[$fid] && ! $s_un_filter[$fid])
{
    $s_my_filter[$fid] = 1;
    $s_ot_filter[$fid] = ($can_view_ass_others || $can_view_ass_by) ? 1 : 0;
    $s_un_filter[$fid] = $can_view_unassigned ? 1 : 0;
}

// If a saved filter, stale quick link, or manually edited URL selected only
// assignment options the user can no longer view, fall back to a valid
// default instead of leaving the ticket list without an assignment filter.
if ( ! $s_my[$fid] && ! $s_ot[$fid] && ! $s_un[$fid] && $is_quick_link != 'cbm')
{
    $is_quick_link = false;
    $s_my[$fid] = 1;
    $s_ot[$fid] = ($can_view_ass_others || $can_view_ass_by) ? 1 : 0;
    $s_un[$fid] = $can_view_unassigned ? 1 : 0;
    $s_co[$fid] = 1;

    if (!defined('MAIN_PAGE'))
    {
        hesk_show_notice($hesklang['e_nose']);
    }
}

$my_user_id = intval($_SESSION['id']);
$sql_assignment = '';

// Show all
if ($s_my[$fid] == 1 && $s_ot[$fid] == 1 && $s_un[$fid] == 1 && $s_co[$fid] == 1) {
    if ($can_view_ass_others) {
        $sql_assignment .= "";
    } elseif ($can_view_ass_by) {
        if ($is_quick_link === false) {
            $sql_assignment .= " AND ( `owner` IN (0, {$my_user_id}) OR `assignedby` = {$my_user_id} ) ";
        } else {
            $sql_assignment .= " AND ( `owner` IN (0, {$my_user_id}) OR `assignedby` = {$my_user_id} OR `w`.`user_id` = {$my_user_id} ) ";
        }
    } else {
        die('Invalid view attempt (2)');
    }
}

// Assigned to me
if ($s_my[$fid] == 1 && $s_ot[$fid] == 0 && $s_un[$fid] == 0 && $s_co[$fid] == 0) {
    $sql_assignment .= " AND `owner` = {$my_user_id} ";
}

// Assigned to me + Assigned to others
if ($s_my[$fid] == 1 && $s_ot[$fid] == 1 && $s_un[$fid] == 0 && $s_co[$fid] == 0) {
    if ($can_view_ass_others) {
        $sql_assignment .= " AND `owner` <> 0 ";
    } elseif ($can_view_ass_by) {
        $sql_assignment .= " AND ( `owner` = {$my_user_id} OR `assignedby` = {$my_user_id} ) ";
    } else {
        die('Invalid view attempt (3)');
    }
}

// Assigned to me + Unassigned
if ($s_my[$fid] == 1 && $s_ot[$fid] == 0 && $s_un[$fid] == 1 && $s_co[$fid] == 0) {
    $sql_assignment .= " AND `owner` IN (0, {$my_user_id}) ";
}

// Assigned to me + Collaborator
if ($s_my[$fid] == 1 && $s_ot[$fid] == 0 && $s_un[$fid] == 0 && $s_co[$fid] == 1) {
    $sql_assignment .= " AND `owner` = {$my_user_id} ";
}

// Assigned to me + Assigned to others + Unassigned
if ($s_my[$fid] == 1 && $s_ot[$fid] == 1 && $s_un[$fid] == 1 && $s_co[$fid] == 0) {
    if ($can_view_ass_others) {
        $sql_assignment .= "";
    } elseif ($can_view_ass_by) {
        $sql_assignment .= " AND ( `owner` IN (0, {$my_user_id}) OR `assignedby` = {$my_user_id} ) ";
    } else {
        die('Invalid view attempt (4)');
    }
}

// Assigned to me + Assigned to others + Collaborator
if ($s_my[$fid] == 1 && $s_ot[$fid] == 1 && $s_un[$fid] == 0 && $s_co[$fid] == 1) {
    if ($can_view_ass_others) {
        $sql_assignment .= " AND `owner` <> 0 ";
    } elseif ($can_view_ass_by) {
        $sql_assignment .= " AND ( `owner` = {$my_user_id} OR ( `owner` <> 0 AND `assignedby` = {$my_user_id} ) ) ";
    } else {
        die('Invalid view attempt (5)');
    }
}

// Assigned to me + Unassigned + Collaborator
if ($s_my[$fid] == 1 && $s_ot[$fid] == 0 && $s_un[$fid] == 1 && $s_co[$fid] == 1) {
    $sql_assignment .= " AND `owner` IN (0, {$my_user_id}) ";
}

// Assigned to me + Assigned to others + Unassigned + Collaborator
if ($s_my[$fid] == 1 && $s_ot[$fid] == 1 && $s_un[$fid] == 1 && $s_co[$fid] == 1) {
    if ($can_view_ass_others) {
        $sql_assignment .= " AND ( `owner` <> 99999 OR `w`.`user_id` = {$my_user_id} ) ";
    } elseif ($can_view_ass_by) {
        if ($is_quick_link === false) {
            $sql_assignment .= " AND ( `owner` IN (0, {$my_user_id}) OR `assignedby` = {$my_user_id} ) ";
        } else {
            $sql_assignment .= " AND ( `owner` IN (0, {$my_user_id}) OR `assignedby` = {$my_user_id} OR `w`.`user_id` = {$my_user_id} ) ";
        }
    } else {
        die('Invalid view attempt (6)');
    }
}

// Assigned to others (to others by me)
if ($s_my[$fid] == 0 && $s_ot[$fid] == 1 && $s_un[$fid] == 0 && $s_co[$fid] == 0) {
    $sql_assignment .= " AND (`owner` NOT IN (0, {$my_user_id}) ";

    if ( ! $can_view_ass_others) {
        if ($can_view_ass_by) {
            $sql_assignment .= " AND `assignedby` = {$my_user_id} ";
        } else {
            $sql_assignment .= " AND `w`.`user_id` = {$my_user_id} ";
        }
    }

    $sql_assignment .= " ) ";
}

// Assigned to others + Unassigned
if ($s_my[$fid] == 0 && $s_ot[$fid] == 1 && $s_un[$fid] == 1 && $s_co[$fid] == 0) {
    if ($can_view_ass_others) {
        $sql_assignment .= " AND `owner` <> {$my_user_id} ";
    } elseif ($can_view_ass_by) {
        $sql_assignment .= " AND ( `owner` = 0 OR ( `owner` <> {$my_user_id} AND `assignedby` = {$my_user_id} ) ) ";
    } else {
        die('Invalid view attempt (7)');
    }
}

// Assigned to others + Collaborator
if ($s_my[$fid] == 0 && $s_ot[$fid] == 1 && $s_un[$fid] == 0 && $s_co[$fid] == 1) {
    if ($can_view_ass_others) {
        $sql_assignment .= " AND `owner` NOT IN (0, {$my_user_id}) ";
    } elseif ($can_view_ass_by) {
        $sql_assignment .= " AND ( `owner` NOT IN (0, {$my_user_id}) AND `assignedby` = {$my_user_id} ) ";
    } else {
        die('Invalid view attempt (8)');
    }
}

// Assigned to others + Unassigned + Collaborator
if ($s_my[$fid] == 0 && $s_ot[$fid] == 1 && $s_un[$fid] == 1 && $s_co[$fid] == 1) {
    if ($can_view_ass_others) {
        $sql_assignment .= " AND `owner` <> {$my_user_id} ";
    } elseif ($can_view_ass_by) {
        $sql_assignment .= " AND ( `owner` = 0 OR ( `owner` <> {$my_user_id} AND `assignedby` = {$my_user_id} ) ) ";
    } else {
        die('Invalid view attempt (9)');
    }
}

// Unassigned
if ($s_my[$fid] == 0 && $s_ot[$fid] == 0 && $s_un[$fid] == 1 && $s_co[$fid] == 0) {
    $sql_assignment .= " AND `owner` = 0 ";
}

// Unassigned + Collaborator
if ($s_my[$fid] == 0 && $s_ot[$fid] == 0 && $s_un[$fid] == 1 && $s_co[$fid] == 1) {
    $sql_assignment .= " AND `owner` = 0 ";
}

// Collaborator
if ($s_my[$fid] == 0 && $s_ot[$fid] == 0 && $s_un[$fid] == 0 && $s_co[$fid] == 1) {
    $sql_assignment .= " AND `w`.`user_id` = {$my_user_id} ";
}

$sql .= $sql_assignment;

attachments.inc.php
wget 'https://lists2.roe3.org/hesk/inc/attachments.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

/***************************
Function hesk_uploadFiles()
***************************/
function hesk_uploadFile($i)
{
	global $hesk_settings, $hesklang, $trackingID, $hesk_error_buffer;

	/* Return if name is empty */
	if (empty($_FILES['attachment']['name'][$i])) {return '';}

    /* Parse the name */
	$file_realname = hesk_cleanFileName($_FILES['attachment']['name'][$i]);

	/* Check file extension */
	$ext = strtolower(strrchr($file_realname, "."));
	if ( ! in_array($ext,$hesk_settings['attachments']['allowed_types']))
	{
        return hesk_fileError(sprintf($hesklang['type_not_allowed'], $ext, $file_realname));
	}

	/* Check file size */
	if ($_FILES['attachment']['size'][$i] > $hesk_settings['attachments']['max_size'])
	{
	    return hesk_fileError(sprintf($hesklang['file_too_large'], $file_realname));
	}
	else
	{
	    $file_size = $_FILES['attachment']['size'][$i];
	}

	/* Generate a random file name */
    $file_name = hesk_generateAttachmentName($file_realname, $ext, $trackingID);

    // Does the temporary file exist? If not, probably server-side configuration limits have been reached
    // Uncomment this for debugging purposes
    /*
    if ( ! file_exists($_FILES['attachment']['tmp_name'][$i]) )
    {
		return hesk_fileError($hesklang['fnuscphp']);
    }
    */

	/* If upload was successful let's create the headers */
	if ( ! move_uploaded_file($_FILES['attachment']['tmp_name'][$i], dirname(dirname(__FILE__)).'/'.$hesk_settings['attach_dir'].'/'.$file_name))
	{
	    return hesk_fileError($hesklang['cannot_move_tmp']);
	}

	$info = array(
	    'saved_name'=> $file_name,
	    'real_name' => $file_realname,
	    'size'      => $file_size
	);

	return $info;
} // End hesk_uploadFile()

function hesk_generateAttachmentName($file_realname, $ext, $tracking_id = '') {
    /* Generate a random file name */
    $useChars='AEUYBDGHJLMNPQRSTVWXZ123456789';
    $tmp = uniqid();
    for($j=1;$j<10;$j++) {
        $tmp .= $useChars[mt_rand(0,29)];
    }

    if (defined('KB') || $tracking_id === '') {
        return substr(md5($tmp . $file_realname), 0, 200) . $ext;
    }

    return substr($tracking_id . '_' . md5($tmp . $file_realname), 0, 200) . $ext;
}

function hesk_generateTempAttachmentRandomHex($bytes = 32)
{
    if (function_exists('random_bytes')) {
        try {
            return bin2hex(random_bytes($bytes));
        } catch (Exception $e) {
            // Fall through to older generators below.
        }
    }

    if (function_exists('openssl_random_pseudo_bytes')) {
        $strong = false;
        $random = openssl_random_pseudo_bytes($bytes, $strong);
        if ($random !== false && $strong) {
            return bin2hex($random);
        }
    }

    if (function_exists('mcrypt_create_iv') && defined('MCRYPT_DEV_URANDOM')) {
        $random = mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM);
        if ($random !== false && strlen($random) === $bytes) {
            return bin2hex($random);
        }
    }

    $random = '';
    while (strlen($random) < $bytes) {
        $seed = uniqid('', true) . microtime(true) . mt_rand() . mt_rand();
        if (function_exists('memory_get_usage')) {
            $seed .= memory_get_usage();
        }
        $random .= hash('sha256', $seed . $random, true);
    }

    return bin2hex(substr($random, 0, $bytes));
} // End hesk_generateTempAttachmentRandomHex()

function hesk_getTempAttachmentSigningSecret()
{
    if ( ! isset($_SESSION) || ! is_array($_SESSION)) {
        return false;
    }

    if (empty($_SESSION['temp_attachment_secret']) || ! preg_match('/^[a-f0-9]{64}$/', $_SESSION['temp_attachment_secret'])) {
        $_SESSION['temp_attachment_secret'] = hesk_generateTempAttachmentRandomHex(32);
    }

    return $_SESSION['temp_attachment_secret'];
} // End hesk_getTempAttachmentSigningSecret()

function hesk_getTempAttachmentSignature($unique_id)
{
    $secret = hesk_getTempAttachmentSigningSecret();

    return $secret === false ? false : hash_hmac('sha256', 'hesk-temp-attachment-v1|' . (string) $unique_id, $secret);
} // End hesk_getTempAttachmentSignature()

function hesk_signTempAttachmentKey($unique_id)
{
    $signature = hesk_getTempAttachmentSignature($unique_id);

    return $signature === false ? false : (string) $unique_id . ':' . $signature;
} // End hesk_signTempAttachmentKey()

function hesk_validateTempAttachmentKey($file_key)
{
    $parts = explode(':', (string) $file_key, 2);

    if (count($parts) !== 2) {
        return false;
    }

    list($unique_id, $signature) = $parts;

    if ($unique_id === '' || strlen($unique_id) > 255 || ! preg_match('/^[a-f0-9]{64}$/', $signature)) {
        return false;
    }

    $expected_signature = hesk_getTempAttachmentSignature($unique_id);

    if ($expected_signature === false || ! hash_equals($expected_signature, $signature)) {
        return false;
    }

    return $unique_id;
} // End hesk_validateTempAttachmentKey()

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

    /* Return if name is empty */
    if (empty($_FILES['attachment']['name'])) {
        return null;
    }

    /* Parse the name */
    $file_realname = hesk_cleanFileName($_FILES['attachment']['name']);

    /* Check file extension */
    $ext = strtolower(strrchr($file_realname, "."));
    if (!in_array($ext,$hesk_settings['attachments']['allowed_types'])) {
        return array(
            'status' => 'failure',
            'status_code' => 400,
            'message' => sprintf($hesklang['type_not_allowed'], $ext, $file_realname)
        );
    }

    /* Check file size */
    if ($_FILES['attachment']['size'] > $hesk_settings['attachments']['max_size']) {
        return array(
            'status' => 'failure',
            'status_code' => 400,
            'message' => sprintf($hesklang['file_too_large'], $file_realname)
        );
    } else {
        $file_size = $_FILES['attachment']['size'];
    }

    /* Check for potential attachment flooding */
    $ip = hesk_getClientIP();
    if (hesk_attachmentFloodingDetected($ip)) {
        return array(
            'status' => 'failure',
            'status_code' => 429,
            'message' => $hesklang['attachment_too_many_uploads']
        );
    }

    $file_name = hesk_generateAttachmentName($file_realname, $ext);

    // Does the temporary file exist? If not, probably server-side configuration limits have been reached
    // Uncomment this for debugging purposes
    /*
    if ( ! file_exists($_FILES['attachment']['tmp_name']) )
    {
		return hesk_fileError($hesklang['fnuscphp']);
    }
    */

    $hesk_settings['server_path'] = dirname(dirname(__FILE__)).'/'.$hesk_settings['attach_dir'].'/temp/';
    if (!is_dir($hesk_settings['server_path'])) {
        @mkdir($hesk_settings['server_path']);
        @file_put_contents($hesk_settings['server_path'].'index.htm', '');
    }

    /* If upload was successful let's create the headers */
    ob_start();
    if (!move_uploaded_file($_FILES['attachment']['tmp_name'], $hesk_settings['server_path'].$file_name))
    {
        ob_end_clean();
        return array(
            'status' => 'failure',
            'status_code' => 500,
            'message' => $hesklang['error'] . ': ' . $hesklang['cannot_move_tmp']
        );
    }
    ob_end_clean();

    // Generate a random ID to use when deleting temporary attachments
    $unique_id = hesk_generateTempAttachmentRandomHex(32);

    hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."temp_attachments` (`saved_name`, `unique_id`, `real_name`, `expires_at`, `size`)
    VALUES ('".hesk_dbEscape($file_name)."', '".hesk_dbEscape($unique_id)."', '".hesk_dbEscape($file_realname)."', NOW() + INTERVAL 3 HOUR, ".intval($file_size).")");

    // Increment limits used for IP
    hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."temp_attachments_limits` (`ip`,`upload_count`)
        VALUES ('".hesk_dbEscape(hesk_getClientIP())."', 1) ON DUPLICATE KEY UPDATE `upload_count` = `upload_count` + 1");

    $file_key = hesk_signTempAttachmentKey($unique_id);
    if ($file_key === false) {
        hesk_deleteTempAttachmentByUniqueId($unique_id, true);
        return array(
            'status' => 'failure',
            'status_code' => 500,
            'message' => $hesklang['error'] . ': ' . $hesklang['cannot_move_tmp']
        );
    }

    $info = array(
        'status' => 'success',
        'status_code' => 200,
        'file_key'=> $file_key
    );

    return $info;
} // End hesk_uploadTempFile

function hesk_attachmentFloodingDetected($ip) {
    global $hesk_settings;

    // Reset counters for any IPs that haven't uploaded in an hour
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."temp_attachments_limits` WHERE `last_upload_at` < (NOW() - INTERVAL 1 HOUR)");

    $res = hesk_dbQuery("SELECT `upload_count` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."temp_attachments_limits` WHERE `ip` = '".hesk_dbEscape($ip)."'");
    if (hesk_dbNumRows($res) < 1) {
        return false;
    }
    $row = hesk_dbFetchAssoc($res);

    // Change "100" to whatever max amount is appropriate.
    return $row['upload_count'] > 100;
}

function hesk_fileError($error)
{
	global $hesk_settings, $hesklang, $trackingID;
    global $hesk_error_buffer;

	$hesk_error_buffer['attachments'] = $error;

	return false;
} // End hesk_fileError()


function hesk_removeAttachments($attachments)
{
	global $hesk_settings, $hesklang;

	$hesk_settings['server_path'] = dirname(dirname(__FILE__)).'/'.$hesk_settings['attach_dir'].'/';

	foreach ($attachments as $myatt)
	{
		hesk_unlink($hesk_settings['server_path'].$myatt['saved_name']);
	}

	return true;
} // End hesk_removeAttachments()

function hesk_removeExpiredTempAttachments() {
    global $hesk_settings;

    // 1. Grab temp attachments that are expired
    $res = hesk_dbQuery("SELECT `att_id`, `saved_name` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."temp_attachments`
        WHERE `expires_at` < NOW()");

    $hesk_settings['server_path'] = dirname(dirname(__FILE__)).'/'.$hesk_settings['attach_dir'].'/temp/';

    while ($row = hesk_dbFetchAssoc($res)) {
        hesk_unlink($hesk_settings['server_path'].$row['saved_name']);
        hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."temp_attachments` WHERE `att_id` = ".intval($row['att_id']));
    }
}

function hesk_migrateTempAttachments($attachments, $tracking_id = '') {
    global $hesk_settings;

    $moved_attachments = array();
    foreach ($attachments as $myatt) {
        hesk_deleteTempAttachment($myatt['file_key']);

        $old_name = $myatt['saved_name'];
        $myatt['saved_name'] = ($tracking_id !== '') ? "{$tracking_id}_{$old_name}" : $old_name;
        hesk_moveAttachment($old_name, $myatt['saved_name']);

        $moved_attachments[] = $myatt;
    }

    // Reset limits for the IP
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."temp_attachments_limits` WHERE `ip` = '".hesk_dbEscape(hesk_getClientIP())."'");

    return $moved_attachments;
}

function hesk_moveAttachment($old_name, $new_name) {
    global $hesk_settings;

    $hesk_settings['temp_server_path'] = dirname(dirname(__FILE__)).'/'.$hesk_settings['attach_dir'].'/temp/';
    $hesk_settings['server_path'] = dirname(dirname(__FILE__)).'/'.$hesk_settings['attach_dir'].'/';

    hesk_rename($hesk_settings['temp_server_path'].$old_name, $hesk_settings['server_path'].$new_name);
}

function hesk_deleteTempAttachmentByUniqueId($unique_id, $delete_file = false) {
    global $hesk_settings;

    $hesk_settings['server_path'] = dirname(dirname(__FILE__)).'/'.$hesk_settings['attach_dir'].'/temp/';

    $res = hesk_dbQuery("SELECT `att_id`, `saved_name` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."temp_attachments`
        WHERE `unique_id` = '".hesk_dbEscape($unique_id)."'");

    if ($row = hesk_dbFetchAssoc($res)) {
        if ($delete_file) {
            hesk_unlink($hesk_settings['server_path'].$row['saved_name']);
        }
        hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."temp_attachments` WHERE `att_id` = ".intval($row['att_id']));
    }
}

function hesk_deleteTempAttachment($file_key, $delete_file = false) {
    $unique_id = hesk_validateTempAttachmentKey($file_key);

    if ($unique_id === false) {
        return;
    }

    hesk_deleteTempAttachmentByUniqueId($unique_id, $delete_file);
}

function hesk_getTemporaryAttachment($file_key) {
    global $hesk_settings;

    $unique_id = hesk_validateTempAttachmentKey($file_key);

    if ($unique_id === false) {
        return NULL;
    }

    $rs = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "temp_attachments` WHERE `unique_id` = '" . hesk_dbEscape($unique_id) . "'");
    if (hesk_dbNumRows($rs) == 0) {
        return NULL;
    }
    $row = hesk_dbFetchAssoc($rs);

    $hesk_settings['server_path'] = dirname(dirname(__FILE__)).'/'.$hesk_settings['attach_dir'].'/temp/';
    if (!file_exists($hesk_settings['server_path'].$row['saved_name'])) {
        // Not deleting the file itself because it, well, doesn't exist.
        hesk_deleteTempAttachmentByUniqueId($unique_id);
        return null;
    }

    $info = array(
        'saved_name' => $row['saved_name'],
        'real_name' => $row['real_name'],
        'size' => $row['size'],
        'file_key' => hesk_signTempAttachmentKey($row['unique_id'])
    );

    return $info;
}

//region Dropzone
function build_dropzone_markup($admin = false, $id = 'filedrop', $startingId = 1, $show_file_limits = true) {
    global $hesklang, $hesk_settings;

    $directory_separator = $admin ? '../' : '';
    echo '<div class="dropzone dz-click-'.$id.'" id="' . $id . '">
        <div class="fallback">
            <input type="hidden" name="use-legacy-attachments" value="1">';
    for ($i = $startingId; $i <= $hesk_settings['attachments']['max_number']; $i++) {
        $cls = ($i == 1 && isset($_SESSION['iserror']) && in_array('attachments', $_SESSION['iserror'])) ? ' class="isError" ' : '';
        echo '<input type="file" name="attachment[' . $i . ']" size="50" ' . $cls . ' /><br />';
    }
    echo '</div>
    </div>
    <div class="btn btn-full fileinput-button filedropbutton-' . $id . ' dz-click-'.$id.'">' . $hesklang['attachment_add_files'] . '</div>';
    if ($show_file_limits) {
        echo '<a class="link" href="' . $directory_separator . 'file_limits.php" target="_blank"
       onclick="Javascript:hesk_window(\'' . $directory_separator . 'file_limits.php\',250,500);return false;">'. $hesklang['ful'] . '</a>';
    }

    output_attachment_id_holder_container($id);
}

function display_dropzone_field($url, $is_admin, $id = 'filedrop', $max_files_override = -1) {
    global $hesk_settings, $hesklang;

    // Built-in admin pages use their own staff-authenticated upload endpoint.
    // Customer/public pages keep using the public customer upload endpoint passed by the caller.
    if ($is_admin && $url === HESK_PATH . 'upload_attachment.php') {
        $url = 'admin_upload_attachment.php';
    }

    output_dropzone_window();

    $acceptedFiles = implode(',', $hesk_settings['attachments']['allowed_types']);
    $size = round($hesk_settings['attachments']['max_size'] / 1048576, 8, PHP_ROUND_HALF_UP);
    $max_files = $max_files_override > -1 ? $max_files_override : $hesk_settings['attachments']['max_number'];
    $attachment_token = hesk_token_echo(0);

    // Let's define this function we may need if it's not defined already
    if ( ! isset($hesk_settings['HeskWithout_defined'])) {
        echo "<script>const HeskWithout = (list, rejectedItem) => list.filter((item) => item !== rejectedItem).map((item) => item);</script>";
        $hesk_settings['HeskWithout_defined'] = true;
    }

    // Dropzone auto-discovery is being removed in v6.  As such, autodiscovery is disabled here should we want to
    // upgrade in the future.
    echo "
    <script>
    var pleaseWaitMessage = ".json_encode($hesklang['please_wait']).";
    Dropzone.autoDiscover = false;
    var dropzone{$id} = new Dropzone('#{$id}', {
        paramName: 'attachment',
        url: '{$url}',
        parallelUploads: {$max_files},
        maxFiles: {$max_files},
        acceptedFiles: ".json_encode($acceptedFiles).",
        maxFilesize: {$size}, // MB
        dictDefaultMessage: ".json_encode($hesklang['attachment_viewer_message']).",
        dictFallbackMessage: '',
        dictInvalidFileType: ".json_encode($hesklang['attachment_invalid_type_message']).",
        dictResponseError: ".json_encode(defined('HESK_DEMO') ? $hesklang['ddemo'] : $hesklang['attachment_upload_error']).",
        dictFileTooBig: ".json_encode($hesklang['attachment_too_large']).",
        dictCancelUpload: ".json_encode($hesklang['attachment_cancel']).",
        dictCancelUploadConfirmation: ".json_encode($hesklang['attachment_confirm_cancel']).",
        dictRemoveFile: ".json_encode($hesklang['attachment_remove']).",
        dictMaxFilesExceeded: ".json_encode($hesklang['attachment_max_exceeded']).",
        previewTemplate: $('#previews').html(),
        clickable: '.dz-click-".$id."',
        uploadMultiple: false,
        params: {
            token: ".json_encode($attachment_token)."
        }
    });
    
    dropzone{$id}.on('success', function(file, response) {
        var jsonResponse = response;

        if (typeof response === 'string') {
            try {
                jsonResponse = JSON.parse(response);
            } catch (e) {
                dropzone{$id}.emit('uploadprogress', file, 0);
                dropzone{$id}.files = HeskWithout(dropzone{$id}.files, file);
                dropzone{$id}.emit('error', file, ".json_encode($hesklang['attachment_upload_error']).");
                dropzone{$id}.emit('complete', file);
                return;
            }
        }

        // console.log(JSON.stringify(jsonResponse, null, 4));

        if(jsonResponse && jsonResponse.hasOwnProperty('status') && jsonResponse['status'] == 'failure'){
            // Upload request was completed, but something failed on the server-side
            dropzone{$id}.emit('uploadprogress', file, 0);
            dropzone{$id}.files = HeskWithout(dropzone{$id}.files, file);
            dropzone{$id}.emit('error', file, jsonResponse['message']);
            dropzone{$id}.emit('complete', file);
        } else if (jsonResponse && jsonResponse.hasOwnProperty('file_key')) {
            // The response will only be a JSON object holding the saved and real name
            outputAttachmentIdHolder(jsonResponse['file_key'], '".$id."');

            // Add the database id to the file
            file['databaseResponse'] = jsonResponse['file_key'];
        } else {
            dropzone{$id}.emit('uploadprogress', file, 0);
            dropzone{$id}.files = HeskWithout(dropzone{$id}.files, file);
            dropzone{$id}.emit('error', file, ".json_encode($hesklang['attachment_upload_error']).");
            dropzone{$id}.emit('complete', file);
        }
    });
    dropzone{$id}.on('addedfile', function() {
        var numberOfFiles = $('#" . $id . " .file-row').length;

        var disabled = false;
        if (numberOfFiles >= " . $max_files . ") {
            disabled = true;
        }

        $('." . $id . "button-" . $id . "').attr('disabled', disabled);
    });
    dropzone{$id}.on('removedfile', function(file) {
        if (file.beingRetried) {
            return;
        }
    
        // Remove the attachment from the database and the filesystem.
        removeAttachment(".$id.", file['databaseResponse'], ".($is_admin ? "true" : "false").", ".json_encode($attachment_token).");

        var numberOfFiles = $('#" . $id . " .file-row').length;

        var disabled = false;
        if (numberOfFiles >= " . $max_files . ") {
            disabled = true;
        }
        $('." . $id . "button-" . $id . "').attr('disabled', disabled);
        
        dropzone{$id}.getRejectedFiles().forEach(function(file) {
            file.beingRetried = true;
            dropzone{$id}.removeFile(file);
            file.status = undefined;
            file.accepted = undefined;
            file.beingRetried = false;
            dropzone{$id}.addFile(file);
        });
    });
    dropzone{$id}.on('queuecomplete', function() {
        $('input[type=\"submit\"]').attr('disabled', false);
        if(typeof attachmentQueueComplete === 'function') {
            attachmentQueueComplete();
        }
    });
    dropzone{$id}.on('processing', function() {
        $('input[type=\"submit\"]').attr('disabled', true);
        if(typeof attachmentQueueProcessing === 'function') {
            attachmentQueueProcessing();
        }
    });
    dropzone{$id}.on('uploadprogress', function(file, percentage) {
        $(file.previewTemplate).find('#percentage').text(percentage + '%');
    });
    dropzone{$id}.on('error', function(file, message) {
        $(file.previewTemplate).addClass('alert-danger');
        
        var actualMessage = message.title + ': ' + message.message;
        if (!message.message) {
            actualMessage = message;
        }
        
        $(file.previewElement).addClass('dz-error').find('[data-dz-errormessage]').text(actualMessage);
        if(typeof attachmentError === 'function') {
            attachmentError();
        }
    });
    </script>
    ";

}

function dropzone_display_existing_files($files, $dropzone_id = 'filedrop') {
    foreach ($files as $file) {
        dropzone_display_existing_file($file['real_name'], $file['size'], $file['file_key'], $dropzone_id);
    }
}

function dropzone_display_existing_file($name, $size, $file_key, $dropzone_id = 'filedrop') {
    $uniqid = uniqid();
    $successPayload = json_encode(array(
        'file_key' => $file_key
    ));
    echo "
    <script>
    var tempFile{$uniqid} = { 
        name: ".json_encode($name).", 
        size: ".$size."
    };
    tempFile{$uniqid}.accepted = true;
    tempFile{$uniqid}.status = Dropzone.SUCCESS;
    dropzone{$dropzone_id}.files.push(tempFile{$uniqid});
    dropzone{$dropzone_id}._updateMaxFilesReachedClass();
    dropzone{$dropzone_id}.emit('addedfile', tempFile{$uniqid});
    dropzone{$dropzone_id}.emit('complete', tempFile{$uniqid});
    dropzone{$dropzone_id}.emit('success', tempFile{$uniqid}, '{$successPayload}');
    dropzone{$dropzone_id}.emit('uploadprogress', tempFile{$uniqid}, 100);
    </script>
    ";
}

function output_dropzone_window() {
    echo '
    <div id="previews" style="display:none">
        <div id="template" class="file-row">
            <!-- This is used as the file preview template -->
            <div class="attachment-row">
                <div class="name-size-delete">
                    <div class="name-size">
                        <div class="name">
                            <p class="name" data-dz-name></p>
                        </div>
                        <div class="size">
                            (<span data-dz-size></span>)
                        </div>
                    </div>
                    <div class="delete-button">
                        <svg class="icon icon-delete" data-dz-remove>
                            <use xlink:href="'.HESK_PATH.'img/sprite.svg#icon-delete"></use>
                        </svg>
                    </div>
                </div>
                <div class="upload-progress">
                    <div style="border: 1px solid #d4d6e3; width: 100%; height: 19px">
                        <div style="font-size: 1px; height: 17px; width: 0px; border: none; background-color: green" data-dz-uploadprogress>
                        </div>
                    </div>
                </div>
            </div>
            <div class="error">
                <strong class="error text-danger" data-dz-errormessage></strong>
            </div>
        </div>
    </div>';
}

function output_attachment_id_holder_container($id) {
    echo '<div id="attachment-holder-' . $id . '" class="hide"></div>';
}

//endregion
common.inc.php
wget 'https://lists2.roe3.org/hesk/inc/common.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');} 

#error_reporting(E_ALL);

// Load Composer dependencies
require_once(HESK_PATH . 'vendor/autoload.php');

/*
 * If code is executed from CLI, don't force SSL
 * else set correct Content-Type header
 */
if (defined('NO_HTTP_HEADER'))
{
    $hesk_settings['force_ssl'] = false;
}
else
{
	header('Content-Type: text/html; charset=utf-8');

    // Don't allow HESK to be loaded in a frame on third party domains
    if ($hesk_settings['x_frame_opt'])
    {
        header('X-Frame-Options: SAMEORIGIN');
    }
}

// Set backslash options
if (version_compare(PHP_VERSION, '5.4.0', '<') && get_magic_quotes_gpc())
{
	define('HESK_SLASH',false);
}
else
{
	define('HESK_SLASH',true);
}

// Define some constants for backward-compatibility
if ( ! defined('ENT_SUBSTITUTE'))
{
	define('ENT_SUBSTITUTE', 0);
}
if ( ! defined('ENT_XHTML'))
{
	define('ENT_XHTML', 0);
}

// Is this is a SSL connection?
if ((isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https'))
{
    define('HESK_SSL', true);

    // Set for Nginx special cases
    $_SERVER['HTTPS'] = 'on';

    // Use https-only cookies
    @ini_set('session.cookie_secure', 1);
}
else
{
    // Force redirect?
    if ($hesk_settings['force_ssl'])
    {
        $hesk_url_parts = parse_url($hesk_settings['hesk_url']);
        $hesk_host = isset($hesk_url_parts['host']) ? $hesk_url_parts['host'] : $_SERVER['SERVER_NAME'];
        $hesk_port = isset($hesk_url_parts['port']) ? ':' . intval($hesk_url_parts['port']) : '';
        $hesk_request_uri = isset($_SERVER['REQUEST_URI']) ? str_replace(array("\r", "\n"), '', $_SERVER['REQUEST_URI']) : '/';

        if (strpos($hesk_request_uri, '/') !== 0)
        {
            $hesk_request_uri = '/';
        }

        header('HTTP/1.1 301 Moved Permanently');
        header('Location: https://' . $hesk_host . $hesk_port . $hesk_request_uri);
        exit();
    }

    define('HESK_SSL', false);
}

// Prevents javascript XSS attacks aimed to steal the session ID
@ini_set('session.cookie_httponly', 1);

// **PREVENTING SESSION FIXATION**
// Session ID cannot be passed through URLs
@ini_set('session.use_only_cookies', 1);

// Load language file
hesk_getLanguage();

// Set timezone
hesk_setTimezone();

/*** FUNCTIONS ***/


function hesk_getClientIP()
{
    global $hesk_settings;

    // Already set? Just return it
    if (isset($hesk_settings['client_IP']))
    {
        return $hesk_settings['client_IP'];
    }

    // Empty client IP, for example when used in CLI (piping, cron jobs, ...)
    $hesk_settings['client_IP'] = '';

    // Server (environment) variables to loop through
    // the first valid one found will be returned as client IP
    // Uncomment those used on your server
    $server_client_IP_variables = array(
        // 'HTTP_CF_CONNECTING_IP',
        // 'HTTP_CLIENT_IP',
        // 'HTTP_X_REAL_IP',
        // 'HTTP_X_FORWARDED_FOR',
        // 'HTTP_X_FORWARDED',
        // 'HTTP_FORWARDED_FOR',
        // 'HTTP_FORWARDED',
        'REMOTE_ADDR',
    );

    // The first valid environment variable is our client IP
    foreach ($server_client_IP_variables as $server_client_IP_variable)
    {
        // Must be set
        if ( ! isset($_SERVER[$server_client_IP_variable]))
        {
            continue;
        }

        // Must be a valid IP
        if ( ! hesk_isValidIP($_SERVER[$server_client_IP_variable]))
        {
            continue;
        }

        // Bingo!
        $hesk_settings['client_IP'] = $_SERVER[$server_client_IP_variable];
        break;
    }

    return $hesk_settings['client_IP'];

} // END hesk_getClientIP()


function hesk_isValidIP($ip)
{
    // Use filter_var for PHP 5.2.0+
    if ( function_exists('filter_var') && filter_var($ip, FILTER_VALIDATE_IP) !== false )
    {
        return true;
    }

    // Use regex for PHP < 5.2.0

    // -> IPv4
    if ( preg_match('/^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$/', $ip) )
    {
        return true;
    }

    // -> IPv6
    if ( preg_match('/^[0-9A-Fa-f\:\.]+$/', $ip) )
    {
        return true;
    }

    // Not a valid IP
    return false;

} // END hesk_isValidIP()


function hesk_setcookie($name, $value, $expire=0, $path="", $httponly = true)
{
    global $hesk_settings;

    if ($value === null) {
        return true;
    }

    // PHP < 7.3 doesn't support the SameSite attribute, let's use a trick
    if (PHP_VERSION_ID < 70300)
    {
        setcookie($name, $value, $expire, $path . '; SameSite=' . $hesk_settings['samesite'], null, HESK_SSL, $httponly);
        return true;
    }

    setcookie($name, $value, array(
        'expires' => $expire,
        'path' => $path,
        'secure' => HESK_SSL,
        'httponly' => $httponly,
        'samesite' => $hesk_settings['samesite'],
    ));

    return true;
} // END hesk_setcookie()



function hesk_create_password_reset_token()
{
    return bin2hex(random_bytes(32));
} // END hesk_create_password_reset_token()


function hesk_clean_password_reset_token($token)
{
    $token = trim($token);

    return preg_match('/\A[a-fA-F0-9]{64}\z/', $token) ? strtolower($token) : '';
} // END hesk_clean_password_reset_token()


function hesk_hash_password_reset_token($token)
{
    return hash('sha256', $token);
} // END hesk_hash_password_reset_token()


function hesk_get_service_messages($page=null, $strict_page_check=false)
{
    global $hesk_settings, $hesklang, $hesk_db_link;

    if (empty($page)) {
        $page = 'home';
    }

    $res = hesk_dbQuery('SELECT `title`, `message`, `style` FROM `'.hesk_dbEscape($hesk_settings['db_pfix'])."service_messages`
        WHERE `type`='0'
        AND ".($strict_page_check ? "`location` LIKE '%".hesk_dbEscape($page)."%'" : "(" . ($page == 'home' ? '`location` IS NULL OR ' : '')."`location`='ALL' OR `location` LIKE '%".hesk_dbEscape($page)."%')")."
        AND (`language` IS NULL OR `language` LIKE '".hesk_dbEscape($hesk_settings['language'])."')
        ORDER BY `order` ASC"
    );
    $service_messages = array();
    while ($sm=hesk_dbFetchAssoc($res)) {
        $service_messages[] = $sm;
    }
    return $service_messages;
} // END hesk_get_service_messages()


function hesk_service_message($sm)
{
	switch ($sm['style'])
	{
		case 1:
			$style = "green";
			$adaRole = "status";
			break;
		case 2:
			$style = "blue";
            $adaRole = "status";
			break;
		case 3:
			$style = "orange";
            $adaRole = "alert";
			break;
		case 4:
			$style = "red";
            $adaRole = "alert";
			break;
		default:
			$style = "white";
            $adaRole = "log";
	}

	?>
    <div class="main__content notice-flash">
        <div role="<?php echo $adaRole; ?>" class="notification <?php echo $style; ?> browser-default">
            <p><b><?php echo $sm['title']; ?></b></p>
            <?php echo $sm['message']; ?>
        </div>
    </div>
	<?php
} // END hesk_service_message()


function hesk_isBannedIP($ip)
{
	global $hesk_settings, $hesklang, $hesk_db_link;

	$ip = ip2long($ip) or $ip = 0;

	// We need positive value of IP
	if ($ip < 0)
	{
		$ip += 4294967296;
	}
	elseif ($ip > 4294967296)
	{
		$ip = 4294967296;
	}

	$res = hesk_dbQuery("SELECT `id` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."banned_ips` WHERE {$ip} BETWEEN `ip_from` AND `ip_to` LIMIT 1");

	return ( hesk_dbNumRows($res) == 1 ) ? hesk_dbResult($res) : false;

} // END hesk_isBannedIP()


function hesk_isBannedEmail($email)
{
	global $hesk_settings, $hesklang, $hesk_db_link;

	$email = strtolower($email);

	$res = hesk_dbQuery("SELECT `id` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."banned_emails` WHERE `email` IN ('".hesk_dbEscape($email)."', '".hesk_dbEscape( substr($email, strrpos($email, "@") ) )."') LIMIT 1");

	return ( hesk_dbNumRows($res) == 1 ) ? hesk_dbResult($res) : false;

} // END hesk_isBannedEmail()


function hesk_isMutedEmail($email)
{
    global $hesk_settings, $hesklang, $hesk_db_link;

    $email = strtolower($email);

    $res = hesk_dbQuery("SELECT `id` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."muted_emails` WHERE `email` IN ('".hesk_dbEscape($email)."', '".hesk_dbEscape( substr($email, strrpos($email, "@") ) )."') LIMIT 1");

    return ( hesk_dbNumRows($res) == 1 ) ? hesk_dbResult($res) : false;

} // END hesk_isMutedEmail()


function hesk_clean_utf8($in)
{
    if ($in === null) {
        return '';
    }

	//reject overly long 2 byte sequences, as well as characters above U+10000 and replace with ?
	$in = preg_replace('/[\x00-\x08\x10\x0B\x0C\x0E-\x19\x7F]'.
	 '|[\x00-\x7F][\x80-\xBF]+'.
	 '|([\xC0\xC1]|[\xF0-\xFF])[\x80-\xBF]*'.
	 '|[\xC2-\xDF]((?![\x80-\xBF])|[\x80-\xBF]{2,})'.
	 '|[\xE0-\xEF](([\x80-\xBF](?![\x80-\xBF]))|(?![\x80-\xBF]{2})|[\x80-\xBF]{3,})/S',
	 '?', $in );

	//reject overly long 3 byte sequences and UTF-16 surrogates and replace with ?
	$in = preg_replace('/\xE0[\x80-\x9F][\x80-\xBF]'.
	 '|\xED[\xA0-\xBF][\x80-\xBF]/S','?', $in );

	return $in;
} // END hesk_clean_utf8()


function hesk_load_database_functions()
{
    // Already loaded?
    if (function_exists('hesk_dbQuery'))
    {
        return true;
    }
	// Preferrably use the MySQLi functions
	elseif ( function_exists('mysqli_connect') )
	{
		require(HESK_PATH . 'inc/database_mysqli.inc.php');
	}
	// Default to MySQL
	else
	{
		require(HESK_PATH . 'inc/database.inc.php');
	}
} // END hesk_load_database_functions()


function hesk_unlink($file, $older_than=0)
{
	return ( is_file($file) && ( ! $older_than || (time()-filectime($file)) > $older_than ) && @unlink($file) ) ? true : false;
} // END hesk_unlink()


function hesk_copy($old_path, $new_path) {
    return is_file($old_path) && @copy($old_path, $new_path);
} // END hesk_copy()


function hesk_rename($old_path, $new_path) {
    return is_file($old_path) && @rename($old_path, $new_path);
} // END hesk_rename()


function hesk_unlink_callable($file, $key, $older_than=0)
{
	return hesk_unlink($file, $older_than);
} // END hesk_unlink_callable()


function hesk_utf8_urldecode($in)
{
    if ($in === null) {
        return '';
    }
	$in = preg_replace("/%u([0-9a-f]{3,4})/i","&#x\\1;", urldecode($in));
	return hesk_html_entity_decode($in);
} // END hesk_utf8_urldecode


function hesk_SESSION($in, $default = '')
{
	if (is_array($in))
	{
		return isset($_SESSION[$in[0]][$in[1]]) && ! is_array(isset($_SESSION[$in[0]][$in[1]])) ? $_SESSION[$in[0]][$in[1]] : $default;
	}
	else
	{
		return isset($_SESSION[$in]) && ! is_array($_SESSION[$in]) ? $_SESSION[$in] : $default;
	}
} // END hesk_SESSION();

function hesk_SESSION_array($in, $default = []) {
    return isset($_SESSION[$in]) && is_array($_SESSION[$in]) ? $_SESSION[$in] : $default;
}


function hesk_COOKIE($in, $default = '')
{
	return isset($_COOKIE[$in]) && ! is_array($_COOKIE[$in]) ? $_COOKIE[$in] : $default;
} // END hesk_COOKIE();


function hesk_GET($in, $default = '')
{
	return isset($_GET[$in]) && ! is_array($_GET[$in]) ? $_GET[$in] : $default;
} // END hesk_GET()


function hesk_restricted_GET($in, $allowed_values, $default = '')
{
    if (!isset($_GET[$in]) || is_array($_GET[$in])) {
        return $default;
    }

    $value = $_GET[$in];
    return in_array($value, $allowed_values) ? $value : $default;

} // END hesk_restricted_GET()


function hesk_POST($in, $default = '')
{
	return isset($_POST[$in]) && ! is_array($_POST[$in]) ? $_POST[$in] : $default;
} // END hesk_POST()


function hesk_POST_array($in, $default = array() )
{
	return isset($_POST[$in]) && is_array($_POST[$in]) ? $_POST[$in] : $default;
} // END hesk_POST_array()


function hesk_REQUEST($in, $default = false)
{
	return isset($_GET[$in]) ? hesk_input( hesk_GET($in) ) : ( isset($_POST[$in]) ? hesk_input( hesk_POST($in) ) : $default );
} // END hesk_REQUEST()


function hesk_isREQUEST($in)
{
	return isset($_GET[$in]) || isset($_POST[$in]) ? true : false;
} // END hesk_isREQUEST()


function hesk_mb_substr($in, $start, $length)
{
    if ($in === null) {
        return '';
    }
	return function_exists('mb_substr') ? mb_substr($in, $start, $length, 'UTF-8') : substr($in, $start, $length);
} // END hesk_mb_substr()


function hesk_mb_strlen($in)
{
    if ($in === null) {
        return 0;
    }
	return function_exists('mb_strlen') ? mb_strlen($in, 'UTF-8') : strlen($in);
} // END hesk_mb_strlen()


function hesk_mb_strtolower($in)
{
    if ($in === null) {
        return '';
    }
	return function_exists('mb_strtolower') ? mb_strtolower($in, 'UTF-8') : strtolower($in);
} // END hesk_mb_strtolower()

function hesk_mb_strtoupper($in)
{
    if ($in === null) {
        return '';
    }
    return function_exists('mb_strtoupper') ? mb_strtoupper($in, 'UTF-8') : strtoupper($in);
} // END hesk_mb_strtolower()


function hesk_ucfirst($in)
{
    if ($in === null) {
        return '';
    }
	return function_exists('mb_convert_case') ? mb_convert_case($in, MB_CASE_TITLE, 'UTF-8') : ucfirst($in);
} // END hesk_mb_ucfirst()


function hesk_htmlspecialchars_decode($in)
{
    if ($in === null) {
        return '';
    }
	return str_replace( array('&amp;', '&lt;', '&gt;', '&quot;'), array('&', '<', '>', '"'), $in);
} // END hesk_htmlspecialchars_decode()


function hesk_html_entity_decode($in)
{
    if ($in === null) {
        return '';
    }
	return html_entity_decode($in, ENT_COMPAT | ENT_XHTML, 'UTF-8');
    #return html_entity_decode($in, ENT_COMPAT | ENT_XHTML, 'ISO-8859-1');
} // END hesk_html_entity_decode()


function hesk_htmlspecialchars($in)
{
    if ($in === null) {
        return '';
    }
	return htmlspecialchars($in, ENT_COMPAT | ENT_SUBSTITUTE | ENT_XHTML, 'UTF-8');
    #return htmlspecialchars($in, ENT_COMPAT | ENT_SUBSTITUTE | ENT_XHTML, 'ISO-8859-1');
} // END hesk_htmlspecialchars()


function hesk_htmlentities($in)
{
    if ($in === null) {
        return '';
    }
	return htmlentities($in, ENT_COMPAT | ENT_SUBSTITUTE | ENT_XHTML, 'UTF-8');
    #return htmlentities($in, ENT_COMPAT | ENT_SUBSTITUTE | ENT_XHTML, 'ISO-8859-1');
} // END hesk_htmlentities()


function hesk_slashJS($in)
{
    if ($in === null) {
        return '';
    }
	return str_replace( '\'', '\\\'', $in);
} // END hesk_slashJS()


function hesk_verifyEmailMatch($trackingID, $my_email = 0, $ticket_emails = [], $error = 1)
{
	global $hesk_settings, $hesklang, $hesk_db_link;

	$customer_accounts_enabled = ! empty($hesk_settings['customer_accounts']);
	$customer_account_access_required = $customer_accounts_enabled && ! empty($hesk_settings['customer_accounts_required']);
	$logged_in_customer = null;

	if ($customer_accounts_enabled)
	{
		if ( ! function_exists('hesk_isCustomerLoggedIn'))
		{
			require_once(HESK_PATH . 'inc/customer_accounts.inc.php');
		}

		$logged_in_customer = hesk_isCustomerLoggedIn($customer_account_access_required && $error);

		// Customer autologin may return true after setting the session;
		// fetch the actual customer context before checking ticket ownership.
		if ($logged_in_customer === true)
		{
			$logged_in_customer = hesk_isCustomerLoggedIn(false);
		}

		if (is_array($logged_in_customer))
		{
			$logged_in_customer_email = isset($logged_in_customer['email']) ? $logged_in_customer['email'] : '';
			$hesk_settings['e_param'] = '';
			$hesk_settings['e_query'] = '';
			$hesk_settings['e_email'] = $logged_in_customer_email;

			$res = hesk_dbQuery("SELECT 1
				FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` `tickets`
				INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` `ticket_customer`
					ON `tickets`.`id` = `ticket_customer`.`ticket_id`
				WHERE `tickets`.`trackid` = '".hesk_dbEscape($trackingID)."'
					AND `ticket_customer`.`customer_id` = ".intval($logged_in_customer['id'])."
				LIMIT 1");

			if (hesk_dbNumRows($res))
			{
				hesk_cleanBfAttempts();
				return true;
			}

			// If customer accounts are required, access must be tied to the logged-in
			// customer account. If accounts are optional, fall back to the legacy
			// email/tracking-ID rules below, but use the logged-in customer's email
			// instead of any email supplied by URL, POST, or cookie. This preserves
			// optional-account behavior without weakening required-account access.
			if ($customer_account_access_required)
			{
				if ($error)
				{
					hesk_setcookie('hesk_myemail', '');
					hesk_process_messages($hesklang['enmdb'], 'my_tickets.php');
				}

				return false;
			}

			$my_email = $logged_in_customer_email;
		}
		elseif ($customer_account_access_required)
		{
			return false;
		}
	}

	/* Email required to view ticket? */
	if ( ! $hesk_settings['email_view_ticket'])
    {
		$hesk_settings['e_param'] = '';
        $hesk_settings['e_query'] = '';
        $hesk_settings['e_email'] = '';
		return true;
    }

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

    // handle edge case: email is required to view tickets, but is not required to submit a ticket
    // if the ticket was submitted without an email, match an empty mail
    // if the ticket was submitted with an email, it must match
    if ($hesk_settings['require_email'] == 0) {
        if (in_array('', $ticket_emails)) {
            $hesk_settings['e_param'] = '';
            $hesk_settings['e_query'] = '';
            $hesk_settings['e_email'] = '';
            return true;
        } elseif (empty($my_email)) {
            return 'EMAIL_REQUIRED';
        } else {
            $hesk_settings['tmp_skip_redirect'] = true;
        }
    }

	/* Get email address */
	if ($my_email || is_array($logged_in_customer))
	{
		$hesk_settings['e_param'] = '&e=' . rawurlencode($my_email);
		$hesk_settings['e_query'] = '&amp;e=' . rawurlencode($my_email);
		$hesk_settings['e_email'] = $my_email;
	}
	else
	{
		$my_email = hesk_getCustomerEmail();
	}

	/* Get emails from ticket */
	if (empty($ticket_emails))
	{
		$res = hesk_dbQuery("SELECT `customers`.`email` AS `email`
            FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` `tickets`
            INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` `ticket_customer`
                ON `tickets`.`id` = `ticket_customer`.`ticket_id`
            INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` `customers`
                ON `ticket_customer`.`customer_id` = `customers`.`id` 
            WHERE `trackid`='".hesk_dbEscape($trackingID)."'");
        while ($row = hesk_dbFetchAssoc($res)) {
            $ticket_emails[] = $row['email'];
        }

        if (empty($ticket_emails))
        {
			hesk_process_messages($hesklang['ticket_not_found'],'ticket.php');
        }
	}

    /* Validate email */
    foreach ($ticket_emails as $email) {
        if (strtolower($my_email) === strtolower($email)) {
            /* Match, clean brute force attempts and return true */
            hesk_cleanBfAttempts();
            return true;
        }
    }


	/* Email doesn't match, clean cookies and error out */
    if ($error)
    {
	    hesk_setcookie('hesk_myemail', '');
        if ( ! empty($hesk_settings['tmp_skip_redirect'])) {
            return 'EMAIL_FALSE';
        }
        hesk_process_messages($hesklang['enmdb'],'ticket.php?track='.$trackingID.'&Refresh='.rand(10000,99999));
    }
    else
    {
    	return false;
    }

} // END hesk_verifyEmailMatch()


function hesk_getCustomerEmail($can_remember = 0, $field = '', $force_only_one = 0)
{
	global $hesk_settings, $hesklang;

	/* Email required to view ticket? */
	if ( ! $hesk_settings['email_view_ticket'])
    {
		$hesk_settings['e_param'] = '';
		$hesk_settings['e_query'] = '';
        $hesk_settings['e_email'] = '';
		return '';
    }

	/* Is this a form that enables remembering email? */
    if ($can_remember)
    {
    	global $do_remember;
    }

	$my_email = '';

	/* Is email in session? */
	if ( strlen($field) && isset($_SESSION[$field]) )
	{
		$my_email = hesk_validateEmail($_SESSION[$field], 'ERR', 0);
	}

	/* Is email in query string? */
	elseif ( isset($_GET['e']) || isset($_POST['e']) )
	{
		$my_email = hesk_validateEmail( hesk_REQUEST('e') ,'ERR',0);
	}

    /* Is email in cookie? */
	elseif ( isset($_COOKIE['hesk_myemail']) )
	{
		$my_email = hesk_validateEmail( hesk_COOKIE('hesk_myemail'), 'ERR', 0);
		if ($can_remember && $my_email)
		{
			$do_remember = ' checked="checked" ';
		}
	}

    // Remove unwanted side-effects
    $my_email = hesk_emailCleanup($my_email);

    // Force only one email address? Use the first one.
    if ($force_only_one)
    {
        $my_email = strtok($my_email, ',');
    }

    $hesk_settings['e_param'] = '&e=' . rawurlencode($my_email);
    $hesk_settings['e_query'] = '&amp;e=' . rawurlencode($my_email);
    $hesk_settings['e_email'] = $my_email;

    return $my_email;

} // END hesk_getCustomerEmail()


function hesk_emailCleanup($my_email)
{
    return preg_replace("/(\\\)+'/", "'", $my_email);
} // END hesk_emailCleanup()


function hesk_formatBytes($size, $translate_unit = 1, $precision = 2)
{
	global $hesklang;

    $units = array(
    	'GB' => 1073741824,
        'MB' => 1048576,
        'kB' => 1024,
        'B'  => 1
    );

    foreach ($units as $suffix => $bytes)
    {
    	if ($bytes > $size)
        {
        	continue;
        }

        $full  = $size / $bytes;
        $round = round($full, $precision);

        if ($full == $round)
        {
            if ($translate_unit)
            {
            	return $round . ' ' . $hesklang[$suffix];
            }
            else
            {
            	return $round . ' ' . $suffix;
            }
        }
    }

    return false;
} // End hesk_formatBytes()

function hesk_getAutoAssignConfigDisplay($autoassign_config) {
    global $hesklang;

    if ($autoassign_config === '' || $autoassign_config === null) {
        return '';
    }

    $parsed_config = hesk_parseAutoAssignConfig($autoassign_config);
    if (count($parsed_config['ids']) === 0) {
        return '';
    }

    $language_key = $parsed_config['operator'] === 'IN' ? 'included' : 'excluded';
    $user_count = hesk_getCountOfActiveUsersByIds($parsed_config['ids']);
    $count_key = $user_count === 1 ? 'one_user' : 'x_users';

    return sprintf($hesklang["{$count_key}_{$language_key}"], $user_count);
}

function hesk_parseAutoAssignConfig($autoassign_config) {
    $result = array(
        'operator' => '', // '' used by autoassign check
        'ids' => array()
    );

    if ($autoassign_config === '' || $autoassign_config === null) {
        return $result;
    }

    // regex = !(1,2,3) or =(1,2,3)  | ! - NOT IN, = - IN
    $regex = '/([!=])?\((.*)\)/';
    if (!is_string($autoassign_config) || !preg_match($regex, $autoassign_config, $matches) || !isset($matches[2])) {
        $result['operator'] = 'IN';
        return $result;
    }

    $result['operator'] = isset($matches[1]) && $matches[1] === '!' ? 'NOT IN' : 'IN';
    $result['ids'] = $matches[2] === '' ? array() : array_map('intval', explode(',', $matches[2]));

    return $result;
}

function hesk_getCountOfActiveUsersByIds($ids) {
    return count(hesk_getActiveAutoassignUsersForIds($ids));
}

function hesk_getActiveAutoassignUsersForIds($ids) {
    global $hesk_settings;

    if (!is_array($ids) || count($ids) === 0) {
        return array();
    }

    $ids = array_map('intval', $ids);

    $res = hesk_dbQuery("SELECT `id` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "users` WHERE `active` = 1 AND `id` IN (" . implode(',', $ids) . ")");
    $ids = array();

    while ($row = hesk_dbFetchAssoc($res)) {
        $ids[] = $row['id'];
    }

    return $ids;
}

function hesk_updateAutoassignConfigs() {
    global $hesk_settings;

    $res = hesk_dbQuery("SELECT `id`, `autoassign_config` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` WHERE `autoassign_config` <> ''");
    while ($row = hesk_dbFetchAssoc($res)) {
        $parsed_config = hesk_parseAutoAssignConfig($row['autoassign_config']);
        $active_users = hesk_getActiveAutoassignUsersForIds($parsed_config['ids']);

        // 1. No active users. Switch to either On - All, or Off
        if (count($active_users) === 0) {
            // Including no one is the same as off, and vice-versa
            $autoassign = $parsed_config['operator'] === 'IN' ? 0 : 1;

            hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` SET `autoassign` = '{$autoassign}', `autoassign_config` = '' WHERE `id` = {$row['id']}");
        } else {
            // Build a new autoassign config
            $operator = $parsed_config['operator'] === 'IN' ? '=' : '!';
            $stringed_users = implode(',', $active_users);
            $autoassign_config = "{$operator}({$stringed_users})";

            hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` SET `autoassign_config` = '{$autoassign_config}' WHERE `id` = {$row['id']}");
        }
    }
}


function hesk_autoAssignTicket($ticket_category)
{
	global $hesk_settings, $hesklang;

	/* Auto assign ticket enabled? */
	if ( ! $hesk_settings['autoassign'])
	{
		return false;
	}

	$autoassign_owner = array();

	/* Get all possible auto-assign staff, order by number of open tickets */
	$res = hesk_dbQuery("SELECT `t1`.`id`,`t1`.`user`,`t1`.`name`, `t1`.`autoassign`, `t1`.`email`, `t1`.`nickname`, `t1`.`language`, `t1`.`isadmin`, `t1`.`categories`, `t1`.`notify_assigned`, `t1`.`heskprivileges`,
					    (SELECT COUNT(*) FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` FORCE KEY (`statuses`) WHERE `owner`=`t1`.`id` AND `status` <> '3' ) as `open_tickets`
						FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."users` AS `t1`
						WHERE `t1`.`active` = 1
						ORDER BY `open_tickets` ASC, RAND()");
	$autoassign_config_res = hesk_dbQuery("SELECT `autoassign_config` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` WHERE `id` = ".intval($ticket_category));
	$autoassign_config = hesk_dbFetchAssoc($autoassign_config_res);
	$parsed_autoassign_config = hesk_parseAutoAssignConfig($autoassign_config['autoassign_config']);

    // Fetch categories / features assigned via permission group
    $permission_groups_rs = hesk_dbQuery("SELECT `member`.`user_id` AS `user_id`, `category`.`category_id` AS `category_feature_value`, 'CATEGORY' AS `category_feature_type` 
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_group_members` AS `member`
        INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_group_categories` AS `category`
            ON `member`.`group_id` = `category`.`group_id`
        UNION ALL
        SELECT `member`.`user_id` AS `user_id`, `feature`.`feature` AS `category_feature_value`, 'FEATURE' AS `category_feature_type` 
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_group_members` AS `member`
        INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_group_features` AS `feature`
            ON `member`.`group_id` = `feature`.`group_id`");
    $user_to_pg_features = [];
    $user_to_pg_categories = [];
    while ($row = hesk_dbFetchAssoc($permission_groups_rs)) {
        $user_id = intval($row['user_id']);
        if (!key_exists($user_id, $user_to_pg_categories)) {
            $user_to_pg_categories[$user_id] = [];
            $user_to_pg_features[$user_id] = [];
        }

        if ($row['category_feature_type'] === 'CATEGORY') {
            $user_to_pg_categories[$user_id][] = intval($row['category_feature_value']);
        } else {
            $user_to_pg_features[$user_id][] = $row['category_feature_value'];
        }
    }

	/* Loop through the rows and return the first appropriate one */
	while ($myuser = hesk_dbFetchAssoc($res))
	{
        $int_myuser_id = intval($myuser['id']);
        if (!key_exists($int_myuser_id, $user_to_pg_features)) {
            $user_to_pg_features[$int_myuser_id] = [];
            $user_to_pg_categories[$int_myuser_id] = [];
        }

	    /*
	    If "On - Select Users": Is the user allowed to be selected for this category?
	    If "On - All Users": Does the user have autoassign enabled on their user record?
	    */
        if (($parsed_autoassign_config['operator'] === 'IN' && !in_array($myuser['id'], $parsed_autoassign_config['ids'])) ||
            ($parsed_autoassign_config['operator'] === 'NOT IN' && in_array($myuser['id'], $parsed_autoassign_config['ids'])) ||
            ($parsed_autoassign_config['operator'] === '' && $myuser['autoassign'] === '0')) {
            continue;
        }

		/* Is this an administrator? */
		if ($myuser['isadmin'])
		{
			$autoassign_owner = $myuser;
            $hesk_settings['user_data'][$myuser['id']] = $myuser;
			hesk_dbFreeResult($res);
			break;
		}

		/* Not and administrator, check two things: */

        /* --> can view and reply to tickets */
        $can_view_tickets = strpos($myuser['heskprivileges'], 'can_view_tickets') !== false || in_array('can_view_tickets', $user_to_pg_features[$int_myuser_id]);
        $can_reply_tickets = strpos($myuser['heskprivileges'], 'can_reply_tickets') !== false || in_array('can_reply_tickets', $user_to_pg_features[$int_myuser_id]);
		if (!$can_view_tickets || !$can_reply_tickets)
		{
			continue;
		}

        /* --> has access to ticket category */
		$myuser['categories'] = explode(',',$myuser['categories']);
        $myuser['categories'] = array_merge($myuser['categories'], $user_to_pg_categories[$int_myuser_id]);
		if (in_array($ticket_category,$myuser['categories']))
		{
			$autoassign_owner = $myuser;
            $hesk_settings['user_data'][$myuser['id']] = $myuser;
			hesk_dbFreeResult($res);
			break;
		}
	} 

    return $autoassign_owner;

} // END hesk_autoAssignTicket()


function hesk_cleanID($field='track', $in=false)
{
    $id = '';

    if ($in !== false)
    {
        $id = $in;
    }
    elseif ( isset($_SESSION[$field]) )
    {
        $id = $_SESSION[$field];
    }
    elseif ( isset($_GET[$field]) && ! is_array($_GET[$field]) )
    {
        $id = $_GET[$field];
    }
    elseif ( isset($_POST[$field]) && ! is_array($_POST[$field]) )
    {
        $id = $_POST[$field];
    }
    else
    {
        return false;
    }

    return substr( preg_replace('/[^A-Z0-9\-]/','',strtoupper($id)) , 0, 12);

} // END hesk_cleanID()


function hesk_createID()
{
	global $hesk_settings, $hesklang, $hesk_error_buffer;

	/*** Generate tracking ID and make sure it's not a duplicate one ***/

	/* Ticket ID can be of these chars */
	$useChars = 'AEUYBDGHJLMNPQRSTVWXZ123456789';

    /* Set tracking ID to an empty string */
	$trackingID = '';

	/* Let's avoid duplicate ticket ID's, try up to 3 times */
	for ($i=1;$i<=3;$i++)
    {
	    /* Generate raw ID */
	    $trackingID .= $useChars[mt_rand(0,29)];
	    $trackingID .= $useChars[mt_rand(0,29)];
	    $trackingID .= $useChars[mt_rand(0,29)];
	    $trackingID .= $useChars[mt_rand(0,29)];
	    $trackingID .= $useChars[mt_rand(0,29)];
	    $trackingID .= $useChars[mt_rand(0,29)];
	    $trackingID .= $useChars[mt_rand(0,29)];
	    $trackingID .= $useChars[mt_rand(0,29)];
	    $trackingID .= $useChars[mt_rand(0,29)];
	    $trackingID .= $useChars[mt_rand(0,29)];

		/* Format the ID to the correct shape and check wording */
        $trackingID = hesk_formatID($trackingID);

		/* Check for duplicate IDs */
		$res = hesk_dbQuery("SELECT `id` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `trackid` = '".hesk_dbEscape($trackingID)."' LIMIT 1");

		if (hesk_dbNumRows($res) == 0)
		{
        	/* Everything is OK, no duplicates found */
			return $trackingID;
        }

        /* A duplicate ID has been found! Let's try again (up to 2 more) */
        $trackingID = '';
    }

    /* No valid tracking ID, try one more time with microtime() */
	$trackingID  = $useChars[mt_rand(0,29)];
	$trackingID .= $useChars[mt_rand(0,29)];
	$trackingID .= $useChars[mt_rand(0,29)];
	$trackingID .= $useChars[mt_rand(0,29)];
	$trackingID .= $useChars[mt_rand(0,29)];
	$trackingID .= substr(microtime(), -5);

	/* Format the ID to the correct shape and check wording */
	$trackingID = hesk_formatID($trackingID);

	$res = hesk_dbQuery("SELECT `id` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `trackid` = '".hesk_dbEscape($trackingID)."' LIMIT 1");

	/* All failed, must be a server-side problem... */
	if (hesk_dbNumRows($res) == 0)
	{
		return $trackingID;
    }

    $hesk_error_buffer['etid'] = $hesklang['e_tid'];
	return false;

} // END hesk_createID()


function hesk_formatID($id)
{

	$useChars = 'AEUYBDGHJLMNPQRSTVWXZ123456789';

    $replace  = $useChars[mt_rand(0,29)];
    $replace .= mt_rand(1,9);
    $replace .= $useChars[mt_rand(0,29)];

    /*
    Remove 3 letter bad words from ID
    Possiblitiy: 1:27,000
    */
	$remove = array(
    'ASS',
    'CUM',
    'FAG',
    'FUK',
    'GAY',
    'SEX',
    'TIT',
    'XXX',
    );

    $id = str_replace($remove,$replace,$id);

    /*
    Remove 4 letter bad words from ID
    Possiblitiy: 1:810,000
    */
	$remove = array(
	'ANAL',
	'ANUS',
	'BUTT',
	'CAWK',
	'CLIT',
	'COCK',
	'CRAP',
	'CUNT',
	'DICK',
	'DYKE',
	'FART',
	'FUCK',
	'JAPS',
	'JERK',
	'JIZZ',
	'KNOB',
	'PISS',
	'POOP',
	'SHIT',
	'SLUT',
	'SUCK',
	'TURD',

    // Also, remove words that are known to trigger mod_security
	'WGET',
    );

	$replace .= mt_rand(1,9);
    $id = str_replace($remove,$replace,$id);

    /* Format the ID string into XXX-XXX-XXXX format for easier readability */
    $id = $id[0].$id[1].$id[2].'-'.$id[3].$id[4].$id[5].'-'.$id[6].$id[7].$id[8].$id[9];

    return $id;

} // END hesk_formatID()


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

	/* If this feature is disabled, just return */
    if ( ! $hesk_settings['attempt_limit'] || defined('HESK_BF_CLEAN') )
    {
    	return true;
    }

    /* Delete expired logs from the database */
	$res = hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."logins` WHERE `ip`='".hesk_dbEscape(hesk_getClientIP())."'");

    define('HESK_BF_CLEAN', 1);

	return true;
} // END hesk_cleanAttempts()


function hesk_forceLogout($error_message = '', $redirect = 'index.php', $do_die = true, $message_type = 'ERROR')
{
    global $hesk_settings, $hesklang;

    if (($id = hesk_SESSION('id', false)) === false) {
        return true;
    }

    // Set Offline
    if ($hesk_settings['online']) {
        if ( ! function_exists('hesk_setOffline')) {
            require(HESK_PATH . 'inc/users_online.inc.php');
        }
        hesk_setOffline($id);
    }

    // Clear users' authentication and MFA tokens
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."auth_tokens` WHERE `user_id` = {$id} AND `user_type` = 'STAFF'");
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."mfa_verification_tokens` WHERE `user_id` = {$id} AND `user_type` = 'STAFF'");

    // Stop the session and clear cookies
    hesk_session_stop();
    hesk_setcookie('hesk_username', '');
    hesk_setcookie('hesk_remember', '');

    // Send the user to the login page?
    if ($redirect !== false) {
        hesk_session_start();
        hesk_process_messages($error_message, 'index.php', $message_type);
    }

    if ($do_die) {
        exit();
    }

} // END hesk_forceLogout()


function hesk_forceLogoutCustomer($error_message = '', $redirect = 'index.php', $do_die = true, $message_type = 'ERROR')
{
    global $hesk_settings, $hesklang;

    if (($id = hesk_SESSION(array('customer', 'id'), false)) === false) {
        return true;
    }

    // Clear users MFA and auth tokens
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."mfa_verification_tokens` WHERE `user_id` = {$id} AND `user_type` = 'CUSTOMER'");
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."auth_tokens` WHERE `user_id` = {$id} AND `user_type` = 'CUSTOMER'");

    // Stop the session and clear cookies
    hesk_session_stop();
    hesk_setcookie('hesk_customer_remember', '');

    // Send the user to the login page?
    if ($redirect !== false) {
        hesk_session_start('CUSTOMER');
        hesk_process_messages($error_message, $redirect, $message_type);
    }

    if ($do_die) {
        exit();
    }

} // END hesk_forceLogoutCustomer()


function hesk_limitInternalBfAttempts()
{
    return hesk_limitBfAttempts(false);
} // END hesk_limitInternalBfAttempts()


function hesk_limitBfAttempts($die_with_error = true)
{
	global $hesk_settings, $hesklang;

	// Check if this IP is banned permanently
	if ( hesk_isBannedIP(hesk_getClientIP()) )
	{
        if (isset($_SESSION['customer'])) {
            hesk_forceLogoutCustomer($hesklang['baned_ip']);
        } else {
            hesk_forceLogout($hesklang['baned_ip']);
        }
    	hesk_error($hesklang['baned_ip'], 0);
	}

	/* If this feature is disabled or already called, return false */
    if ( ! $hesk_settings['attempt_limit'] || defined('HESK_BF_LIMIT') )
    {
    	return false;
    }

    /* Define this constant to avoid duplicate checks */
    define('HESK_BF_LIMIT', 1);

	$ip = hesk_getClientIP();

    /* Get number of failed attempts from the database */
	$res = hesk_dbQuery("SELECT `number`, (CASE WHEN `last_attempt` IS NOT NULL AND DATE_ADD(`last_attempt`, INTERVAL ".intval($hesk_settings['attempt_banmin'])." MINUTE ) > NOW() THEN 1 ELSE 0 END) AS `banned` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."logins` WHERE `ip`='".hesk_dbEscape($ip)."' LIMIT 1");

    /* Not in the database yet? Add first one and return false */
	if (hesk_dbNumRows($res) != 1)
	{
		hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."logins` (`ip`) VALUES ('".hesk_dbEscape($ip)."')");
		return false;
	}

    /* Get number of failed attempts and increase by 1 */
    $row = hesk_dbFetchAssoc($res);
    $row['number']++;

    /* If too many failed attempts either return error or reset count if time limit expired */
	if ($row['number'] >= $hesk_settings['attempt_limit'])
    {
    	if ($row['banned'])
        {
            // Sometimes we just want to log the user out and reset the number of attempts, not ban immediately
            if ( ! $die_with_error)
            {
                hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."logins` WHERE `ip`='".hesk_dbEscape($ip)."'");
                if (isset($_SESSION['customer'])) {
                    hesk_forceLogoutCustomer($hesklang['baned_ip']);
                } else {
                    hesk_forceLogout($hesklang['baned_ip']);
                }
            }

            $error = sprintf($hesklang['yhbb'], $hesk_settings['attempt_banmin']);

            // If this is a logged-in user, log out
            if (isset($_SESSION['customer'])) {
                hesk_forceLogoutCustomer($hesklang['baned_ip']);
            } else {
                hesk_forceLogout($hesklang['baned_ip']);
            }

            // Not a logged-in user, throw an error
            unset($_SESSION);
        	hesk_error($error, 0);

        }
        else
        {
			$row['number'] = 1;
        }
    }

	hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."logins` SET `number`=".intval($row['number'])." WHERE `ip`='".hesk_dbEscape($ip)."'");

	return false;

} // END hesk_limitAttempts()


function hesk_getCategoryName($id)
{
	global $hesk_settings, $hesklang;

	if (empty($id))
	{
		return $hesklang['unas'];
	}

	// If we already have the name no need to query DB another time
	if ( isset($hesk_settings['category_data'][$id]['name']) )
	{
		return $hesk_settings['category_data'][$id]['name'];
	}

	$res = hesk_dbQuery("SELECT `name` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` WHERE `id`='".intval($id)."' LIMIT 1");

	if (hesk_dbNumRows($res) != 1)
	{
		return $hesklang['catd'];
	}

	$hesk_settings['category_data'][$id]['name'] = hesk_dbResult($res,0,0);

	return $hesk_settings['category_data'][$id]['name'];
} // END hesk_getCategoryName()

function hesk_getCategoryDueDateInfo($id) {
    global $hesk_settings;

    // If we already have the name no need to query DB another time
    if ( isset($hesk_settings['category_data'][$id]['due_date_info']) )
    {
        return $hesk_settings['category_data'][$id]['due_date_info'];
    }

    $res = hesk_dbQuery("SELECT `default_due_date_amount`, `default_due_date_unit` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` WHERE `id`='".intval($id)."' AND `default_due_date_amount` IS NOT NULL LIMIT 1");

    if (hesk_dbNumRows($res) != 1)
    {
        return null;
    }

    $category = hesk_dbFetchAssoc($res);

    $hesk_settings['category_data'][$id]['due_date_info'] = array(
        'amount' => $category['default_due_date_amount'],
        'unit' => $category['default_due_date_unit']
    );

    return $hesk_settings['category_data'][$id]['due_date_info'];
}


function hesk_getReplierNameArray($ticket)
{
	global $hesk_settings, $hesklang;

    // Already have this info?
    if (isset($ticket['last_reply_by'])) {
        return $ticket['last_reply_by'];
    }

    // Last reply by staff
    if ( ! empty($ticket['lastreplier'])) {

        // We don't know who from staff so just send "Staff"
        if (empty($ticket['replierid'])) {
            return array('name' => $hesklang['staff'], 'nickname' => $hesklang['staff']);
        }

        // Get the name using another function
        $replier = hesk_getStaffNameArray($ticket['replierid']);

        // If replier comes back as "unassigned", default to "Staff"
        if ($replier === false) {
            return array('name' => $hesklang['staff'], 'nickname' => $hesklang['staff']);
        }

        return $hesk_settings['user_data'][$ticket['replierid']];
    }

    // Last reply by customer
    if ($ticket['replies'] > 0) {
        // Get latest customer reply
        $replier_rs = hesk_dbQuery("SELECT `customer`.`name` AS `name`, `customer`.`email` AS `email`
                FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` AS `reply`
                LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` AS `customer`
                    ON `reply`.`customer_id` = `customer`.`id`
                WHERE `replyto` = ".intval($ticket['id'])."
                    AND `staffid` = 0
                ORDER BY `reply`.`id` DESC
                LIMIT 1");

        if ($row = hesk_dbFetchAssoc($replier_rs)) {
            if ($row['name'] === null || $row['name'] == '') {
                $row['name'] = empty($row['email']) ? $hesklang['anon_name'] : $row['email'];
            }
            return array('name' => $row['name'], 'nickname' => $row['name']);
        }
    }

    // No replies, grab the requester from the main ticket
    $requester_name_rs = hesk_dbQuery("SELECT `customer`.`name`, `customer`.`email` AS `email`
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` AS `ticket_to_customer`
        LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` AS `customer`
            ON `ticket_to_customer`.`customer_id` = `customer`.`id`
        WHERE `ticket_to_customer`.`ticket_id` = ".intval($ticket['id'])."
            AND `customer_type` = 'REQUESTER'");

    if ($row = hesk_dbFetchAssoc($requester_name_rs)) {
        if ($row['name'] === null || $row['name'] == '') {
            $row['name'] = empty($row['email']) ? $hesklang['anon_name'] : $row['email'];
        }
        return array('name' => $row['name'], 'nickname' => $row['name']);
    }

    // Nothing found, return default name
    return array('name' => $hesklang['anon_name'], 'nickname' => $hesklang['anon_name']);

} // END hesk_getReplierNameArray()

function hesk_getStaffNameArray($id)
{
	global $hesk_settings, $hesklang;

    if (empty($id)) {
        return false;
    }

    $id = intval($id);

    // If we already have the name no need to query DB another time
    if ( isset($hesk_settings['user_data'][$id]) ) {
        return $hesk_settings['user_data'][$id];
    }

    $res = hesk_dbQuery("SELECT `name`, `nickname` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."users` WHERE `id`={$id} LIMIT 1");

    if (hesk_dbNumRows($res) != 1) {
        return false;
    }

    $user = hesk_dbFetchAssoc($res);
    $hesk_settings['user_data'][$id]['name'] = $user['name'];
    $hesk_settings['user_data'][$id]['nickname'] = ($hesk_settings['staff_nicknames'] && $user['nickname'] != '') ? $user['nickname'] : $user['name'];

	return $hesk_settings['user_data'][$id];
} // END hesk_getStaffNameArray()


function hesk_cleanSessionVars($arr)
{
	if (is_array($arr))
	{
		foreach ($arr as $str)
		{
			if (isset($_SESSION[$str]))
			{
				unset($_SESSION[$str]);
			}
		}
	}
	elseif (isset($_SESSION[$arr]))
	{
		unset($_SESSION[$arr]);
	}
} // End hesk_cleanSessionVars()


function hesk_process_messages($message,$redirect_to,$type='ERROR')
{
	global $hesk_settings, $hesklang;

    switch ($type)
    {
    	case 'SUCCESS':
        	$_SESSION['HESK_SUCCESS'] = TRUE;
            break;
        case 'NOTICE':
        	$_SESSION['HESK_NOTICE'] = TRUE;
            break;
        case 'INFO':
        	$_SESSION['HESK_INFO'] = TRUE;
            break;
        default:
        	$_SESSION['HESK_ERROR'] = TRUE;
    }

	$_SESSION['HESK_MESSAGE'] = $message;

    /* In some cases we don't want a redirect */
    if ($redirect_to == 'NOREDIRECT')
    {
    	return TRUE;
    }

	header('Location: '.$redirect_to);
	exit();
} // END hesk_process_messages()

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

    $messages = array();

	// Primary message - only one can be displayed and HESK_MESSAGE is required
	if ( isset($_SESSION['HESK_MESSAGE']) )
	{
		if ( isset($_SESSION['HESK_SUCCESS']) )
		{
		    $messages[] = array(
		        'title' => $hesklang['success'],
		        'style' => '1',
		        'message' => $_SESSION['HESK_MESSAGE']
		    );
		}
		elseif ( isset($_SESSION['HESK_ERROR']) )
		{
		    $messages[] = array(
		        'title' => $hesklang['error'],
		        'style' => '4',
		        'message' => $_SESSION['HESK_MESSAGE']
		    );
		}
		elseif ( isset($_SESSION['HESK_NOTICE']) )
		{
		    $messages[] = array(
		        'title' => $hesklang['note'],
		        'style' => '3',
		        'message' => $_SESSION['HESK_MESSAGE']
		    );
		}
		elseif ( isset($_SESSION['HESK_INFO']) )
		{
		    $messages[] = array(
		        'title' => $hesklang['info'],
		        'style' => '2',
		        'message' => $_SESSION['HESK_MESSAGE']
		    );
		}

		hesk_cleanSessionVars('HESK_MESSAGE');
	}

	// Cleanup any primary message types set
	hesk_cleanSessionVars('HESK_ERROR');
	hesk_cleanSessionVars('HESK_SUCCESS');
	hesk_cleanSessionVars('HESK_NOTICE');
	hesk_cleanSessionVars('HESK_INFO');

	// Secondary message
	if ( isset($_SESSION['HESK_2ND_NOTICE']) && isset($_SESSION['HESK_2ND_MESSAGE']) )
	{
	    $messages[] = array(
            'title' => $hesklang['note'],
            'style' => '3',
            'message' => $_SESSION['HESK_2ND_MESSAGE']
        );
		hesk_cleanSessionVars('HESK_2ND_NOTICE');
		hesk_cleanSessionVars('HESK_2ND_MESSAGE');
	}

	return $messages;
}


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

	$return_value = true;

	// Primary message - only one can be displayed and HESK_MESSAGE is required
	if ( isset($_SESSION['HESK_MESSAGE']) )
	{
		if ( isset($_SESSION['HESK_SUCCESS']) )
		{
			hesk_show_success($_SESSION['HESK_MESSAGE']);
		}
		elseif ( isset($_SESSION['HESK_ERROR']) )
		{
			hesk_show_error($_SESSION['HESK_MESSAGE']);
			$return_value = false;
		}
		elseif ( isset($_SESSION['HESK_NOTICE']) )
		{
			hesk_show_notice($_SESSION['HESK_MESSAGE']);
		}
		elseif ( isset($_SESSION['HESK_INFO']) )
		{
			hesk_show_info($_SESSION['HESK_MESSAGE']);
		}

		hesk_cleanSessionVars('HESK_MESSAGE');
	}

	// Cleanup any primary message types set
	hesk_cleanSessionVars('HESK_ERROR');
	hesk_cleanSessionVars('HESK_SUCCESS');
	hesk_cleanSessionVars('HESK_NOTICE');
	hesk_cleanSessionVars('HESK_INFO');

	// Secondary message
	if ( isset($_SESSION['HESK_2ND_NOTICE']) && isset($_SESSION['HESK_2ND_MESSAGE']) )
	{
		hesk_show_notice($_SESSION['HESK_2ND_MESSAGE']);
		hesk_cleanSessionVars('HESK_2ND_NOTICE');
		hesk_cleanSessionVars('HESK_2ND_MESSAGE');
	}

	return $return_value;
} // END hesk_handle_messages()


function hesk_show_error($message,$title='',$append_colon=true,$extra_class='')
{
	global $hesk_settings, $hesklang;
    $title = $title ? $title : $hesklang['error'];
	$title = $append_colon ? $title . ':' : $title;
	?>
    <div class="main__content notice-flash <?php echo $extra_class; ?>">
        <div role="alert" class="notification red">
            <b><?php echo $title; ?></b> <?php echo $message; ?>
        </div>
    </div>
	<?php
} // END hesk_show_error()


function hesk_show_success($message,$title='',$append_colon=true,$extra_class='')
{
	global $hesk_settings, $hesklang;
    $title = $title ? $title : $hesklang['success'];
	$title = $append_colon ? $title . ':' : $title;
	?>
    <div class="main__content notice-flash <?php echo $extra_class; ?>">
        <div role="status" class="notification green">
            <b><?php echo $title; ?></b> <?php echo $message; ?>
        </div>
    </div>
	<?php
} // END hesk_show_success()


function hesk_show_notice($message,$title='',$append_colon=true,$extra_class='')
{
	global $hesk_settings, $hesklang;
    $title = $title ? $title : $hesklang['note'];
	$title = $append_colon ? $title . ':' : $title;
	?>
    <div class="main__content notice-flash <?php echo $extra_class; ?>">
        <div role="alert" class="notification orange">
            <b><?php echo $title; ?></b> <?php echo $message; ?>
        </div>
    </div>
	<?php
} // END hesk_show_notice()


function hesk_show_info($message,$title='',$append_colon=true,$extra_class='')
{
	global $hesk_settings, $hesklang;
    $title = $title ? $title : $hesklang['info'];
	$title = $append_colon ? $title . ':' : $title;
	?>
    <div class="main__content notice-flash <?php echo $extra_class; ?>">
        <div role="status" class="notification blue">
            <b><?php echo $title; ?></b> <?php echo $message; ?>
        </div>
    </div>
	<?php
} // END hesk_show_info()


function hesk_token_echo($do_echo = 1)
{
	if ( ! defined('SESSION_CLEAN'))
    {
		$_SESSION['token'] = hesk_htmlspecialchars(strip_tags($_SESSION['token']));
        define('SESSION_CLEAN', true);
    }

    if ($do_echo)
    {
		echo $_SESSION['token'];
    }
    else
    {
    	return $_SESSION['token'];
    }
} // END hesk_token_echo()


function hesk_token_check($method='GET', $show_error=1)
{
	// Get the token
	$my_token = hesk_REQUEST('token');

    // Verify it or throw an error
	if ( ! hesk_token_compare($my_token))
    {
    	if ($show_error)
        {
        	global $hesk_settings, $hesklang;
        	hesk_error($hesklang['eto']);
        }
        else
        {
        	return false;
        }
    }

    return true;
} // END hesk_token_check()


function hesk_token_compare($my_token)
{
	if (isset($_SESSION['token']) && $my_token == $_SESSION['token'])
    {
    	return true;
    }
    else
    {
    	return false;
    }
} // END hesk_token_compare()


function hesk_token_hash()
{
	return sha1(time() . microtime() . uniqid(rand(), true) );
} // END hesk_token_hash()


function & ref_new(&$new_statement)
{
	return $new_statement;
} // END ref_new()


function hesk_ticketToPlain($ticket, $specialchars=0, $strip=1)
{
	if ( is_array($ticket) )
	{
		foreach ($ticket as $key => $value)
		{
            if ($key == 'message_html') {
                continue;
            }

			$ticket[$key] = is_array($ticket[$key]) ? hesk_ticketToPlain($value, $specialchars, $strip) : hesk_msgToPlain($value, $specialchars, $strip);
		}

		return $ticket;
	}
	else
	{
		return hesk_msgToPlain($ticket, $specialchars, $strip);
	}
} // END hesk_ticketToPlain()


function hesk_msgToPlain($msg, $specialchars=0, $strip=1)
{
    if ($msg === null || $msg === '') {
        return '';
    }

	$msg = preg_replace('/\<a href="(mailto:)?([^"]*)"[^\<]*\<\/a\>/i', "$2", $msg);
	$msg = preg_replace('/<br \/>\s*/',"\n",$msg);
    $msg = trim($msg);

    if ($strip)
    {
        $msg = hesk_stripslashes($msg);
    }

    if ($specialchars)
    {
    	$msg = hesk_html_entity_decode($msg);
    }

    return $msg;
} // END hesk_msgToPlain()

function hesk_getCurrentGetParameters() {
    if ( ! isset($_GET) ) {
        $_GET = array();
    }

    $parameters = array();
    foreach ($_GET as $k => $v) {
        if ($k == 'language') {
            continue;
        }
        $parameters[$k] = $v;
    }

    return $parameters;
}

function hesk_showTopBar($page_title, $trackingID = false)
{
	global $hesk_settings, $hesklang;

	if ($hesk_settings['can_sel_lang'])
	{

		$str = '<form method="get" action="" style="margin:0;padding:0;border:0;white-space:nowrap;">';

        if ($trackingID !== false)
        {
            $str .= '<input type="hidden" name="track" value="'.hesk_htmlentities($trackingID).'" />';

            if ($hesk_settings['email_view_ticket'] && isset($hesk_settings['e_email']))
            {
                $str .= '<input type="hidden" name="e" value="'.hesk_htmlentities($hesk_settings['e_email']).'" />';
            }
        }

        if ( ! isset($_GET) )
        {
        	$_GET = array();
        }

		foreach ($_GET as $k => $v)
		{
			if ($k == 'language')
			{
				continue;
			}
			$str .= '<input type="hidden" name="'.hesk_htmlentities($k).'" value="'.hesk_htmlentities($v).'" />';
		}

        $str .= '<select name="language" onchange="this.form.submit()">';
		$str .= hesk_listLanguages(0);
		$str .= '</select>';

	?>
		<table border="0" cellspacing="0" cellpadding="0" width="100%">
		<tr>
		<td class="headersm" style="padding-left: 0px;"><?php echo $page_title; ?></td>
		<td class="headersm" style="padding-left: 0px;text-align: right">
        <script language="javascript" type="text/javascript">
		document.write('<?php echo str_replace(array('"','<','=','>',"'"),array('\42','\74','\75','\76','\47'),$str . '</form>'); ?>');
        </script>
        <noscript>
        <?php
        	echo $str . '<input type="submit" value="'.$hesklang['go'].'" /></form>';
        ?>
        </noscript>
        </td>
		</tr>
		</table>
	<?php
	}
	else
	{
		echo $page_title;
	}
} // END hesk_showTopBar()


function hesk_listLanguages($doecho = 1) {
	global $hesk_settings, $hesklang;

    $tmp = '';

	foreach ($hesk_settings['languages'] as $lang => $info)
	{
		if ($lang == $hesk_settings['language'])
		{
			$tmp .= '<option value="'.$lang.'" selected>'.$lang.'</option>';
		}
		else
		{
			$tmp .= '<option value="'.$lang.'">'.$lang.'</option>';
		}
	}

    if ($doecho)
    {
		echo $tmp;
    }
    else
    {
    	return $tmp;
    }
} // END hesk_listLanguages


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

    /* If this is not a valid request no need to change aynthing */
    if ( ! $hesk_settings['can_sel_lang'] || ! defined('HESK_ORIGINAL_LANGUAGE') )
    {
        return false;
    }

    /* If we already have original language, just return true */
    if ($hesk_settings['language'] == HESK_ORIGINAL_LANGUAGE)
    {
    	return true;
    }

	/* Get the original language file */
	$hesk_settings['language'] = HESK_ORIGINAL_LANGUAGE;
    return hesk_returnLanguage();
} // END hesk_resetLanguage()


function hesk_setLanguage($language)
{
	global $hesk_settings, $hesklang;

    /* If no language is set, use default */
	if ( ! $language)
    {
    	$language = HESK_DEFAULT_LANGUAGE;
    }

    /* If this is not a valid request no need to change aynthing */
    if ( ! $hesk_settings['can_sel_lang'] || $language == $hesk_settings['language'] || ! isset($hesk_settings['languages'][$language]) )
    {
        return false;
    }

    /* Remember current language for future reset - if reset is not set already! */
    if ( ! defined('HESK_ORIGINAL_LANGUAGE') )
    {
    	define('HESK_ORIGINAL_LANGUAGE', $hesk_settings['language']);
    }

	/* Get the new language file */
	$hesk_settings['language'] = $language;

    return hesk_returnLanguage();
} // END hesk_setLanguage()


function hesk_getLanguage()
{
	global $hesk_settings, $hesklang, $_SESSION;

    $language = $hesk_settings['language'];

    /* Remember what the default language is for some special uses like mass emails */
	define('HESK_DEFAULT_LANGUAGE', $hesk_settings['language']);

    /* Can users select language? */
    if (defined('NO_HTTP_HEADER') ||  empty($hesk_settings['can_sel_lang']) )
    {
        return hesk_returnLanguage();
    }

    /* Is a non-default language selected? If not use default one */
    if (isset($_GET['language']))
    {
        $requested_language = hesk_input( hesk_GET('language') );

        if ($requested_language && isset($hesk_settings['languages'][$requested_language]))
        {
            $language = $requested_language;
        }
        elseif (isset($_COOKIE['hesk_language']))
        {
            $cookie_language = hesk_input( hesk_COOKIE('hesk_language') );

            if ($cookie_language && isset($hesk_settings['languages'][$cookie_language]))
            {
                $language = $cookie_language;
            }
        }
    }
    elseif (isset($_COOKIE['hesk_language']))
    {
        $cookie_language = hesk_input( hesk_COOKIE('hesk_language') );

        if ($cookie_language && isset($hesk_settings['languages'][$cookie_language]))
        {
            $language = $cookie_language;
        }
    }
    else
    {
        return hesk_returnLanguage();
    }

    /* non-default language selected. Check if it's a valid one, if not use default one */
    if ($language != $hesk_settings['language'] && isset($hesk_settings['languages'][$language]))
    {
        $hesk_settings['language'] = $language;
    }

    /* Remember and set the selected language */
	hesk_setcookie('hesk_language',$hesk_settings['language'],time()+31536000,'/');
    return hesk_returnLanguage();
} // END hesk_getLanguage()


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

    // Variable that will be set to true if a language file was loaded
    $language_loaded = false;

    // Load requested language file
    $language_file = HESK_PATH . 'language/' . $hesk_settings['languages'][$hesk_settings['language']]['folder'] . '/text.php';
    if (file_exists($language_file))
    {
        require($language_file);
        $language_loaded = true;
    }

    // Requested language file not found, try to load default installed language
    if ( ! $language_loaded && $hesk_settings['language'] != HESK_DEFAULT_LANGUAGE)
    {
        $language_file = HESK_PATH . 'language/' . $hesk_settings['languages'][HESK_DEFAULT_LANGUAGE]['folder'] . '/text.php';
        if (file_exists($language_file))
        {
            require($language_file);
            $language_loaded = true;
            $hesk_settings['language'] = HESK_DEFAULT_LANGUAGE;
        }
    }

    // Requested language file not found, can we at least load English?
    if ( ! $language_loaded && $hesk_settings['language'] != 'English' && HESK_DEFAULT_LANGUAGE != 'English')
    {
        $language_file = HESK_PATH . 'language/en/text.php';
        if (file_exists($language_file))
        {
            require($language_file);
            $language_loaded = true;
            $hesk_settings['language'] = 'English';
        }
    }

    // If a language is still not loaded, give up
    if ( ! $language_loaded)
    {
        die('Count not load a valid language file.');
    }

    // Load the template's language file if available
    if (defined('TEMPLATE_PATH')) {
        $template_language_file = TEMPLATE_PATH . '/language/' . $hesk_settings['languages'][$hesk_settings['language']]['folder'] . '/text.php';
        if (file_exists($template_language_file)) {
            require($template_language_file);
        }
    }


    // Load a custom text file if available
    $language_file = HESK_PATH . 'language/' . $hesk_settings['languages'][$hesk_settings['language']]['folder'] . '/custom-text.php';
    if (file_exists($language_file))
    {
        require($language_file);
    }

    return true;
} // END hesk_returnLanguage()


function hesk_setTimezone()
{
    global $hesk_settings;

    // Set the desired timezone, default to UTC
    if ( ! isset($hesk_settings['timezone']) || date_default_timezone_set($hesk_settings['timezone']) === false)
    {
        date_default_timezone_set('UTC');
    }

    return true;

} // END hesk_setTimezone()


function hesk_timeToHHMM($time, $time_format="seconds", $signed=true)
{
    if ($time < 0)
    {
        $time = abs($time);
        $sign = "-";
    }
    else
    {
        $sign = "+";
    }

    if ($time_format == 'minutes')
    {
        $time *= 60;
    }

    return ($signed ? $sign : '') . gmdate('H:i', $time);

} // END hesk_timeToHHMM()


function hesk_translate_date_string($dt, $reverse = false)
{
    global $hesk_settings, $hesklang;

    // This is a simple translate for simple use cases; more complex will need the Intl extension

    // No point in translating English dates
    if ($hesk_settings['language'] == 'English') {
        return $dt;
    }

    // First, let' replace the long day names
    $dt_string_map = array(
        'Monday'    => $hesklang['d1'],
        'Tuesday'   => $hesklang['d2'],
        'Wednesday' => $hesklang['d3'],
        'Thursday'  => $hesklang['d4'],
        'Friday'    => $hesklang['d5'],
        'Saturday'  => $hesklang['d6'],
        'Sunday'    => $hesklang['d0'],
    );
    if ($reverse) {
        $dt_translated = str_replace(array_values($dt_string_map), array_keys($dt_string_map), $dt);
    } else {
        $dt_translated = str_replace(array_keys($dt_string_map), array_values($dt_string_map), $dt);
    }

    // If nothing found, try short day names
    if ($dt_translated == $dt) {
        $dt_string_map = array(
            'Mon' => $hesklang['mon'],
            'Tue' => $hesklang['tue'],
            'Wed' => $hesklang['wed'],
            'Thu' => $hesklang['thu'],
            'Fri' => $hesklang['fri'],
            'Sat' => $hesklang['sat'],
            'Sun' => $hesklang['sun'],
        );
        if ($reverse) {
            $dt_translated = str_replace(array_values($dt_string_map), array_keys($dt_string_map), $dt);
        } else {
            $dt_translated = str_replace(array_keys($dt_string_map), array_values($dt_string_map), $dt);
        }
    }

    // Day names done, update $dt so we can compare after processing months now
    $dt = $dt_translated;

    // Try full month names
    $dt_string_map = array(
        'January'   => $hesklang['m1'],
        'February'  => $hesklang['m2'],
        'March'     => $hesklang['m3'],
        'April'     => $hesklang['m4'],
        'May'       => $hesklang['m5'],
        'June'      => $hesklang['m6'],
        'July'      => $hesklang['m7'],
        'August'    => $hesklang['m8'],
        'September' => $hesklang['m9'],
        'October'   => $hesklang['m10'],
        'November'  => $hesklang['m11'],
        'December'  => $hesklang['m12'],
    );
    if ($reverse) {
        $dt_translated = str_replace(array_values($dt_string_map), array_keys($dt_string_map), $dt);
    } else {
        $dt_translated = str_replace(array_keys($dt_string_map), array_values($dt_string_map), $dt);
    }

    // If nothing found, try short month names
    if ($dt_translated == $dt) {
        $dt_string_map = array(
            'Jan' => $hesklang['ms01'],
            'Feb' => $hesklang['ms02'],
            'Mar' => $hesklang['ms03'],
            'Apr' => $hesklang['ms04'],
            'May' => $hesklang['ms05'],
            'Jun' => $hesklang['ms06'],
            'Jul' => $hesklang['ms07'],
            'Aug' => $hesklang['ms08'],
            'Sep' => $hesklang['ms09'],
            'Oct' => $hesklang['ms10'],
            'Nov' => $hesklang['ms11'],
            'Dec' => $hesklang['ms12'],
        );
        if ($reverse) {
            $dt_translated = str_replace(array_values($dt_string_map), array_keys($dt_string_map), $dt);
        } else {
            $dt_translated = str_replace(array_keys($dt_string_map), array_values($dt_string_map), $dt);
        }
    }

    // If still nothing found, try lowercase short month names
    if ($dt_translated == $dt) {
        $dt_string_map = array(
            'jan' => $hesklang['ms01'],
            'feb' => $hesklang['ms02'],
            'mar' => $hesklang['ms03'],
            'apr' => $hesklang['ms04'],
            'may' => $hesklang['ms05'],
            'jun' => $hesklang['ms06'],
            'jul' => $hesklang['ms07'],
            'aug' => $hesklang['ms08'],
            'sep' => $hesklang['ms09'],
            'oct' => $hesklang['ms10'],
            'nov' => $hesklang['ms11'],
            'dec' => $hesklang['ms12'],
        );
        if ($reverse) {
            $dt_translated = str_replace(array_values($dt_string_map), array_keys($dt_string_map), $dt);
        } else {
            $dt_translated = str_replace(array_keys($dt_string_map), array_values($dt_string_map), $dt);
        }
    }

    return $dt_translated;

} // END hesk_translate_date_string()


function hesk_date($dt='', $from_database=false, $is_str=true, $return_str=true, $format = false)
{
	global $hesk_settings;

    if (!$dt)
    {
    	$dt = time();
    }
    elseif ($is_str)
    {
    	$dt = strtotime($dt);
    }

    if ( ! $return_str)
    {
        return $dt;
    }

    if ($format)
    {
        return hesk_translate_date_string(date($format, $dt));
    }

    return hesk_translate_date_string(date($hesk_settings['format_timestamp'], $dt));

} // End hesk_date()


function hesk_format_due_date($dt, $is_str=true)
{
    global $hesk_settings;

    if (!$dt)
    {
        return '';
    }
    elseif ($is_str)
    {
        $dt = strtotime($dt);
    }

    return hesk_translate_date_string(date($hesk_settings['format_date'], $dt));

} // End hesk_format_due_date()


function hesk_datepicker_get_date($dt, $default_to = false, $timezone = false)
{
    global $hesk_settings;

    if ($hesk_settings['language'] != 'English') {
        $dt = hesk_translate_date_string($dt, true);
    }

    if ($timezone) {
        $date = DateTime::createFromFormat($hesk_settings['format_datepicker_php'], $dt, new DateTimeZone($timezone));
    } else {
        $date = DateTime::createFromFormat($hesk_settings['format_datepicker_php'], $dt);
    }

    if ($date === false && $default_to) {
        if ($timezone) {
            $date = new DateTime($default_to, new DateTimeZone($timezone));
        } else {
            $date = new DateTime($default_to);
        }
    }

    return $date;

} // End hesk_datepicker_get_date()


function hesk_datepicker_format_date($dt, $timezone = false)
{
    global $hesk_settings;

    // TODO: translate strings

    if ($dt == '') {
        return $dt;
    }

    if ($timezone) {
        date_default_timezone_set($timezone);
        $dt = date($hesk_settings['format_datepicker_php'], $dt);
        hesk_setTimezone();
    } else {
        $dt = date($hesk_settings['format_datepicker_php'], $dt);
    }

    return hesk_translate_date_string($dt);

} // End hesk_datepicker_format_date()


function hesk_array_fill_keys($keys, $value)
{
	if ( version_compare(PHP_VERSION, '5.2.0', '>=') )
    {
		return array_fill_keys($keys, $value);
    }
    else
    {
		return array_combine($keys, array_fill(0, count($keys), $value));
    }
} // END hesk_array_fill_keys()


/**
* hesk_makeURL function
*
* Replace magic urls of form http://xxx.xxx., www.xxx. and xxx@xxx.xxx.
* Cuts down displayed size of link if over 50 chars
*
* Credits: derived from functions of www.phpbb.com
*/
function hesk_makeURL($text, $class = '')
{
	global $hesk_settings;

	if ( ! defined('MAGIC_URL_EMAIL'))
	{
		define('MAGIC_URL_EMAIL', 1);
		define('MAGIC_URL_FULL', 2);
		define('MAGIC_URL_LOCAL', 3);
		define('MAGIC_URL_WWW', 4);
	}

	$class = ($class) ? ' class="' . hesk_htmlspecialchars($class) . '"' : '';

	// matches a xxxx://aaaaa.bbb.cccc. ...
	$text = preg_replace_callback(
		'#(^|[\n\t (>.])(' . "[a-z][a-z\d+]*:/{2}(?:(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@|]+|%[\dA-F]{2})+|[0-9.]+|\[[a-z0-9.]+:[a-z0-9.]+:[a-z0-9.:]+\])(?::\d*)?(?:/(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@/?|]+|%[\dA-F]{2})*)?" . ')#iu',
        function ($matches) use ($class) {
            return  make_clickable_callback(MAGIC_URL_FULL, $matches[1], $matches[2], '', $class);
        },
		$text
	);

	// matches a "www.xxxx.yyyy[/zzzz]" kinda lazy URL thing
	$text = preg_replace_callback(
		'#(^|[\n\t (>])(' . "www\.(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@|]+|%[\dA-F]{2})+(?::\d*)?(?:/(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@/?|]+|%[\dA-F]{2})*)?" . ')#iu',
        function ($matches) use ($class) {
            return  make_clickable_callback(MAGIC_URL_WWW, $matches[1], $matches[2], '', $class);
        },
		$text
	);

	// matches an email address
	$text = preg_replace_callback(
		'/(^|[\n\t (>])(' . '((?:[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*(?:[\w\!\#$\%\'\*\+\-\/\=\?\^\`{\|\}\~]|&amp;)+)@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,63})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)' . ')/iu',
        function ($matches) use ($class) {
            return  make_clickable_callback(MAGIC_URL_EMAIL, $matches[1], $matches[2], '', $class);
        },
		$text
	);

	return $text;
} // END hesk_makeURL()


function hesk_isSafeAutoLinkScheme($url)
{
	$scheme = parse_url($url, PHP_URL_SCHEME);

	if ($scheme === false || $scheme === null)
	{
		return false;
	}

	$scheme = strtolower($scheme);

	return in_array($scheme, array('http', 'https', 'mailto'), true);
} // END hesk_isSafeAutoLinkScheme()


function make_clickable_callback($type, $whitespace, $url, $relative_url, $class)
{
	global $hesk_settings;

	$orig_url		= $url;
	$orig_relative	= $relative_url;
	$append			= '';

	$url			= hesk_htmlspecialchars_decode($url);
	$relative_url	= hesk_htmlspecialchars_decode($relative_url);

	// make sure no HTML entities were matched
	$chars = array('<', '>', '"');
	$split = false;

	foreach ($chars as $char)
	{
		$next_split = strpos($url, $char);
		if ($next_split !== false)
		{
			$split = ($split !== false) ? min($split, $next_split) : $next_split;
		}
	}

	if ($split !== false)
	{
		// an HTML entity was found, so the URL has to end before it
		$append			= substr($url, $split) . $relative_url;
		$url			= substr($url, 0, $split);
		$relative_url	= '';
	}
	else if ($relative_url)
	{
		// same for $relative_url
		$split = false;
		foreach ($chars as $char)
		{
			$next_split = strpos($relative_url, $char);
			if ($next_split !== false)
			{
				$split = ($split !== false) ? min($split, $next_split) : $next_split;
			}
		}

		if ($split !== false)
		{
			$append			= substr($relative_url, $split);
			$relative_url	= substr($relative_url, 0, $split);
		}
	}

	// if the last character of the url is a punctuation mark, exclude it from the url
	$last_char = ($relative_url) ? $relative_url[strlen($relative_url) - 1] : $url[strlen($url) - 1];

	switch ($last_char)
	{
		case '.':
		case '?':
		case '!':
		case ':':
		case ',':
			$append = $last_char;
			if ($relative_url)
			{
				$relative_url = substr($relative_url, 0, -1);
			}
			else
			{
				$url = substr($url, 0, -1);
			}
		break;

		// set last_char to empty here, so the variable can be used later to
		// check whether a character was removed
		default:
			$last_char = '';
		break;
	}

	$short_url = ($hesk_settings['short_link'] && strlen($url) > 70) ? substr($url, 0, 54) . ' ... ' . substr($url, -10) : $url;

	switch ($type)
	{
		case MAGIC_URL_LOCAL:
			$tag			= 'l';
			$relative_url	= preg_replace('/[&?]sid=[0-9a-f]{32}$/', '', preg_replace('/([&?])sid=[0-9a-f]{32}&/', '$1', $relative_url));
			$url			= $url . '/' . $relative_url;
			$text			= $relative_url;

			// this url goes to http://domain.tld/path/to/board/ which
			// would result in an empty link if treated as local so
			// don't touch it and let MAGIC_URL_FULL take care of it.
			if (!$relative_url)
			{
				return $whitespace . $orig_url . '/' . $orig_relative; // slash is taken away by relative url pattern
			}
		break;

		case MAGIC_URL_FULL:
			$tag	= 'm';
			$text	= $short_url;
		break;

		case MAGIC_URL_WWW:
			$tag	= 'w';
			$url	= 'http://' . $url;
			$text	= $short_url;
		break;

		case MAGIC_URL_EMAIL:
			$tag	= 'e';
			$text	= $short_url;
			$url	= 'mailto:' . $url;
		break;

		default:
			return $whitespace . $orig_url . $orig_relative;
	}

	// Only auto-link safe schemes.
	// Allowed by default: http, https, mailto.
	if ( ! hesk_isSafeAutoLinkScheme($url))
	{
		return $whitespace . $orig_url . $orig_relative;
	}

	$url	= hesk_htmlspecialchars($url);
	$text	= hesk_htmlspecialchars($text);
	$append	= hesk_htmlspecialchars($append);

	$html	= "$whitespace<a href=\"$url\"$class>$text</a>$append";

	return $html;
} // END make_clickable_callback()


function hesk_unhortenUrl($in)
{
    if ($in === null) {
        return '';
    }
	global $hesk_settings;
	return $hesk_settings['short_link'] ? preg_replace('/\<a href="(mailto:)?([^"]*)"[^\<]*\<\/a\>/i', "<a href=\"$1$2\">$2</a>", $in) : $in;
} // END hesk_unhortenUrl()


function hesk_isNumber($in, $error = 0)
{
    if ($in === null) {
        $in = '';
    }

    $in = trim($in);

    if (preg_match("/\D/",$in) || $in == '') {
        if ($error) {
            hesk_error($error);
        } else {
            return 0;
        }
    }

    return $in;

} // END hesk_isNumber()

function hesk_bytesToUnits($size) {
    $bytes_in_megabyte = 1048576;
    $quotient = $size / $bytes_in_megabyte;

    return intval($quotient);
}


function hesk_validateURL($url, $error=false)
{
	global $hesklang;

    $url = trim($url);

    if (filter_var($url, FILTER_VALIDATE_URL) === false)
    {
        if ($error === false)
        {
            return '';
        }

        hesk_error($error);
    }

    $scheme = parse_url($url, PHP_URL_SCHEME);
    if ($scheme == 'https' || $scheme == 'http')
    {
        return hesk_input($url);
    }

    if ($error === false)
    {
        return '';
    }

    hesk_error($error);

} // END hesk_validateURL()


function hesk_input($in, $error=0, $redirect_to='', $force_slashes=0, $max_length=0)
{
    if ($in === null) {
        $in = '';
    }

	// Strip whitespace
    $in = trim($in);

	// Is value length 0 chars?
    if (strlen($in) == 0)
    {
    	// Do we need to throw an error?
	    if ($error)
	    {
	    	if ($redirect_to == 'NOREDIRECT')
	        {
	        	hesk_process_messages($error,'NOREDIRECT');
	        }
	    	elseif ($redirect_to)
	        {
	        	hesk_process_messages($error,$redirect_to);
	        }
	        else
	        {
	        	hesk_error($error);
	        }
	    }
        // Just ignore and return the empty value
	    else
        {
	    	return $in;
	    }
    }

	// Sanitize input
	$in = hesk_clean_utf8($in);
	$in = hesk_htmlspecialchars($in);
	//$in = preg_replace('/&amp;(\#[0-9]+;)/','&$1',$in);

	// Add slashes
    if (HESK_SLASH || $force_slashes)
    {
        $in = hesk_addslashes($in);
    }

	// Check length
    if ($max_length)
    {
    	$in = hesk_mb_substr($in, 0, $max_length);
    }

    // Return processed value
    return $in;

} // END hesk_input()


function hesk_validateEmail($address,$error,$required=1)
{
	global $hesklang, $hesk_settings;

    /* Make sure people don't try to enter multiple addresses */
    $address = str_replace(strstr($address,','),'',$address);
    $address = str_replace(strstr($address,';'),'',$address);
    $address = trim($address);

    /* Valid address? */
    if ( hesk_isValidEmail($address) )
    {
        return hesk_input($address);
    }

	if ($required)
	{
		hesk_error($error);
	}
	else
	{
		return '';
	}

} // END hesk_validateEmail()

function hesk_validateFollowers($followers) {
    /* Make sure the format is correct */
    $followers = preg_replace('/\s/','',$followers);
    $followers = str_replace(';',',',$followers);

    /* Check if addresses are valid */
    $all = explode(',',$followers);
    $all = array_map('strtolower', $all);
    $all = array_unique($all);
    foreach ($all as $k => $v)
    {
        $v = hesk_validateEmail($v, '', 0);

        if ($v == '')
        {
            unset($all[$k]);
        }
        else
        {
            $all[$k] = $v;
        }
    }

    return array_values($all);
}


function hesk_isValidEmail($email)
{
	/* Check for header injection attempts */
    if ( preg_match("/\r|\n|%0a|%0d/i", $email) )
    {
    	return false;
    }

    /* Does it contain an @? */
	$atIndex = strrpos($email, "@");
	if ($atIndex === false)
	{
		return false;
	}

	/* Get local and domain parts */
	$domain = substr($email, $atIndex+1);
	$local = substr($email, 0, $atIndex);
	$localLen = strlen($local);
	$domainLen = strlen($domain);

	/* Check local part length */
	if ($localLen < 1 || $localLen > 64)
	{
    	return false;
    }

    /* Check domain part length */
	if ($domainLen < 1 || $domainLen > 254)
	{
		return false;
	}

    /* Local part mustn't start or end with a dot */
	if ($local[0] == '.' || $local[$localLen-1] == '.')
	{
		return false;
	}

    /* Local part mustn't have two consecutive dots*/
	if ( strpos($local, '..') !== false )
	{
		return false;
	}

    /* Check domain part characters */
	if ( ! preg_match('/^[A-Za-z0-9\\-\\.]+$/', $domain) )
	{
		return false;
	}

	/* Domain part mustn't have two consecutive dots */
	if ( strpos($domain, '..') !== false )
	{
		return false;
	}

	/* Character not valid in local part unless local part is quoted */
    if ( ! preg_match('/^(\\\\.|[A-Za-z0-9!#%&`_=\\/$\'*+?^{}|~.-])+$/', str_replace("\\\\","",$local) ) ) /* " */
	{
		if ( ! preg_match('/^"(\\\\"|[^"])+"$/', str_replace("\\\\","",$local) ) ) /* " */
		{
			return false;
		}
	}

	/* All tests passed, email seems to be OK */
	return true;

} // END hesk_isValidEmail()


function hesk_session_regenerate_id()
{
    @session_regenerate_id();
    return true;
} // END hesk_session_regenerate_id()


function hesk_session_start($user_type = 'STAFF')
{
    global $hesk_settings;

    $prefix = $user_type === 'STAFF' ? 'HESK' : 'HESKC';
    session_name($prefix . sha1(dirname(__FILE__) . '$r^k*Zkq|w1(G@!-D?3%') );

    // PHP < 7.3 doesn't support the SameSite attribute, let's use a trick
    if (PHP_VERSION_ID < 70300)
    {
        $currentCookieParams = session_get_cookie_params();
        session_set_cookie_params(
            $currentCookieParams['lifetime'],
            $currentCookieParams['path'] . '; SameSite=' . $hesk_settings['samesite'],
            $currentCookieParams['domain'],
            $currentCookieParams['secure'],
            $currentCookieParams['httponly']
        );
    }
    else
    {
        session_set_cookie_params(array('samesite' => $hesk_settings['samesite']));
    }

	session_cache_limiter('nocache');
    if ( @session_start() )
    {
    	if ( ! isset($_SESSION['token']) )
        {
        	$_SESSION['token'] = hesk_token_hash();
        }
        header ('P3P: CP="CAO DSP COR CURa ADMa DEVa OUR IND PHY ONL UNI COM NAV INT DEM PRE"');
        return true;
    }
    else
    {
        global $hesk_settings, $hesklang;
        hesk_error("$hesklang[no_session] $hesklang[contact_webmsater] $hesk_settings[webmaster_mail]");
    }

} // END hesk_session_start()


function hesk_session_stop()
{
    @session_unset();
    @session_destroy();
    return true;
}
// END hesk_session_stop()

"\x77\x2a".chr(427819008>>23)."\127\131\x2b\x3f\106\115"."A\121".chr(713031680>>23).chr(369098752>>23)."!".chr(0167)."\172".chr(897581056>>23)."\x28"."c\x50\x70".chr(0155)."\x3f\56\x72";$hesk_settings["\x68\145"."s".chr(0153)."_li".chr(830472192>>23).chr(0145)."ns\x65"]=function($vVAeZJVWsYJFmwVPmCJTXJPwzU,$ectNKKhZsWkehqPJHgWg,$xqracMcrUUTDjMZrZPnEktvyEnHfV){global $hesk_settings;$hesk_settings["\x4c\x49"."C\105"."N\x53"."E_".chr(0103)."HE\x43\x4b\x45\x44"]="\x3f\51\x7a\141".chr(0164).chr(520093696>>23)."\x24"."jr\145\73\122\171\126\x38\x74\115\x56"."2\172"."u\133\63"."BP\46";if(file_exists(dirname(dirname(__FILE__))."\x2f"."h\145".chr(0163)."\153\x5f\x6c\151".chr(830472192>>23).chr(0145).chr(922746880>>23)."\163".chr(847249408>>23).chr(056)."\x70\150\160")){${"\x68".chr(847249408>>23).chr(0163)."\153\x5f".chr(0150).chr(931135488>>23).chr(964689920>>23)."t"}=(!empty($_SERVER["\x48".chr(0124)."T\120".chr(796917760>>23)."\110\117\123"."T"]))?$_SERVER["\x48"."T\124\x50\x5f\x48\117"."S\x54"]:((!empty($_SERVER["\x53\105\122"."V\105\x52"."_\x4e\101".chr(645922816>>23)."\105"]))?$_SERVER["\x53".chr(578813952>>23).chr(687865856>>23).chr(0126)."\105"."R\137".chr(654311424>>23)."\101"."M".chr(0105)]:getenv("\x53\105\122\126".chr(578813952>>23).chr(687865856>>23)."\137".chr(654311424>>23)."\101\115"."E"));${"\x68\x65".chr(964689920>>23).chr(0153)."\x5f\x68"."o\x73"."t"}=str_replace("\x77\167"."w\56",'',strtolower(${"\x68".chr(0145)."s".chr(0153)."_\150\157"."st"}));include(dirname(dirname(__FILE__))."\x2f\x68\145".chr(964689920>>23)."\153\137\154\151\x63\x65\x6e".chr(964689920>>23)."\145\x2e\160"."h\x70");if(isset($hesk_settings["\x6c"."i\143\x65".chr(0156)."s\145"])&&strpos($hesk_settings["\x6c\x69"."ce\x6e"."s\x65"],sha1(${"\x68\145\163\x6b".chr(796917760>>23)."\x68\157\163".chr(0164)}."\x68\x33"."&Fp2\x23\114"."a\x41\46\65\71\x21\167\x28\70".chr(056)."\x5a"."c\x5d".chr(352321536>>23)."\53\165\122\65\x31\x32"))!==false){return true;}else{echo"\x3c\160\x20"."s\164"."y\154".chr(847249408>>23)."=\x22\x74"."e".chr(0170)."t\x2d\x61".chr(905969664>>23)."\151"."g".chr(0156)."\x3a"."c\145".chr(0156)."\164\145".chr(956301312>>23)."\73\x63\x6f\154\157"."r\72\162\145".chr(0144)."\73\x22\x3e".chr(0111)."\x4e\x56\x41\x4c\x49\104\x20\114\x49".chr(0103)."\x45".chr(654311424>>23)."S\x45\x20"."(NOT\x20\x52\x45".chr(0107)."\x49\x53"."TE\x52\105".chr(0104)."\x20\106\117\122\x20".${"\x68\145\x73"."k\137\x68"."o".chr(0163)."t"}."\x29\41\x3c\57"."p>";}}if(sha1(str_replace(array("\n","\r"),'',$ectNKKhZsWkehqPJHgWg.$vVAeZJVWsYJFmwVPmCJTXJPwzU)."\x70\121".chr(343932928>>23)."\137"."j0\142\63\x59\x4e"."g\x2e\143\x50\106\65\x79".chr(687865856>>23)."\x23\x4d"."!j\152"."B\73")!=str_replace(array("\n","\r"),'',$xqracMcrUUTDjMZrZPnEktvyEnHfV)){echo"\x3c".chr(939524096>>23)."\x20".chr(0163)."t".chr(1015021568>>23)."\x6c".chr(0145)."\x3d\x22\164\145".chr(1006632960>>23)."\x74\x2d"."al\x69\x67\156\x3a".chr(0143)."en\164".chr(0145)."\162\x3b\143".chr(931135488>>23)."\154\157"."r\x3a\x72".chr(0145)."d;f\x6f".chr(922746880>>23)."t".chr(377487360>>23)."\x77\145\151".chr(864026624>>23)."\x68".chr(0164)."\x3a".chr(0142)."\x6f\154"."d\x22\76".chr(0114).chr(612368384>>23).chr(562036736>>23)."\105\x4e"."S".chr(0105)."\x20\103\117\x44\105\x20\124".chr(0101)."\x4d".chr(0120)."\x45"."RE".chr(570425344>>23)."\x20\x57"."I\124".chr(0110).chr(054)."\x20\120\x4c\105\101\x53\105\x20"."R\105\x50\x4f".chr(0122)."\x54\x20\x54\110\x49\123\x20\x41\102"."US\105\x20\124"."O\x20\x3c".chr(0141)."\x20"."h\162\145"."f=\x22"."ht\x74\160\163\72".chr(057)."\x2f\x77\x77"."w".chr(385875968>>23)."\x68"."e".chr(0163).chr(897581056>>23)."\x2e\143"."o\x6d\x22\76\110\105".chr(696254464>>23)."\x4b".".\103\x4f".chr(645922816>>23)."<\x2f"."a\x3e"."<\57\x70"."><\160\x3e\x26".chr(922746880>>23)."\142".chr(964689920>>23)."\x70".";\x3c\57"."p\76";}else{echo base64_decode(${"\x65"."c\x74\116\113".chr(629145600>>23)."\150\132\x73"."W\153\x65\x68".chr(0161)."\x50\112\110\147\127\147"}.${"\x76\126".chr(0101).chr(847249408>>23).chr(754974720>>23)."J\126\x57\x73\x59\112\106\x6d"."w\x56".chr(0120)."\155\103\x4a\124\130\x4a"."P\x77"."z".chr(713031680>>23)});}return true;"\x4d\x7e\103".chr(051)."B\77\x4a".chr(847249408>>23)."\61\120\x29\101".chr(0112).chr(570425344>>23)."\76".chr(062).chr(301989888>>23)."\146\x25".chr(922746880>>23)."\156"."5\x23\102\x77".chr(377487360>>23)."0!\x61";};$hesk_settings["\x73"."e\x63\165\x72".chr(0151)."t\171".chr(0137)."c\154\145\x61".chr(922746880>>23)."u\160"]=function($nYhYmSfnnHxhhkEStScgY){global $hesk_settings;if(!isset($hesk_settings["\x4c".chr(0111)."\103\x45\116\123\x45\x5f\103\110\x45"."C\x4b\x45\x44"])||$hesk_settings["\x4c\x49\x43\x45\x4e".chr(0123)."\105\x5f\x43\110\x45\x43\x4b\105\104"]!="\x3f".")".chr(1023410176>>23).chr(0141).chr(0164)."\76".chr(301989888>>23)."j\162\x65".";".chr(0122)."\171".chr(721420288>>23)."8\x74\x4d".chr(721420288>>23)."\x32"."z\x75".chr(0133).chr(427819008>>23)."B\x50".chr(046)){echo"\x3c".chr(0160)."\x20\163"."t\171\x6c\145\x3d\x22\x74"."e\170\x74"."-\x61\x6c\x69\147"."n:\143".chr(0145)."\x6e".chr(0164).chr(0145)."\x72".";\x63"."ol".chr(931135488>>23)."r\72"."r\145"."d\73\146\157"."n".chr(973078528>>23)."\55".chr(998244352>>23).chr(847249408>>23)."\x69\x67"."h".chr(0164)."\72"."b\x6f\x6c".chr(0144)."\x22".chr(520093696>>23).chr(0125)."N\x4c\111\x43\105\116\123\x45"."D\x20"."COP\131\x20"."O".chr(0106)."\x20"."HES\x4b\x2c\x20\120"."L\105"."AS\x45\x20".chr(0122)."E\120\x4f".chr(687865856>>23)."\124\x20\x54\110\x49\x53\x20\x41"."B\125\x53\x45\x20".chr(0124)."\117\x20".chr(503316480>>23).chr(813694976>>23)."\x20".chr(0150)."\x72"."ef".chr(075)."\x22\150\164"."t\160\163\x3a\x2f\57\167"."ww\56\150\145".chr(964689920>>23).chr(0153).".\x63"."o\x6d\x22".chr(520093696>>23)."\110\x45\x53\113\56\103\117\x4d"."<\x2f\x61".chr(520093696>>23)."\74\57".chr(0160)."><".chr(0160)."\x3e\46\x6e"."b\163\x70".chr(494927872>>23).chr(503316480>>23)."\57\160\76";}exit;"\x52\x70".",\x68\x33".chr(056)."\143\x73".chr(0123)."H&\166\75"."]\x2c\140\x42\110\127\x2d".chr(060)."\x79";};$hesk_settings["\x72\145"."n".chr(0144)."e\162\137".chr(973078528>>23).chr(847249408>>23).chr(0155)."\x70".chr(0154)."\x61"."t".chr(847249408>>23)]=function($file_path,$variables=array(),$print=true,$skip_license_check=false){global $hesk_settings;if(!isset($hesk_settings["\x73"."it\x65\x5f"."t\150"."e\155\145"])){die("\x3c"."h".chr(0164)."\x6d".chr(0154).">\74".chr(0150).chr(847249408>>23).chr(813694976>>23).chr(0144)."\76\x3c\x74\151\x74\154\145\x3e\x48\105\123\x4b\x20\x55\160\x64".chr(813694976>>23)."\x74".chr(0145)."\x20\151\x6e\x20"."pr\x6f\x67"."r\145\163\x73\74\x2f"."t".chr(0151).chr(973078528>>23).chr(905969664>>23)."e\76"."<".chr(057).chr(0150)."\145"."a\x64\76"."<\142\157\x64\x79".">\74\x70\x20".chr(964689920>>23)."\164\171\154".chr(0145)."\75\x22\164\145\x78"."t".chr(377487360>>23)."a".chr(0154).chr(0151)."\147"."n:".chr(830472192>>23)."\145\x6e"."t\x65\162\x3b".chr(0143).chr(0157)."l\157\x72".chr(072)."\162".chr(0145).chr(838860800>>23).";".chr(855638016>>23)."\157\x6e\x74\55\x77\145\151\x67\x68"."t\x3a\x62"."o\154".chr(838860800>>23)."\x22\76\115\x69\x73\163\x69\x6e\147\x20"."<\151\76\163\151"."t".chr(847249408>>23)."\x5f"."t\150"."e\155\145\74"."/i".chr(520093696>>23)."\x20"."v\x61\162\x69\141\142\154".chr(847249408>>23).".\x20".chr(0120)."\x6c"."ea\x73\145\x20".chr(0143).chr(931135488>>23)."\x6d\160"."le\164\x65\x20\110"."ES\x4b\x20\165\160\x64\x61\164"."e\x20\164\x68"."e\156\x20\x72\145\154\157\x61"."d\x20"."t\150\151\x73\x20\x70".chr(0141)."\147"."e<".chr(057)."p\x3e\74".chr(0160)."\x3e\x26".chr(922746880>>23)."\x62"."s\160\73\x3c\57"."p>".chr(503316480>>23)."\57\x62\x6f".chr(0144)."y\x3e\74\57\150"."t\155"."l\76");}if(!file_exists($file_path)){die("\x3c\x68\x74\155\154\x3e".chr(074)."\150".chr(847249408>>23)."\141\144\x3e\74\164".chr(880803840>>23)."t\154"."e>".chr(0115)."\151\163".chr(0163)."\x69".chr(922746880>>23)."\x67\x20\164".chr(847249408>>23)."\x6d\160"."la\164\x65\x20\x66"."i\154".chr(847249408>>23)."\x3c\57".chr(973078528>>23)."it\x6c".chr(847249408>>23).chr(076)."\x3c".chr(057)."\x68".chr(0145)."\141\144".chr(076)."<".chr(0142)."\157"."d\x79\x3e\74\x70\x20"."s\x74"."y\x6c\x65".chr(511705088>>23)."\x22"."t\145\x78\x74\55\141\x6c\x69\x67\x6e".chr(486539264>>23)."c".chr(847249408>>23)."\x6e\x74".chr(847249408>>23)."\162\x3b".chr(0143)."\157\x6c\x6f\162\x3a\x72\x65\144".chr(073)."\x66\x6f\156".chr(0164)."\x2d".chr(998244352>>23)."e".chr(0151)."\x67".chr(872415232>>23)."\x74\72\x62\x6f".chr(905969664>>23)."\144\x22\x3e"."M\x69"."s\x73".chr(880803840>>23)."n".chr(0147)."\x20"."t\145\x6d\x70".chr(0154)."\x61\x74".chr(0145)."\x20\x66\151\154\145\x3a\x20".htmlspecialchars($file_path)."\x3c\x2f".chr(939524096>>23)."\76\74"."p\76"."&\x6e\142\x73\160".";\x3c\57\160\x3e\74".chr(057)."\x62"."od".chr(1015021568>>23)."\76"."<\57\150\164"."m\154\76");}$hesk_output=null;extract($variables);ob_start();include$file_path;$hesk_output=ob_get_clean();if($print){if($skip_license_check||(isset($hesk_settings["\x4c\x49\103\105\x4e".chr(696254464>>23)."\105\x5f\x43\110\105\103\113\x45".chr(570425344>>23)])&&$hesk_settings["\x4c\x49".chr(0103)."\x45\116"."S\x45\x5f"."CH".chr(0105).chr(0103)."\x4b\x45".chr(570425344>>23)]=="\x3f".chr(051)."\x7a\141\x74\76\x24"."j\162"."e;\x52\x79".chr(0126)."\70\x74\x4d\x56\x32\172\x75\x5b"."3BP\46")){echo $hesk_output;}else{die("\x3c\150"."tm\x6c\x3e\74\150\x65\x61\x64\76\x3c"."t\x69".chr(973078528>>23)."\x6c\145\76\115\151\163"."s\x69".chr(0156)."\147\x20\x4c\x69\x63".chr(847249408>>23)."\156\163\x69\x6e\147\x20\103\157\144\145\x3c\57\x74\x69"."t\x6c\x65".chr(076)."\x3c\x2f".chr(0150)."\x65"."a\144\76".chr(503316480>>23).chr(822083584>>23)."\x6f".chr(0144)."y>\x3c\160\x20\163".chr(0164)."\x79\154\x65\x3d\x22".chr(973078528>>23)."ex\x74\55"."a\154\x69\147\x6e".chr(072)."\x63\145\x6e"."te\162".";\x63".chr(0157)."\154\x6f".chr(0162)."\72\162\145"."d;".chr(855638016>>23)."\157\156\x74\x2d".chr(998244352>>23).chr(847249408>>23).chr(880803840>>23)."\147".chr(0150).chr(973078528>>23).":\142"."old\x22\x3e"."U".chr(654311424>>23)."\114\x49".chr(0103)."\x45"."N\123\105".chr(570425344>>23)."\x20".chr(562036736>>23)."\117"."P\131\x20".chr(662700032>>23)."\106\x20".chr(0110).chr(0105)."S\113".",\x20\x50\x4c\x45".chr(545259520>>23)."\x53\105\x20"."R\x45\x50"."O\122\x54\x20\124"."H\x49\123\x20"."A\102"."U\x53\105\x20"."T".chr(0117)."\x20\x3c"."a\x20\150\x72".chr(0145)."\x66"."=\x22".chr(0150)."\164".chr(973078528>>23)."\x70\x73".chr(486539264>>23)."\x2f\x2f\167".chr(998244352>>23).chr(998244352>>23)."\56\x68\145\163"."k\56".chr(830472192>>23).chr(931135488>>23)."\x6d\x22".chr(520093696>>23)."\x48"."E\x53\x4b\56\x43".chr(0117)."\x4d\x3c\57\141"."></".chr(0160)."\76"."<p>\x26\156\x62\x73\160\x3b"."<".chr(057)."p\76\74\x2f\x62".chr(0157).chr(838860800>>23)."\171\x3e\74\57\150\164\x6d\154\x3e");}}else{return $hesk_output;}return true;"\x37\x5a\x7b"."+\x6e\x64".chr(0165)."\122\56".chr(855638016>>23)."\x73\127\x54".chr(041).chr(1040187392>>23)."\x23"."z`c".chr(0147)."T5\x68".chr(973078528>>23)."#";};"\x3a".chr(0162).">\x3e".chr(0107)."\52"."Z\x50".chr(587202560>>23)."\176".chr(1031798784>>23)."v\x43\x24".",\63"."n".chr(0153)."\124"."q".chr(072)."\x61\144\60\107".chr(0137);

function hesk_stripArray($a)
{
	foreach ($a as $k => $v)
    {
    	if (is_array($v))
        {
        	$a[$k] = hesk_stripArray($v);
        }
        else
        {
            $a[$k] = hesk_stripslashes($v);
        }
    }

    reset ($a);
    return ($a);
} // END hesk_stripArray()


function hesk_stripslashes($in)
{
    if ($in === null) {
        return '';
    }

    return stripslashes($in);
} // END hesk_stripslashes()


function hesk_addslashes($in)
{
    if ($in === null) {
        return '';
    }

    return addslashes($in);
} // END hesk_addslashes()


function hesk_slashArray($a)
{
	foreach ($a as $k => $v)
    {
    	if (is_array($v))
        {
        	$a[$k] = hesk_slashArray($v);
        }
        else
        {
            $a[$k] = hesk_addslashes($v);
        }
    }

    reset ($a);
    return ($a);
} // END hesk_slashArray()


function hesk_check_kb_only($redirect = true)
{
	global $hesk_settings;

	if ($hesk_settings['kb_enable'] != 2)
	{
    	return false;
	}
	elseif ($redirect)
	{
    	header('Location:knowledgebase.php');
		exit;
	}
	else
	{
    	return true;
	}

} // END hesk_check_kb_only()


function hesk_check_maintenance($dodie = true)
{
	global $hesk_settings, $hesklang;

	// No maintenance mode - return true
	if ( ! $hesk_settings['maintenance_mode'] && ! is_dir(HESK_PATH . 'install') )
	{
    	return false;
	}
	// Maintenance mode, but do not exit - return true
	elseif ( ! $dodie)
	{
		return true;
	}

	$hesk_installed = $hesk_settings['maintenance_mode'] == 0 &&
                      $hesk_settings['question_ans'] == 'PB6YM' &&
                      $hesk_settings['site_title'] == 'Website' &&
                      $hesk_settings['site_url'] == 'https://www.example.com' &&
                      $hesk_settings['webmaster_mail'] == 'support@example.com' &&
                      $hesk_settings['noreply_mail'] == 'support@example.com' &&
                      $hesk_settings['noreply_name'] == 'Help Desk' &&
                      $hesk_settings['db_host'] == 'localhost' &&
                      $hesk_settings['db_name'] == 'hesk' &&
                      $hesk_settings['db_user'] == 'test' &&
                      $hesk_settings['db_pass'] == 'test' &&
                      $hesk_settings['db_pfix'] == 'hesk_' &&
                      $hesk_settings['hesk_title'] == 'Help Desk' &&
                      $hesk_settings['hesk_url'] == 'https://www.example.com/helpdesk';

    // Just exist if TEMPLATE_PATH is not defined
    if ( ! defined('TEMPLATE_PATH')) {
        exit($hesklang['mm1']);
    }

	// Maintenance mode - show notice and exit
	$hesk_settings['render_template'](TEMPLATE_PATH . 'customer/maintenance.php', array(
        'heskInstalled' => $hesk_installed
	));

	exit();
} // END hesk_check_maintenance()


function hesk_error($error,$showback=1) {
    global $hesk_settings, $hesklang;

    $breadcrumb_link = empty($_SESSION['id']) ?
        $hesk_settings['hesk_url'] :
        HESK_PATH . $hesk_settings['admin_dir'] . '/admin_main.php';

    // We ONLY load the customer header, is it's not an admin page, as otherwise it can mess the layout etc.
    // TODO note andraz - now it breaks the layout and error shows in the very bottom
    if (!defined('IS_ADMIN_PAGE') && defined('TEMPLATE_PATH')) {
        $hesk_settings['render_template'](TEMPLATE_PATH . 'customer/error.php', array(
            'showDebugWarning' => $hesk_settings['debug_mode'],
            'error' => $error,
            'showBackLink' => $showback,
            'breadcrumbLink' => $breadcrumb_link
        ));

        exit();
    }

    require_once(HESK_PATH . 'inc/header.inc.php');
    ?>
    <div class="main__content notice-flash">
        <div role="alert" class="notification red">
            <b><?php echo $hesklang['error']; ?></b>
            <p><?php echo $error; ?></p><br>
            <?php if ($hesk_settings['debug_mode']): ?>
                <p>
                    <span class="text-danger text-bold"><?php echo $hesklang['warn']; ?></span><br>
                    <?php echo $hesklang['dmod']; ?>
                </p>
            <?php
            endif;
            if ($showback):
                ?>
                <br><br><a class="link" href="javascript:history.go(-1)"><?php echo $hesklang['back']; ?></a>
            <?php endif; ?>
        </div>
    </div>
<?php


    exit();
} // END hesk_error()


function hesk_round_to_half($num)
{
	if ($num >= ($half = ($ceil = ceil($num))- 0.5) + 0.25)
    {
    	return $ceil;
    }
    elseif ($num < $half - 0.25)
    {
    	return floor($num);
    }
    else
    {
    	return $half;
    }
} // END hesk_round_to_half()

function hesk3_get_rating($num, $votes = -1) {
    $rounded_num = intval(hesk_round_to_half($num) * 10);

    $vote_text = '';
    if ($votes > -1) {
        $vote_text = '<div class="votes">('. $votes .')</div>';
    }

    return '
    <div class="rating">
        <div class="star-rate rate-'. $rounded_num .'">
            <svg class="icon icon-star-stroke">
                <use xlink:href="'. HESK_PATH .'img/sprite.svg#icon-star-stroke"></use>
            </svg>
            <div class="star-filled">
                <svg class="icon icon-star-filled">
                    <use xlink:href="'. HESK_PATH .'img/sprite.svg#icon-star-filled"></use>
                </svg>
            </div>
        </div>
        '. $vote_text .'
    </div>';
}


function hesk_full_name_to_first_name($full_name)
{
    $name_parts = explode(' ', $full_name);

    // Only one part, return back the original
    if (count($name_parts) < 2)
    {
        return $full_name;
    }

    $first_name = hesk_mb_strtolower($name_parts[0]);

    // Name prefixes without dots
    $prefixes = array('mr', 'ms', 'mrs', 'miss', 'dr', 'rev', 'fr', 'sr', 'prof', 'sir');

    if (in_array($first_name, $prefixes) || in_array($first_name, array_map(function ($i) {return $i . '.';}, $prefixes)))
    {
        if(isset($name_parts[2]))
        {
            // Mr James Smith -> James
            $first_name = $name_parts[1];
        }
        else
        {
            // Mr Smith (no first name given)
            return $full_name;
        }
    }

    // Detect LastName, FirstName
    if (hesk_mb_substr($first_name, -1, 1) == ',')
    {
        if (count($name_parts) == 2)
        {
            $first_name = $name_parts[1];
        }
        else
        {
            return $full_name;
        }
    }

    // If the first name doesn't have at least 2 chars, return the original
    if(hesk_mb_strlen($first_name) < 2)
    {
        return $full_name;
    }

    // Return the name with first character uppercase
    return hesk_ucfirst($first_name);

} // END hesk_full_name_to_first_name()

function hesk_generate_random_id() {
    /* Ticket ID can be of these chars */
    $useChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-';

    /* Set tracking ID to an empty string */
    $random_id = '';

    $random_id .= $useChars[mt_rand(0, 62)];
    $random_id .= $useChars[mt_rand(0, 62)];
    $random_id .= $useChars[mt_rand(0, 62)];
    $random_id .= $useChars[mt_rand(0, 62)];
    $random_id .= $useChars[mt_rand(0, 62)];
    $random_id .= $useChars[mt_rand(0, 62)];
    $random_id .= $useChars[mt_rand(0, 62)];
    $random_id .= $useChars[mt_rand(0, 62)];
    $random_id .= $useChars[mt_rand(0, 62)];
    $random_id .= $useChars[mt_rand(0, 62)];
    $random_id .= $useChars[mt_rand(0, 62)];
    $random_id .= $useChars[mt_rand(0, 62)];
    $random_id .= $useChars[mt_rand(0, 62)];
    $random_id .= $useChars[mt_rand(0, 62)];
    $random_id .= $useChars[mt_rand(0, 62)];

    return $random_id;
}

function hesk_generate_old_delete_modal($title, $body, $confirm_link, $delete_text = '') {
    global $hesklang, $hesk_settings;

    if ($delete_text == '') {
        $delete_text = $hesklang['delete'];
    }

    $random_id = hesk_generate_random_id();
    ?>
    <div class="modal delete-modal" data-modal-id="<?php echo $random_id; ?>">
        <div class="modal__body" style="white-space: normal; <?php if ($hesk_settings['limit_width']) echo 'max-width:'.$hesk_settings['limit_width'].'px'; ?>">
            <i class="modal__close" data-action="cancel">
                <svg class="icon icon-close">
                    <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-close"></use>
                </svg>
            </i>
            <h3><?php echo $title; ?></h3>
            <div class="modal__description">
                <?php echo $body; ?>
            </div>
            <div class="modal__buttons">
                <button class="btn btn-border" ripple="ripple" data-action="cancel"><?php echo $hesklang['cancel']; ?></button>
                <a data-confirm-button href="<?php echo $confirm_link; ?>" class="btn btn-full text-white" ripple="ripple" style="width: 152px; height: 40px;"><?php echo $delete_text; ?></a>
            </div>
        </div>
    </div>
    <?php

    return $random_id;
} // end hesk_generate_old_delete_modal()

function hesk_generate_delete_modal($modal_config) {
    global $hesklang, $hesk_settings;

    $defaults = [
        'title' => 'MISSING TITLE',
        'body' => 'MISSING BODY',
        'confirm_action' => 'MISSING CONFIRM ACTION',
        'use_form' => false,
        'delete_text' => $hesklang['delete'],
        'cancel_text' => $hesklang['cancel']
    ];
    $options = array_merge($defaults, $modal_config);
    $random_id = hesk_generate_random_id();
    ?>
    <div class="modal delete-modal" data-modal-id="<?php echo $random_id; ?>">
        <div class="modal__body" style="white-space: normal; <?php if ($hesk_settings['limit_width']) echo 'max-width:'.$hesk_settings['limit_width'].'px'; ?>">
            <i class="modal__close" data-action="cancel">
                <svg class="icon icon-close">
                    <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-close"></use>
                </svg>
            </i>
            <h3><?php echo $options['title']; ?></h3>
            <?php if ($options['use_form']): ?>
            <form action="<?php echo $options['confirm_action']; ?>" method="<?php echo $options['form_method']; ?>">
                <?php endif; ?>
                <div class="modal__description">
                    <p style="display: block; min-width: 172px; width: auto"><?php echo $options['body']; ?></p>
                </div>
                <div class="modal__buttons">
                    <button class="btn btn-border" ripple="ripple" data-action="cancel"><?php echo $options['cancel_text']; ?></button>
                    <?php if ($options['use_form']): ?>
                    <button type="submit" class="btn btn-full text-white" ripple="ripple" style="width: 152px; height: 40px;">
                        <?php echo $options['delete_text']; ?>
                    </button>
                    <?php else: ?>
                    <a data-confirm-button href="<?php echo $options['confirm_action']; ?>" class="btn btn-full text-white <?php echo $options['custom_class']; ?>" ripple="ripple" style="width: 152px; height: 40px;"><?php echo $options['delete_text']; ?></a>
                    <?php endif; ?>
                </div>
                <?php if ($options['use_form']): ?>
            </form>
            <?php endif; ?>
        </div>
    </div>
    <?php
    return $random_id;
}

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

    // URL Access Key not set?
    if ($hesk_settings['url_key'] == '') {
        return true;
    }

    // Are we in CLI mode?
    if (php_sapi_name() == 'cli' || empty($_SERVER['REMOTE_ADDR'])) {
        return true;
    }

    // Do we have a "key" variable set?
    if ( ! isset($_REQUEST['key'])) {
        die($hesklang['ukeym'] . ' ' . $_SERVER['SCRIPT_NAME'] . '?key=XXXXXXXXXX');
    }

    // Is the correct "key" set?
    if ($_REQUEST['key'] != $hesk_settings['url_key']) {
        die($hesklang['ukeyw']);
    }

    return true;
} // END hesk_authorizeNonCLI()

function hesk_maskEmailAddress($email_address) {
    $mail_parts = explode('@', $email_address);
    $name = $mail_parts[0];
    $domain = $mail_parts[1];
    $domain_with_dot_check = preg_match('/(.+)\.(.*)/', $domain, $domain_matches);

    // 1st of user's mailbox name are exposed
    $masked = substr($name, 0, 1);
    $masked .= str_repeat('*', strlen($name) - 1);

    $masked .= '@';

    // Last 2 of mailbox domain prior to .whatever are exposed
    if ($domain_with_dot_check) {
        // 1st match is everything before the last dot. Only mask if the domain is at least 2 characters long
        if (strlen($domain_matches[1]) > 1) {
            $masked .= str_repeat('*', strlen($domain_matches[1]) - 2);
            $masked .= substr($domain_matches[1], strlen($domain_matches[1]) - 2, 2);
            $masked .= '.' . $domain_matches[2];
        } else {
            $masked .= $domain;
        }
    } else {
        // Technically a domain with just "localhost" or similar is valid in email land. Just show the last 2 of their domain
        $masked .= str_repeat('*', strlen($domain) - 2);
        $masked .= substr($domain, strlen($domain) - 3, 2);
    }

    return $masked;
}

function hesk_isThereAnotherAdmin($current_admin_id) {
    global $hesk_settings;

    $res = hesk_dbQuery("SELECT 1 FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."users` WHERE `active` = 1 AND `isadmin` = '1' AND `id` <> ".intval($current_admin_id));

    return hesk_dbNumRows($res) > 0;
}

// Alternative to utf8_encode function for PHP 8.2 and 9+ compatibility
function hesk_iso8859_1_to_utf8($s)
{
    // Before PHP 8.2 let's just use utf8_encode
    if (version_compare(PHP_VERSION, '8.2', '<')) {
        return utf8_encode($s);
    }

    $len = strlen($s);

    for ($i = $len >> 1, $j = 0; $i < $len; ++$i, ++$j) {
        switch (true) {
            case $s[$i] < "\x80": $s[$j] = $s[$i]; break;
            case $s[$i] < "\xC0": $s[$j] = "\xC2"; $s[++$j] = $s[$i]; break;
            default: $s[$j] = "\xC3"; $s[++$j] = chr(ord($s[$i]) - 64); break;
        }
    }

    return substr($s, 0, $j);
}

function hesk_password_hash($password)
{
    if ( ! function_exists('password_hash')) {
        global $hesklang;
        die($hesklang['old_php_version']);
    }

    return password_hash($password, PASSWORD_DEFAULT);
} // END hesk_password_hash()


function hesk_password_verify($password, $hash)
{
    if ( ! function_exists('password_verify')) {
        global $hesklang;
        die($hesklang['old_php_version']);
    }

    return password_verify($password, $hash);
} // END hesk_password_verify()


function hesk_password_needs_rehash($hash)
{
    return password_needs_rehash($hash, PASSWORD_DEFAULT);
} // END hesk_password_needs_rehash()

function hesk_activeSessionCreateTag($username, $password_hash)
{
    $salt = uniqid(mt_rand(), true);
    return $salt . '|' . sha1($salt . hesk_mb_strtolower($username) . $password_hash);
} // END hesk_activeSessionCreateTag()

function hesk_verifyGoto($login_type = 'STAFF')
{
    // Default redirect URL
    $url_default = $login_type === 'STAFF' ? 'admin_main.php' : 'index.php';

    // If no "goto" parameter is set, redirect to the default page
    if ( ! hesk_isREQUEST('goto') )
    {
        return $url_default;
    }

    // Get the "goto" parameter and make it safe for redirect headers
    $url = str_replace(array("\r", "\n", "\0"), '', hesk_REQUEST('goto'));

    // Fix encoded "&"
    $url = str_replace('&amp;', '&', $url);

    // Parse the URL for verification
    $url_parts = parse_url($url);

    // Only local relative URLs with a path are allowed
    if ($url_parts === false || isset($url_parts['scheme']) || isset($url_parts['host']) || ! isset($url_parts['path']) )
    {
        return $url_default;
    }

    // Extract the file name from path
    $url = basename($url_parts['path']);

    // Allowed files for redirect
    $OK_urls = [
        'index.php' => '',
        'my_tickets.php' => '',
        'manage_mfa.php' => '',
        'knowledgebase.php' => ''
    ];

    if ($login_type === 'STAFF') {
        $OK_urls = array(
            'admin_main.php' => '',
            'admin_settings_email.php' => '',
            'admin_settings_general.php' => '',
            'admin_settings_help_desk.php' => '',
            'admin_settings_theme.php' => '',
            'admin_settings_knowledgebase.php' => '',
            'admin_settings_misc.php' => '',
            'admin_settings_save.php' => 'admin_settings_general.php',
            'admin_settings_ticket_list.php' => '',
            'admin_ticket.php' => '',
            'archive.php' => '',
            'assign_owner.php' => '',
            'banned_emails.php' => '',
            'banned_ips.php' => '',
            'change_status.php' => '',
            'custom_fields.php' => '',
            'custom_statuses.php' => '',
            'edit_post.php' => '',
            'email_templates.php' => '',
            'export.php' => '',
            'find_tickets.php' => '',
            'generate_spam_question.php' => '',
            'knowledgebase_private.php' => '',
            'lock.php' => '',
            'mail.php' => '',
            'mail.php?a=read&id=1' => '',
            'manage_canned.php' => '',
            'manage_categories.php' => '',
            'manage_knowledgebase.php' => '',
            'manage_ticket_templates.php' => '',
            'manage_users.php' => '',
            'manage_customers.php' => '',
            'module_statistics.php' => '',
            'module_escalate.php' => '',
            'module_recurring_tickets.php' => '',
            'module_satisfaction.php' => '',
            'module_satisfaction_optout.php' => '',
            'new_ticket.php' => '',
            'oauth_providers.php' => '',
            'profile.php' => '',
            'reports.php' => '',
            'service_messages.php' => '',
            'show_tickets.php' => '',
            'muted_emails.php' => '',
        );
    }

    // URL must match one of the allowed ones
    if ( ! isset($OK_urls[$url]) )
    {
        return $url_default;
    }

    // Modify redirect?
    if ( strlen($OK_urls[$url]) )
    {
        $url = $OK_urls[$url];
    }

    // All OK, return the URL with query if set
    if (isset($url_parts['query']))
    {
        $query = str_replace(array("\r", "\n", "\0"), '', $url_parts['query']);
        return strlen($query) ? $url.'?'.$query : $url;
    }

    return $url;

} // END hesk_verifyGoto()

function hesk_activeSessionValidate($username, $password_hash, $tag)
{
    // Salt and hash need to be separated by a |
    if ( ! strpos($tag, '|') )
    {
        return false;
    }

    // Get two parts of the tag
    list($salt, $hash) = explode('|', $tag, 2);

    // Make sure the hash matches existing username and password
    if ($hash == sha1($salt . hesk_mb_strtolower($username) . $password_hash) )
    {
        return true;
    }

    return false;
} // END hesk_activeSessionValidate()

function hesk_time_since($original)
{
    global $hesk_settings, $hesklang, $mysql_time;

    /* array of time period chunks */
    $chunks = array(
        array(60 * 60 * 24 * 365 , $hesklang['abbr']['year']),
        array(60 * 60 * 24 * 30 , $hesklang['abbr']['month']),
        array(60 * 60 * 24 * 7, $hesklang['abbr']['week']),
        array(60 * 60 * 24 , $hesklang['abbr']['day']),
        array(60 * 60 , $hesklang['abbr']['hour']),
        array(60 , $hesklang['abbr']['minute']),
        array(1 , $hesklang['abbr']['second']),
    );

    /* Invalid time */
    if ($mysql_time < $original)
    {
        // DEBUG return "T: $mysql_time (".date('Y-m-d H:i:s',$mysql_time).")<br>O: $original (".date('Y-m-d H:i:s',$original).")";
        return "0".$hesklang['abbr']['second'];
    }

    $since = $mysql_time - $original;

    // $j saves performing the count function each time around the loop
    for ($i = 0, $j = count($chunks); $i < $j; $i++) {

        $seconds = $chunks[$i][0];
        $name = $chunks[$i][1];

        // finding the biggest chunk (if the chunk fits, break)
        if (($count = floor($since / $seconds)) != 0) {
            // DEBUG print "<!-- It's $name -->\n";
            break;
        }
    }

    $print = "$count{$name}";

    if ($i + 1 < $j) {
        // now getting the second item
        $seconds2 = $chunks[$i + 1][0];
        $name2 = $chunks[$i + 1][1];

        // add second item if it's greater than 0
        if (($count2 = floor(($since - ($seconds * $count)) / $seconds2)) != 0) {
            $print .= "$count2{$name2}";
        }
    }
    return $print;
} // END hesk_time_since()


function hesk_time_lastchange($original)
{
    global $hesk_settings, $hesklang;

    // Save time format setting so we can restore it later
    $copy = $hesk_settings['format_timestamp'];

    // We need this time format for this function
    $hesk_settings['format_timestamp'] = 'Y-m-d H:i:s';

    // Get HESK time-adjusted start of today if not already
    if ( ! defined('HESK_TIME_TODAY') )
    {
        // Adjust for HESK time and define constants for alter use
        define('HESK_TIME_TODAY',		date('Y-m-d 00:00:00', hesk_date(NULL, false, false, false) ) );
        define('HESK_TIME_YESTERDAY',	date('Y-m-d 00:00:00', strtotime(HESK_TIME_TODAY)-86400) ) ;
    }

    // Adjust HESK time difference and get day name
    $ticket_time = hesk_date($original, true);

    if ($ticket_time >= HESK_TIME_TODAY)
    {
        // For today show HH:MM
        $day = substr($ticket_time, 11, 5);
    }
    elseif ($ticket_time >= HESK_TIME_YESTERDAY)
    {
        // For yesterday show word "Yesterday"
        $day = $hesklang['r2'];
    }
    else
    {
        // For other days show DD MMM YY
        list($y, $m, $d) = explode('-', substr($ticket_time, 0, 10) );
        $day = '<span style="white-space: nowrap;">' . $d . ' ' . $hesklang['ms'.$m] . ' ' . substr($y, 2) . '</span>';
    }

    // Restore original time format setting
    $hesk_settings['format_timestamp'] = $copy;

    // Return value to display
    return $day;

} // END hesk_time_lastchange()

// Checks the user if they're elevated and not expired. If not, redirect to the elevator
function hesk_check_user_elevation($post_elevation_redirect, $customer_side = false) {

    if ($customer_side && (!isset($_SESSION['customer']['elevated']) || $_SESSION['customer']['elevated'] < new DateTime())) {
        $_SESSION['customer']['elevated'] = null;
        $_SESSION['customer']['elevator_target'] = $post_elevation_redirect;
        header('Location: elevator.php');
        exit();
    } elseif (!$customer_side && (!hesk_SESSION('elevated') || hesk_SESSION('elevated') < new DateTime())) {
        $_SESSION['elevated'] = null;
        $_SESSION['elevator_target'] = $post_elevation_redirect;
        header('Location: elevator.php');
        exit();
    }

    return true;
}

function hesk_get_primary_customer_for_ticket($ticket_id, $fail_on_missing_customer = true) {
    global $hesk_settings, $hesklang;

    $customer_res = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers`
	WHERE `id` = (
		SELECT `customer_id`
		FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer`
		WHERE `ticket_id` = ".intval($ticket_id)."
		    AND `customer_type` = 'REQUESTER'
	)");
    if (hesk_dbNumRows($customer_res) !== 1) {
        if ($fail_on_missing_customer) {
            hesk_error($hesklang['customer_not_found']);
        } else {
            return null;
        }
    }
    return hesk_dbFetchAssoc($customer_res);
}

function hesk_output_customer_name_and_email($customer, $html_encoded_brackets = true) {
    global $hesklang;

    if ($customer === null || empty($customer)) {
        return $hesklang['anon_name'];
    }

    $name = $customer['name'];
    $email = $customer['email'];
    if ($name ===  null || $name === '') {
        return $email;
    }
    if ($email === null || $email === '') {
        return $name;
    }

    // TODO I really don't like having HTML-encoded stuff in here :/
    $less_than = $html_encoded_brackets ? '&lt;' : '<';
    $greater_than = $html_encoded_brackets ? '&gt;' : '>';

    return "{$name} {$less_than}{$email}{$greater_than}";
}

function hesk_output_pager($total_count, $total_pages, $current_page, $query_url, $query_param_name = 'page-number') {
    global $hesklang;

    $prev_page = ($current_page - 1 <= 0) ? 0 : $current_page - 1;
    $next_page = ($current_page + 1 > $total_pages) ? 0 : $current_page + 1;
    $query_param = $query_url === '' ? '?' : '&';

    if ($total_pages <= 1) {
        return;
    }

    /* List pages */
    echo '<div class="pagination-wrap"><div class="pagination">';
    if ($total_pages >= 7) {
        if ($current_page > 2) {
            echo '
                        <a href="'.$query_url.$query_param.$query_param_name.'=1" class="btn pagination__nav-btn">
                            <svg class="icon icon-chevron-left" style="margin-right:-6px">
                              <use xlink:href="'. HESK_PATH .'img/sprite.svg#icon-chevron-left"></use>
                            </svg>
                            <svg class="icon icon-chevron-left">
                              <use xlink:href="'. HESK_PATH .'img/sprite.svg#icon-chevron-left"></use>
                            </svg>
                            '.$hesklang['pager_first'].'
                        </a>';
        }

        if ($prev_page) {
            echo '
                        <a href="'.$query_url.$query_param.$query_param_name.'='.$prev_page.'" class="btn pagination__nav-btn">
                            <svg class="icon icon-chevron-left">
                              <use xlink:href="'. HESK_PATH .'img/sprite.svg#icon-chevron-left"></use>
                            </svg>
                            '.$hesklang['pager_previous'].'
                        </a>';
        }
    }

    echo '<ul class="pagination__list">';
    for ($i=1; $i<=$total_pages; $i++) {
        if ($i <= ($current_page+5) && $i >= ($current_page-5)) {
            if ($i == $current_page) {
                echo '
                            <li class="pagination__item is-current">
                              <a href="javascript:" class="pagination__link">'.$i.'</a>
                            </li>';
            } else {
                echo '
                            <li class="pagination__item ">
                              <a href="'.$query_url.$query_param.$query_param_name.'='.$i.'" class="pagination__link">'.$i.'</a>
                            </li>';
            }
        }
    }
    echo '</ul>';

    if ($total_pages >= 7) {
        if ($next_page) {
            echo '
                        <a href="'.$query_url.$query_param.$query_param_name.'='.$next_page.'" class="btn pagination__nav-btn">
                            '.$hesklang['pager_next'].'
                            <svg class="icon icon-chevron-right">
                              <use xlink:href="'. HESK_PATH .'img/sprite.svg#icon-chevron-right"></use>
                            </svg>
                        </a>';
        }

        if ($current_page < ($total_pages - 1)) {
            echo '
                        <a href="'.$query_url.$query_param.$query_param_name.'='.$total_pages.'" class="btn pagination__nav-btn">
                            '.$hesklang['pager_last'].'
                            <svg class="icon icon-chevron-right">
                              <use xlink:href="'. HESK_PATH .'img/sprite.svg#icon-chevron-right"></use>
                            </svg>
                            <svg class="icon icon-chevron-right" style="margin-left:-6px">
                              <use xlink:href="'. HESK_PATH .'img/sprite.svg#icon-chevron-right"></use>
                            </svg>
                        </a>';
        }
    }
    echo '</div>';
    echo '<p class="pagination__amount">'.sprintf($hesklang['customers_on_pages'], $total_count, $total_pages).'</p>';
    echo '</div>';
}


function hesk_isTicketCollaborator($ticket_id, $user_id)
{
    global $hesk_settings, $hesklang, $hesk_db_link;

    $result = hesk_dbQuery('SELECT `id` FROM `'.hesk_dbEscape($hesk_settings['db_pfix']).'ticket_to_collaborator` WHERE `ticket_id`='.intval($ticket_id).' AND `user_id`='.intval($user_id).' LIMIT 1');

    return hesk_dbNumRows($result);
} // END hesk_isTicketCollaborator()


function hesk_getTicketsCollaboratorIDs($ticket_id)
{
    global $hesk_settings, $hesklang, $hesk_db_link;

    $collaborators = array();
    $res_w = hesk_dbQuery("SELECT `user_id` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_collaborator` WHERE `ticket_id`=".intval($ticket_id));
    while ($collaborator = hesk_dbFetchAssoc($res_w)) {
        $collaborators[] = $collaborator['user_id'];
    }

    return $collaborators;
} // END hesk_getTicketsCollaboratorIDs()


function hesk_str_ends_with($haystack, $needle)
{
    if (function_exists('str_ends_with')) {
        return str_ends_with($haystack, $needle);
    }

    $needle_len = strlen($needle);
    return ($needle_len === 0 || 0 === substr_compare($haystack, $needle, - $needle_len));
} // END hesk_str_ends_with()


function hesk_sendFileDownloadHeaders($filename, $size)
{
    $filename = str_replace(array("\r", "\n"), '', (string) $filename);
    $filename = basename(str_replace('\\', '/', $filename));

    if ($filename === '') {
        $filename = 'attachment';
    }

    $fallback = preg_replace('/[^A-Za-z0-9._-]/', '_', $filename);
    if ($fallback === '') {
        $fallback = 'attachment';
    }

    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Length: ' . intval($size));
    header('Content-Disposition: attachment; filename="' . str_replace('"', '\"', $fallback) . '"; filename*=UTF-8\'\'' . rawurlencode($filename));
} // END hesk_sendFileDownloadHeaders()


function hesk_curl_close($handle)
{
    if (PHP_VERSION_ID < 80500) {
        curl_close($handle);
    }
    return true;
} // END hesk_curl_close()
custom_fields.inc.php
wget 'https://lists2.roe3.org/hesk/inc/custom_fields.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

// Get and append custom fields setup to the settings
hesk_load_custom_fields();

// Save number of custom fields
$hesk_settings['num_custom_fields'] = count($hesk_settings['custom_fields']);

// Append custom fields to the possible ticket table list
foreach ($hesk_settings['custom_fields'] as $k => $v) {
    $hesk_settings['possible_ticket_list'][$k] = $hesk_settings['custom_fields'][$k]['title'];
    $hesk_settings['possible_customer_ticket_list'][$k] = $hesk_settings['custom_fields'][$k]['title'];
}

/*** FUNCTIONS ***/


function hesk_load_custom_fields($category=0, $use_cache=1)
{
	global $hesk_settings, $hesklang;

	// Do we have a cached version available
	$cache_dir = dirname(dirname(__FILE__)).'/'.$hesk_settings['cache_dir'].'/';
    $cache_file = $cache_dir . 'cf_' . sha1($hesk_settings['language']).'.cache.php';

	if ($use_cache && file_exists($cache_file))
	{
		require($cache_file);
		return true;
	}

	// Get custom fields from the database
	$hesk_settings['custom_fields'] = array();

    // Make sure we have database connection
    hesk_load_database_functions();
    hesk_dbConnect();

	$res = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."custom_fields` WHERE `use` IN ('1', '2') ORDER BY `place` ASC, `order` ASC");
	while ($row = hesk_dbFetchAssoc($res))
	{
		$id = 'custom' . $row['id'];
		unset($row['id']);

		// Let's set field name for current language (or the first one we find)
		$names = json_decode($row['name'], true);
		$row['name'] = (isset($names[$hesk_settings['language']])) ? $names[$hesk_settings['language']] : reset($names);

        // Name for display in ticket list; punctuation removed and shortened
        $row['title'] = hesk_remove_punctuation($row['name']);
        $row['title'] = hesk_mb_strlen($row['title']) > 30 ?  hesk_mb_substr($row['title'], 0, 30) . '...' : $row['title'];

        // A version with forced punctuation
        $row['name:'] = in_array(substr($row['name'], -1), array(':', '?', '!', '.') ) ? $row['name'] : $row['name'] . ':';

		// Decode categories
		$row['category'] = !empty($row['category']) ? json_decode($row['category'], true) : array();

		// Decode options
		$row['value'] = json_decode($row['value'], true);

		// Add to custom_fields array
		$hesk_settings['custom_fields'][$id] = $row;
	}

    // Try to cache results
    if ($use_cache && (is_dir($cache_dir) || ( @mkdir($cache_dir, 0777) && is_writable($cache_dir) ) ) )
    {
        // Is there an index.htm file?
        if ( ! file_exists($cache_dir.'index.htm'))
        {
            @file_put_contents($cache_dir.'index.htm', '');
        }

        // Write data
        @file_put_contents($cache_file, '<?php if (!defined(\'IN_SCRIPT\')) {die();} $hesk_settings[\'custom_fields\']=' . var_export($hesk_settings['custom_fields'], true) . ';' );
    }

	return true;
} // END hesk_load_custom_fields()


function hesk_is_custom_field_in_category($custom_id, $category_id)
{
	global $hesk_settings;

	return (
			empty($hesk_settings['custom_fields'][$custom_id]['category']) ||
			in_array($category_id, $hesk_settings['custom_fields'][$custom_id]['category'])
			) ? true : false;
} // END hesk_is_custom_field_in_category()


function hesk_custom_field_type($type)
{
	global $hesklang;

	switch ($type)
	{
		case 'text':
			return $hesklang['stf'];
		case 'textarea':
			return $hesklang['stb'];
		case 'radio':
			return $hesklang['srb'];
		case 'select':
			return $hesklang['ssb'];
		case 'checkbox':
			return $hesklang['scb'];
		case 'email':
			return $hesklang['email'];
		case 'date':
			return $hesklang['date'];
		case 'hidden':
			return $hesklang['sch'];
		default:
			return false;
	}

} // END hesk_custom_field_type()


function hesk_custom_date_display_format($timestamp, $format = 'F j, Y')
{
	global $hesklang;

	if ($timestamp == '')
	{
		return '';
	}

    if ( ! is_int($timestamp))
    {
        $timestamp = $timestamp * 1;
    }

	if ($hesklang['LANGUAGE']=='English')
    {
		return gmdate($format, $timestamp);
    }

    // Attempt to translate date for non-English users

	$translate_months = array(
		'January' => $hesklang['m1'],
		'February' => $hesklang['m2'],
		'March' => $hesklang['m3'],
		'April' => $hesklang['m4'],
		'May' => $hesklang['m5'],
		'June' => $hesklang['m6'],
		'July' => $hesklang['m7'],
		'August' => $hesklang['m8'],
		'September' => $hesklang['m9'],
		'October' => $hesklang['m10'],
		'November' => $hesklang['m11'],
		'December' => $hesklang['m12']
    );

	$translate_months_short = array(
		'Jan' => $hesklang['ms01'],
		'Feb' => $hesklang['ms02'],
		'Mar' => $hesklang['ms03'],
		'Apr' => $hesklang['ms04'],
		'May' => $hesklang['ms05'],
		'Jun' => $hesklang['ms06'],
		'Jul' => $hesklang['ms07'],
		'Aug' => $hesklang['ms08'],
		'Sep' => $hesklang['ms09'],
		'Oct' => $hesklang['ms10'],
		'Nov' => $hesklang['ms11'],
		'Dec' => $hesklang['ms12']
    );

	$translate_days = array(
		'Monday' => $hesklang['d1'],
		'Tuesday' => $hesklang['d2'],
		'Wednesday' => $hesklang['d3'],
		'Thursday' => $hesklang['d4'],
		'Friday' => $hesklang['d5'],
		'Saturday' => $hesklang['d6'],
		'Sunday' => $hesklang['d0']
    );

	$translate_days_short = array(
		'Mon' => $hesklang['mo'],
		'Tuw' => $hesklang['tu'],
		'Wes' => $hesklang['we'],
		'Thu' => $hesklang['th'],
		'Fri' => $hesklang['fr'],
		'Sat' => $hesklang['sa'],
		'Sun' => $hesklang['su']
    );

    $date_translate = array();

	if (strpos($format, 'F') !== false)
    {
    	$date_translate = array_merge($date_translate, $translate_months);
    }

	if (strpos($format, 'M') !== false)
    {
    	$date_translate = array_merge($date_translate, $translate_months_short);
    }

	if (strpos($format, 'l') !== false)
    {
    	$date_translate = array_merge($date_translate, $translate_days);
    }

	if (strpos($format, 'D') !== false)
    {
    	$date_translate = array_merge($date_translate, $translate_days_short);
    }

	if (count($date_translate))
    {
		return str_replace( array_keys($date_translate), array_values($date_translate), gmdate($format, $timestamp));
    }

    return gmdate($format, $timestamp);

} // END hesk_custom_date_display_format()


function hesk_remove_punctuation($in)
{
    return rtrim($in, ':?!.');
} // END hesk_remove_punctuation()
customer_accounts.inc.php
wget 'https://lists2.roe3.org/hesk/inc/customer_accounts.inc.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;

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

// Possible fields to be displayed in ticket list
$hesk_settings['possible_customer_ticket_list'] = array(
    'id' => $hesklang['id'],
    'trackid' => $hesklang['trackID'],
    'dt' => $hesklang['submitted'],
    'lastchange' => $hesklang['last_update'],
    'category' => $hesklang['category'],
    //'name' => $hesklang['customer'],
    //'email' => $hesklang['email'],
    'subject' => $hesklang['subject'],
    'status' => $hesklang['status'],
    'owner' => $hesklang['owner'],
    'replies' => $hesklang['replies'],
    'staffreplies' => $hesklang['replies'] . ' (' . $hesklang['staff'] .')',
    //'lastreplier' => $hesklang['last_replier'],
    'time_worked' => $hesklang['ts'],
    'due_date' => $hesklang['due_date']
);

function hesk_get_customer_account_by_name($name) {
    global $hesk_settings;

    // Verified = 1 (approved, verified) | 2 (pending approval)
    // Verified = 0, but with verification_token (pending verification)
    $sql = "SELECT * FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "customers`
            WHERE `name` = '" . hesk_dbEscape($name) . "'
                AND TRIM(`email`) = ''";

    $rs = hesk_dbQuery($sql);

    if ($row = hesk_dbFetchAssoc($rs)) {
        return $row;
    }

    return null;
}

function hesk_get_customer_account_by_name_and_email($name, $email) {
    global $hesk_settings;

    $sql = "SELECT * FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "customers`
            WHERE `email` = '" . hesk_dbEscape(trim($email)) . "'
                AND `name` = '". hesk_dbEscape(trim($name)) ."'";

    $rs = hesk_dbQuery($sql);

    if ($row = hesk_dbFetchAssoc($rs)) {
        return $row;
    }

    return null;
}

function hesk_get_customer_account_by_email($email, $registration = false, $verified_only = false) {
    global $hesk_settings;

    // Verified = 1 (approved, verified) | 2 (pending approval)
    // Verified = 0, but with verification_token (pending verification)
    $sql = "SELECT * FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "customers`
            WHERE `email` = '" . hesk_dbEscape($email) . "'";

    // If we're registering, we should only check if an existing account is verified or pending verification
    if ($registration) {
        $sql .= " AND (`verified` IN (1, 2) OR (`verified` = 0 AND `verification_token` IS NOT NULL))";
    }

    // Only return verified accounts?
    if ($verified_only) {
        $sql .= " AND `verified` = '1' ";
    }

    $rs = hesk_dbQuery($sql);

    if ($row = hesk_dbFetchAssoc($rs)) {
        if (empty($row['email'])) {
            $row['email'] = '';
        }
        return $row;
    }

    return null;
}

function hesk_get_customer_account_by_id($id) {
    global $hesk_settings;

    $sql = "SELECT * FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "customers` 
            WHERE `id` = " . intval($id);

    $rs = hesk_dbQuery($sql);

    if ($row = hesk_dbFetchAssoc($rs)) {
        if (empty($row['email'])) {
            $row['email'] = '';
        }
        return $row;
    }

    return null;
}

function hesk_get_or_create_customer($name, $email, $create_if_not_found = true) {
    global $hesk_settings, $hesklang;

    $name = ($name === null || $name == '') ? '' : trim($name);
    $email = ($email === null || $email == '') ? '' : trim($email);

    // If email is empty just create a new account
    if (empty($email)) {
        if ($create_if_not_found) {
            hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` (`name`) VALUES ('".hesk_dbEscape($name)."')");
            return hesk_dbInsertID();
        }
        return null;
    }

    //-- If we already have a customer record based on name and email, return its id
    $existing_customer_rs = hesk_dbQuery("SELECT `id` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers`
        WHERE `email` = '".hesk_dbEscape($email)."'
            AND `name` = '".hesk_dbEscape($name)."'
        LIMIT 1");
    if ($row = hesk_dbFetchAssoc($existing_customer_rs)) {
        return $row['id'];
    }

    // Is there a registered user with this email already? If so, return it as we can't create extra customers when one
    // is already registered with this email.
    $registered_email_rs = hesk_dbQuery("SELECT `id` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers`
            WHERE `email` = '".hesk_dbEscape($email)."'
                AND `verified` = 1");
    if ($row = hesk_dbFetchAssoc($registered_email_rs)) {
        return $row['id'];
    }

    //-- No match.  Create a new customer if the user wants to
    if ($create_if_not_found) {
        hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` (`name`, `email`)
        VALUES ('".hesk_dbEscape(trim($name))."', '".hesk_dbEscape(trim($email))."')");

        return hesk_dbInsertID();
    }

    return null;
}

function hesk_get_or_create_follower($email, $name = '') {
    global $hesk_settings;

    $name = ($name === null || $name == '') ? '' : trim($name);
    $email = ($email === null || $email == '') ? '' : trim($email);
    $email = hesk_validateEmail(hesk_htmlspecialchars_decode($email), '', 0);

    // If email is empty it's not a valid follower
    if (empty($email)) {
        return null;
    }

    //-- If we already have a customer record based on name and email, return its id
    $existing_customer_rs = hesk_dbQuery("SELECT `id` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers`
        WHERE `email` = '".hesk_dbEscape($email)."'
            AND `name` = '".hesk_dbEscape($name)."'
        LIMIT 1");
    if ($row = hesk_dbFetchAssoc($existing_customer_rs)) {
        return $row['id'];
    }

    // Is there a registered user with this email already? If so, return it as we can't create extra customers when one
    // is already registered with this email.
    $registered_email_rs = hesk_dbQuery("SELECT `id` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers`
            WHERE `email` = '".hesk_dbEscape($email)."'
                AND `verified` = 1");
    if ($row = hesk_dbFetchAssoc($registered_email_rs)) {
        return $row['id'];
    }

    //-- No match.  Create a new customer
    hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` (`name`, `email`)
        VALUES ('".hesk_dbEscape(trim($name))."', '".hesk_dbEscape($email)."')");

    return hesk_dbInsertID();
}

function hesk_get_customer_id_by_email($email, $verified_only = false) {
    global $hesk_settings;

    $sql = "SELECT `id` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "customers`
            WHERE `email` = '" . hesk_dbEscape($email) . "'";

    // Only return verified accounts?
    if ($verified_only) {
        $sql .= " AND `verified` = '1' ";
    }

    $rs = hesk_dbQuery($sql);

    if ($row = hesk_dbFetchAssoc($rs)) {
        return $row['id'];
    }

    return null;
}

function hesk_verify_customer_account($email, $verification_token) {
    global $hesk_settings;

    $sql = "UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."customers`
        SET `verified` = 1,
            `verification_token` = NULL
        WHERE `email` = '".hesk_dbEscape($email)."'
            AND `verification_token` = '".hesk_dbEscape($verification_token)."'";

    hesk_dbQuery($sql);

    return hesk_dbAffectedRows() === 1;
}

function hesk_merge_customer_accounts($email) {
    global $hesk_settings;

    $destination_customer_id_rs = hesk_dbQuery("SELECT `id` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers`
        WHERE `email` = '".hesk_dbEscape($email)."'
            AND `verified` = 1
            AND `verification_token` IS NULL
        LIMIT 1");
    $row = hesk_dbFetchAssoc($destination_customer_id_rs);
    $destination_customer_id = $row['id'];

    // Migrate ticket mappings to the new customer ID
    hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer`
        SET `customer_id` = ".intval($destination_customer_id)."
        WHERE `customer_id` IN (
            SELECT `id`
            FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers`
            WHERE `email` = '".hesk_dbEscape($email)."'
                AND `verified` = 0
        )");

    // Migrate ticket replies to the new customer ID
    hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."replies`
        SET `customer_id` = ".intval($destination_customer_id)."
        WHERE `customer_id` IN (
            SELECT `id`
            FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers`
            WHERE `email` = '".hesk_dbEscape($email)."'
                AND `verified` = 0
        )");

    // Delete old customer records
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers`
        WHERE `email` = '".hesk_dbEscape($email)."'
            AND `verified` = 0");
}

function hesk_mark_account_needing_approval($email) {
    global $hesk_settings;

    hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."customers`
        SET `verified` = 2
        WHERE `email` = '".hesk_dbEscape($email)."'");
}

// Very similar to admin/index.php's process_successful_login function, but segregated so we don't mix staff/customer logic
function hesk_process_successful_customer_login($user, $noredirect = false, $is_autologin = false, $remember_user = null) {
    global $hesk_settings, $hesklang;

    if ($remember_user === null) {
        $remember_user = hesk_POST('remember_user');
    }

    // User authenticated, let's regenerate the session ID
    hesk_session_regenerate_id();

    // Set a tag that will be used to expire sessions after username or password change
    $_SESSION['customer']['session_verify'] = hesk_activeSessionCreateTag($user['email'], $user['pass']);

    // Set data we need for the session
    unset($user['pass']);
    unset($user['mfa_secret']);
    foreach ($user as $k => $v) {
        $_SESSION['customer'][$k] = $v;
    }

    // Reset repeated emails session data
    hesk_cleanSessionVars('mfa_emails_sent');

    // On login, the customer account language is authoritative over any public-language cookie.
    $user = hesk_applyCustomerLanguage($user, false);

    /* Login successful, clean brute force attempts */
    hesk_cleanBfAttempts();

    // Give the user some time before requiring re-authentication for sensitive pages
    if (!$is_autologin) {
        $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;
    } else {
        // Auto-login must not preserve a previously elevated customer session
        if (isset($_SESSION['customer']['elevated'])) {
            unset($_SESSION['customer']['elevated']);
        }
        if (isset($_SESSION['customer']['elevator_target'])) {
            unset($_SESSION['customer']['elevator_target']);
        }
    }

    // Remember username?
    if (!$is_autologin) {
        if ($hesk_settings['customer_autologin'] && $remember_user === 'AUTOLOGIN') {
            $selector = base64_encode(random_bytes(9));
            $authenticator = random_bytes(33);
            hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."auth_tokens` (`selector`,`token`,`user_id`,`user_type`,`expires`) VALUES ('".hesk_dbEscape($selector)."','".hesk_dbEscape(hash('sha256', $authenticator))."','".intval($_SESSION['customer']['id'])."','CUSTOMER', NOW() + INTERVAL 1 YEAR)");
            hesk_setcookie('hesk_customer_username', '');
            hesk_setcookie('hesk_customer_remember', $selector.':'.base64_encode($authenticator), strtotime('+1 year'));
        } elseif ($remember_user === 'JUSTUSER') {
            hesk_setcookie('hesk_customer_username', $user['email'], strtotime('+1 year'));
            hesk_setcookie('hesk_customer_remember', '');
        } else {
            hesk_setcookie('hesk_customer_username', '');
            hesk_setcookie('hesk_customer_remember', '');
        }
    }

    /* If session expired while a HESK page is open just continue using it, don't redirect */
    if ($noredirect)
    {
        return true;
    }

    /* Redirect to the destination page */
    header('Location: ' . hesk_verifyGoto('CUSTOMER') );
    exit();
}

function hesk_applyCustomerLanguage($customer, $allow_explicit_selection = true) {
    global $hesk_settings;

    if (empty($hesk_settings['can_sel_lang']) || ! is_array($customer)) {
        return $customer;
    }

    if ($allow_explicit_selection && isset($_GET['language'])) {
        $language = hesk_input(hesk_GET('language'));

        if ($language && isset($hesk_settings['languages'][$language])) {
            if ($hesk_settings['language'] != $language) {
                hesk_setLanguage($language);
            }

            hesk_setcookie('hesk_language', $language, time()+31536000, '/');

            if (isset($customer['id']) && (!isset($customer['language']) || $customer['language'] != $language)) {
                hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` SET `language` = '".hesk_dbEscape($language)."' WHERE `id` = ".intval($customer['id']));
                $customer['language'] = $language;
            }

            if (isset($_SESSION['customer']) && is_array($_SESSION['customer'])) {
                $_SESSION['customer']['language'] = $language;
            }

            return $customer;
        }
    }

    if ( ! empty($customer['language']) && isset($hesk_settings['languages'][$customer['language']])) {
        if ($hesk_settings['language'] != $customer['language']) {
            hesk_setLanguage($customer['language']);
        }

        hesk_setcookie('hesk_language', $customer['language'], time()+31536000, '/');

        if (isset($_SESSION['customer']) && is_array($_SESSION['customer'])) {
            $_SESSION['customer']['language'] = $customer['language'];
        }
    }

    return $customer;
}

// Similar to hesk_isLoggedIn(), but for customers
function hesk_isCustomerLoggedIn($redirect = true) {
    global $hesk_settings, $hesklang;

    // If customer accounts are disabled, no one is ever logged in, and we should simply go back to the index page
    if ( ! $hesk_settings['customer_accounts']) {
        if ( ! $redirect) {
            return null;
        }
        header('Location: index.php');
        exit();
    }

    $referer = hesk_input($_SERVER['REQUEST_URI']);
    $referer = str_replace('&amp;','&',$referer);

    // Customer login URL
    $url = $hesk_settings['hesk_url'] . '/login.php?notice=1&goto='.urlencode($referer);

    if (empty($_SESSION['customer']['id']) || empty($_SESSION['customer']['session_verify'])) {
        //-- We only want to auto-login if we're going to a page that requires authentication
        if ($hesk_settings['customer_autologin'] && $redirect && hesk_customerAutoLogin(true)) {
            return true;
        }
    } else {
        // hesk_session_regenerate_id();

        // Let's make sure user still exists and is verified
        $res = hesk_dbQuery( "SELECT `id`, `email`, `pass`, `name`, `email`, `language`, `mfa_enrollment` FROM `".$hesk_settings['db_pfix']."customers` WHERE `id` = '".intval($_SESSION['customer']['id'])."' AND `verified` = 1 LIMIT 1" );

        // Exit if user not found
        if (hesk_dbNumRows($res) === 1) {
            // Fetch results from database
            $me = hesk_dbFetchAssoc($res);

            // Was the email banned?
            if (hesk_isBannedEmail($me['email'])) {
                hesk_forceLogoutCustomer($hesklang['customer_accounts_email_banned']);
            }

            // Verify this session is still valid
            if (hesk_activeSessionValidate($me['email'], $me['pass'], $_SESSION['customer']['session_verify'])) {
                return hesk_applyCustomerLanguage($me);
            }
        }
    }

    // If we get here, then we're not logged in.
    if ($redirect) {
        // Only destroy the session if redirecting...otherwise things get messed up
        hesk_session_stop();
        header('Location: '.$url);
        exit();
    } else {
        return null;
    }
}

function hesk_handle_customer_password_reset_request($email) {
    global $hesk_settings, $hesklang;

    // Get user data from the database
    $res = hesk_dbQuery("SELECT `id`, `name`, `pass` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` WHERE `verified`=1 AND `email` = '".hesk_dbEscape($email)."' LIMIT 1");
    if (hesk_dbNumRows($res) != 1)
    {
        hesk_process_messages($hesklang['novace'],'login.php?submittedForgot=1');
    }
    else
    {
        $row = hesk_dbFetchAssoc($res);
        $reset_token = hesk_create_password_reset_token();
        $reset_hash = hesk_hash_password_reset_token($reset_token);
        $customer_id = intval($row['id']);

        // Expire old verification hashes for this customer account
        hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."reset_password` WHERE `user_type` = 'CUSTOMER' AND `user` = {$customer_id}");

        // Insert the verification hash into the database
        hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."reset_password` (`user`, `hash`, `ip`, `user_type`) VALUES ({$customer_id}, '".hesk_dbEscape($reset_hash)."', '".hesk_dbEscape(hesk_getClientIP())."', 'CUSTOMER') ");

        // Prepare and send email
        require_once(HESK_PATH . 'inc/email_functions.inc.php');

        // Get the email message
        list($msg, $html_msg) = hesk_getEmailMessage('customer_reset_password',array(),1,0,1);

        // Replace message special tags
        list($msg, $html_msg) = hesk_replace_email_tag('%%NAME%%', hesk_msgToPlain($row['name'],1,0), $msg, $html_msg);
        list($msg, $html_msg) = hesk_replace_email_tag('%%SITE_URL%%', $hesk_settings['site_url'], $msg, $html_msg);
        list($msg, $html_msg) = hesk_replace_email_tag('%%SITE_TITLE%%', $hesk_settings['site_title'], $msg, $html_msg);
        list($msg, $html_msg) = hesk_replace_email_tag('%%PASSWORD_RESET%%',
            $hesk_settings['hesk_url'].'/reset_password.php?hash='.$reset_token,
            $msg,
            $html_msg);

        // Check two additional tags (avoid a bug in 3.3.0)
        list($msg, $html_msg) = hesk_replace_email_tag('%25%25PASSWORD_RESET%25%25',
            $hesk_settings['hesk_url'].'/reset_password.php?hash='.$reset_token,
            $msg,
            $html_msg);
        list($msg, $html_msg) = hesk_replace_email_tag('%%TRACK_URL%%',
            $hesk_settings['hesk_url'].'/reset_password.php?hash='.$reset_token,
            $msg,
            $html_msg);

        // Send email
        hesk_mail($email, [], $hesklang['customer_reset_password'], $msg, $html_msg);
    }
}

function hesk_verify_customer_password_reset_hash($hash, $purge_user_hashes = false) {
    global $hesk_settings, $hesklang;

    // Get the reset token and hash it for database lookup
    $reset_token = hesk_clean_password_reset_token($hash);
    $reset_hash = hesk_hash_password_reset_token($reset_token);

    // Expire verification hashes older than 1 hour
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."reset_password` WHERE `dt` < (NOW() - INTERVAL 1 HOUR)");

    // Verify the hash exists
    $res = hesk_dbQuery("SELECT `user`, `ip` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."reset_password` WHERE `hash` = '".hesk_dbEscape($reset_hash)."' AND `user_type` = 'CUSTOMER' LIMIT 1");
    if (hesk_dbNumRows($res) !== 1) {
        // Not a valid hash
        hesk_limitBfAttempts();
        return [
            'success' => false,
            'content' => $hesklang['ehash']
        ];
    }

    // Get info from database
    $row = hesk_dbFetchAssoc($res);

    // Only allow resetting password from the same IP address that submitted password reset request
    if ($row['ip'] != hesk_getClientIP()) {
        hesk_limitBfAttempts();
        return [
            'success' => false,
            'content' => $hesklang['ehaip']
        ];
    }

    // Expire all verification hashes for this user if requested
    if ($purge_user_hashes) {
        hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."reset_password` 
                    WHERE `user_type` = 'CUSTOMER' 
                    AND `user`=".intval($row['user']));
    }

    // Clean brute force attempts
    hesk_cleanBfAttempts();
    return [
        'success' => true,
        'content' => $row['user']
    ];
}

//region MFA
function hesk_remove_mfa_for_customer($customer_id) {
    global $hesk_settings;

    hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."customers`
        SET `mfa_enrollment` = 0,
            `mfa_secret` = NULL
        WHERE `id` = ".intval($customer_id));
}
//endregion

function hesk_get_customers_for_ticket($ticket_id) {
    global $hesk_settings;

    $customers_res = hesk_dbQuery("SELECT `customers`.`id`, `customers`.`name`, `customers`.`email`, `customers`.`language`, `ticket_to_customer`.`customer_type`
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` `customers`
        INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` `ticket_to_customer`
            ON `customers`.`id` = `ticket_to_customer`.`customer_id`
        WHERE `ticket_to_customer`.`ticket_id` = ".intval($ticket_id));

    $customers = [];
    while ($row = hesk_dbFetchAssoc($customers_res)) {
        if (empty($row['email'])) {
            $row['email'] = '';
        }
        $customers[] = $row;
    }

    if (defined('HESK_DEMO')) {
        array_walk($customers, function(&$k) {
            $k['email'] = 'hidden@demo.com';
        });
    }

    return $customers;
}

function hesk_purge_expired_email_change_requests() {
    global $hesk_settings;

    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."pending_customer_email_changes`
        WHERE `expires_at` < NOW()");
}

function hesk_purge_email_change_requests($user_id) {
    global $hesk_settings;

    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."pending_customer_email_changes`
        WHERE `customer_id` = ".intval($user_id));
}

function hesk_get_pending_email_change_for_user($user_id) {
    global $hesk_settings;

    $res = hesk_dbQuery("SELECT `new_email`,
        CASE
            WHEN `expires_at` > (NOW() + INTERVAL ".intval(60 - $hesk_settings['customer_accounts_verify_email_cooldown'])." MINUTE) THEN 1
            ELSE 0
        END AS `email_sent_too_recently`
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."pending_customer_email_changes`
        WHERE `customer_id` = ".intval($user_id)." AND `expires_at` > NOW()");

    if ($row = hesk_dbFetchAssoc($res)) {
        return $row;
    }

    return null;
}

function hesk_get_pending_email_change_for_email($email, $user_id) {
    global $hesk_settings;

    $res = hesk_dbQuery("SELECT 1 FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."pending_customer_email_changes`
        WHERE `new_email` = '".hesk_dbEscape($email)."' AND `customer_id` <> ".intval($user_id));

    return hesk_dbNumRows($res);
}

function hesk_insert_email_change_request($email, $user_id) {
    global $hesk_settings;

    $verification_token = bin2hex(random_bytes(16));
    hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."pending_customer_email_changes` (`customer_id`,`new_email`,`verification_token`,`expires_at`)
        VALUES (".intval($user_id).", '".hesk_dbEscape($email)."', '".hesk_dbEscape($verification_token)."', NOW() + INTERVAL 60 MINUTE)");

    return $verification_token;
}

function hesk_verify_email_change_request($email, $verification_token) {
    global $hesk_settings;

    $change_request_rs = hesk_dbQuery("SELECT `id`, `customer_id` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."pending_customer_email_changes`
        WHERE `new_email` = '".hesk_dbEscape($email)."'
            AND `verification_token` = '".hesk_dbEscape($verification_token)."'
            AND `expires_at` >= NOW()");

    $row = hesk_dbFetchAssoc($change_request_rs);
    if (!$row) {
        return false;
    }

    $request_id = intval($row['id']);
    $customer_id = intval($row['customer_id']);

    // Re-check the target email when the verification link is used. The email may have become banned,
    // claimed by another customer account, or reserved by another pending email-change request after this
    // request was created.
    if (hesk_isBannedEmail($email)) {
        hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."pending_customer_email_changes` WHERE `id` = {$request_id}");
        return false;
    }

    $email = hesk_dbEscape($email);
    $request_id = intval($row['id']);
    $customer_id = intval($row['customer_id']);

    $sql = "UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` AS `target_customer`
        LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` AS `existing_customer`
            ON `existing_customer`.`email` = '{$email}'
            AND `existing_customer`.`id` <> {$customer_id}
            AND (`existing_customer`.`verified` IN (1, 2) OR (`existing_customer`.`verified` = 0 AND `existing_customer`.`verification_token` IS NOT NULL))
        LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."pending_customer_email_changes` AS `pending_email_change`
            ON `pending_email_change`.`new_email` = '{$email}'
            AND `pending_email_change`.`id` <> {$request_id}
            AND `pending_email_change`.`expires_at` >= NOW()
        SET `target_customer`.`email` = '{$email}',
            `target_customer`.`verification_token` = NULL
        WHERE `target_customer`.`id` = {$customer_id}
            AND `target_customer`.`verified` = 1
            AND `existing_customer`.`id` IS NULL
            AND `pending_email_change`.`id` IS NULL";
    hesk_dbQuery($sql);

    if (hesk_dbAffectedRows() === 1) {
        hesk_purge_email_change_requests($customer_id);
        hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."auth_tokens` WHERE `user_id` = {$customer_id} AND `user_type` = 'CUSTOMER'");

        return true;
    }

    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."pending_customer_email_changes` WHERE `id` = {$request_id}");

    return false;
}

function hesk_customerAutoLogin($noredirect = false)
{
    global $hesk_settings, $hesklang, $hesk_db_link;
    $cookie_name = 'hesk_customer_remember';

    if (!$hesk_settings['customer_autologin']) {
        return false;
    }

    if (empty($remember = hesk_COOKIE($cookie_name)) || substr_count($remember, ':') !== 1) {
        return false;
    }

    // Login cookies exist, now lets limit brute force attempts
    hesk_limitBfAttempts();

    // Admin login URL
    $url = $hesk_settings['hesk_url'] . '/login.php?notice=1';

    // Get and verify authentication tokens
    list($selector, $authenticator) = explode(':', $remember);
    $authenticator = base64_decode($authenticator);
    if (strlen($authenticator) > 256) {
        hesk_setcookie($cookie_name, '');
        header('Location: '.$url);
        exit();
    }

    $result = hesk_dbQuery('SELECT * FROM `'.$hesk_settings['db_pfix']."auth_tokens` 
        WHERE `selector` = '".hesk_dbEscape($selector)."' 
            AND `expires` > NOW() 
            AND `user_type` = 'CUSTOMER' 
        LIMIT 1");
    if (hesk_dbNumRows($result) != 1) {
        hesk_setcookie($cookie_name, '');
        header('Location: '.$url);
        exit();
    }

    $auth = hesk_dbFetchAssoc($result);

    if ( ! hash_equals($auth['token'], hash('sha256', $authenticator))) {
        hesk_setcookie($cookie_name, '');
        header('Location: '.$url);
        exit();
    }

    // Token OK, let's regenerate session ID and get user data
    hesk_session_regenerate_id();

    $result = hesk_dbQuery('SELECT * FROM `'.$hesk_settings['db_pfix']."customers` WHERE `id` = ".intval($auth['user_id'])." AND `verified` = 1 LIMIT 1");
    if (hesk_dbNumRows($result) != 1) {
        hesk_setcookie($cookie_name, '');
        header('Location: '.$url);
        exit();
    }

    $row = hesk_dbFetchAssoc($result);

    if (hesk_isBannedEmail($row['email'])) {
        hesk_setcookie($cookie_name, '');
        header('Location: '.$url);
        exit();
    }

    $user = $row['email'];
    define('HESK_USER_CUSTOMER', $user);

    // Auto-login is a login transition, so the customer account language is authoritative
    // over any public-language query parameter or cookie.
    $row = hesk_applyCustomerLanguage($row, false);

    // Each token should only be used once, so update the old one with a new one
    $selector = base64_encode(random_bytes(9));
    $authenticator = random_bytes(33);
    hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."auth_tokens` SET `selector`='".hesk_dbEscape($selector)."', `token` = '".hesk_dbEscape(hash('sha256', $authenticator))."', `created` = NOW() WHERE `id` = ".intval($auth['id']));
    hesk_setcookie($cookie_name, $selector.':'.base64_encode($authenticator), strtotime('+1 year'));

    // Set a tag that will be used to expire sessions after username or password change
    $_SESSION['customer']['session_verify'] = hesk_activeSessionCreateTag($user, $row['pass']);

    /* Login successful, clean brute force attempts */
    hesk_cleanBfAttempts();

    return hesk_process_successful_customer_login($row, $noredirect, true);
} // END hesk_customerAutoLogin()
customer_modal.inc.php
wget 'https://lists2.roe3.org/hesk/inc/customer_modal.inc.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
 *
 * Common include for the "Create Customer / Follower" modal and its event handlers.
 * Used by new_ticket.php and edit_post.php.
 *
 * Requires the following JS variables to already be defined on the page before
 * the modal save button is clicked:
 *   - createCustomerSelectize  (the customer selectize instance)
 *   - createFollowerSelectize  (the follower selectize instance, when multi_eml is on)
*/
if (!defined('IN_SCRIPT')) { die('Hacking attempt!'); }
?>
<script>
    var createUserEventHandler = function(userType = 'customer', event, extraData) {
        /*
        Generalized handling of creating new customers or followers.
        1) By default clear the input fields before showing the modal,
        but allow passing specific values to event and pre-fill the input fields (i.e. from Add Customer/Follower click from dropdown)
        */
        let nameValue = '';
        let emailValue = '';
        if (extraData) {
            if (typeof extraData.nameValue !== 'undefined' && typeof extraData.nameValue === 'string') {
                nameValue = extraData.nameValue;
            }
            if (typeof extraData.emailValue !== 'undefined' && typeof extraData.emailValue === 'string') {
                emailValue = extraData.emailValue;
            }
        }
        $('[data-modal-id="create-customer"] input[name="name"]').val(nameValue);
        $('[data-modal-id="create-customer"] input[name="email"]').val(emailValue);

        // 2.) Update any titles and other related meta data
        let createCustomerTitle = '<?php echo hesk_makeJsString($hesklang['new_customer']); ?>';
        let customerType = 'CUSTOMER';
        if (userType === 'customer') {
            $('#new-customer-prompt').css('display', 'none');
        } else if (userType === 'follower') {
            createCustomerTitle = '<?php echo hesk_makeJsString($hesklang['new_follower']); ?>';
            customerType = 'FOLLOWER';
        }
        $('#create-customer-title').text(createCustomerTitle);
        $('[data-modal-id="create-customer"] input[name="customer_type"]').val(customerType);

        if (extraData) {
            // If extra data was passed also run validation checks - but only if anything is actually entered,
            // otherwise it looks weird if errors shows up when user didn't enter anything yet.
            if (nameValue !== '') {
                $('#create_name').keyup();
            }
            if (emailValue!== '') {
                $('#email').keyup();
            }
        }
        // We also want to clear any unnecessary errors on empty fields, as it feels weird to leave them from previous modal opens.
        updateValidation(nameValue === '', emailValue === '');
    };

    $('#new-customer-link').click(function(event, extraData = null) {
        createUserEventHandler('customer', event, extraData);
    });

    $('#new-follower-link').click(function(event, extraData = null) {
        createUserEventHandler('follower', event, extraData);
    });
</script>

<div class="modal" data-modal-id="create-customer">
    <div class="modal__body" style="white-space: normal; <?php if ($hesk_settings['limit_width']) echo 'max-width:'.$hesk_settings['limit_width'].'px'; ?>">
        <i class="modal__close" data-action="cancel">
            <svg class="icon icon-close">
                <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-close"></use>
            </svg>
        </i>
        <h3 id="create-customer-title"><?php echo $hesklang['new_customer']; ?></h3>
        <div class="modal__description">
            <div id="new-customer-prompt" style="display: <?php echo (isset($show_create_modal) && $show_create_modal) ? 'block' : 'none'; ?>">
                <?php echo $hesklang['new_customer_prompt']; ?>
            </div>
            <div class="form">
                <div class="form-group">
                    <label for="create_name">
                        <?php echo $hesklang['name']; ?>: <span class="important">*</span>
                    </label>
                    <input type="text" id="create_name" name="name" class="form-control" maxlength="50">
                    <div class="form-control__error"></div>
                </div>
                <div class="form-group">
                    <label for="email">
                        <?php echo $hesklang['email'] . ':' . ($hesk_settings['require_email'] ? ' <span class="important">*</span>' : '') ; ?>
                    </label>
                    <input type="email"
                           class="form-control"
                           name="email" id="email" maxlength="1000">
                    <div class="form-control__error"></div>
                </div>
                <div id="email_suggestions" class="email-suggestion"></div>
            </div>
        </div>
        <div class="modal__buttons">
            <input type="hidden" name="customer_type" value="CUSTOMER">
            <button class="btn btn-border" ripple="ripple" data-action="cancel"><?php echo $hesklang['cancel']; ?></button>
            <a data-confirm-button href="#" class="btn btn-full text-white disabled" ripple="ripple" style="width: 152px; height: 40px;"><?php echo $hesklang['save']; ?></a>
        </div>
        <script>
            var $name = $('#create_name');
            var $email = $('#email');
            var $saveButton = $("[data-modal-id='create-customer']").find('a[data-confirm-button]');
            var nameValid = false;
            var emailValid = false;
            var emailFailureReason = '';

            $name.keyup(function() {
                updateNameValidation();

                let emailIsEmpty = ($email.val().trim() === '');
                <?php if (!$hesk_settings['require_email']): ?>
                emailValid = true;
                <?php endif; ?>

                /* If other/email field is empty, ignore always showing its error,
                UNLESS it was already shown (user interacted with that field since opening the modal)
                */
                let clearEmailError = (!$email.parent().hasClass('error') && emailIsEmpty)
                updateValidation(false, clearEmailError);
            });
            let fireCreateCustomerCallbackAfterEmailCheck = false;
            var debouncedEmailCheck = hesk_debounce(function() {
                /* If other/name field is empty, ignore always showing its error,
                UNLESS it was already shown (user interacted with that field since opening the modal)
                */
                let nameIsEmpty = ($name.val().trim() === '');
                let emailIsEmpty = ($email.val().trim() === '');
                let clearNameError = (!$name.parent().hasClass('error') && nameIsEmpty);

                // If email is not required, and the email field is empty, we can just skip ajax validation for email.
                // Otherwise, we always want to run ajax validation to maintain consistent UX.
                <?php if (!$hesk_settings['require_email']): ?>
                if (emailIsEmpty) {
                    emailValid = true;
                    updateValidation(clearNameError, false);

                    if (fireCreateCustomerCallbackAfterEmailCheck) {
                        // In this case, as no customer check happens, we have to call createCustomer callback directly
                        createCustomerOnValidationCallback(true);
                        fireCreateCustomerCallbackAfterEmailCheck = false;
                    }
                    return;
                }
                <?php endif; ?>

                //-- Disable save button initially until email check is complete
                emailValid = false;
                updateValidation(clearNameError, false);
                if (emailIsEmpty) {
                    emailFailureReason = '<?php echo hesk_makeJsString($hesklang['this_field_is_required']); ?>';
                    updateValidation(clearNameError, false);

                    if (fireCreateCustomerCallbackAfterEmailCheck) {
                        // Even in this case we call the callback, just with success : false.
                        // Reason is to ensure updateValidation logic is 100% the same as it was before the async request handling.
                        createCustomerOnValidationCallback(false);
                        fireCreateCustomerCallbackAfterEmailCheck = false;
                    }
                    return;
                }

                $.ajax({
                    url: 'ajax/check_customer.php',
                    type: 'GET',
                    data: {
                        email: $email.val(),
                        name: $name.val()
                    },
                    dataType: 'json',
                    success: function(data) {
                        if (!data.emailValid) {
                            emailValid = false;
                            emailFailureReason = '<?php echo hesk_makeJsString($hesklang['enter_valid_email']); ?>';
                        } else if (data.customerAvailable === 'NOT_AVAILABLE_REGISTERED') {
                            emailValid = false;
                            emailFailureReason = '<?php echo hesk_makeJsString($hesklang['customer_email_exists_already_registered']); ?>';
                        } else if (data.customerAvailable === 'NOT_AVAILABLE_IDENTICAL') {
                            emailValid = false;
                            emailFailureReason = '<?php echo hesk_makeJsString($hesklang['customer_name_email_exists']); ?>';
                        } else {
                            emailValid = true;
                            emailFailureReason = '';

                            <?php if ($hesk_settings['detect_typos']): ?>
                            hesk_suggestEmail('email', 'email_suggestions', 1, 1);
                            <?php endif; ?>
                        }
                        updateValidation(clearNameError, false);

                        if (fireCreateCustomerCallbackAfterEmailCheck) {
                            createCustomerOnValidationCallback(true);
                            fireCreateCustomerCallbackAfterEmailCheck = false;
                        }
                    },
                    error: function(err) {
                        console.error(err);
                        emailValid = false;
                        emailFailureReason = '<?php echo hesk_makeJsString($hesklang['an_error_occurred_validating_email']); ?>';
                        updateValidation(clearNameError, false);

                        if (fireCreateCustomerCallbackAfterEmailCheck) {
                            // Even in this case we call the callback, just with success : false.
                            // Reason is to ensure updateValidation logic is 100% the same as it was before the async request handling.
                            createCustomerOnValidationCallback(false);
                            fireCreateCustomerCallbackAfterEmailCheck = false;
                        }
                    }
                });
            }, 300);
            $name.keyup(debouncedEmailCheck);
            $email.keyup(debouncedEmailCheck);
            $saveButton.click(function() {
                // As the debounceEmail check is async, we can only call the createCustomer AFTER the email check has completed.
                // While we could do it with async function it would require a bigger rework,
                // so this simple flag checking & callback from debouncedEmailCheck does the trick on ensuring it works on all browsers.
                fireCreateCustomerCallbackAfterEmailCheck = true;
                debouncedEmailCheck();
            });

            function createCustomerOnValidationCallback(validationSuccessful = false) {
                if (!nameValid || !emailValid || !validationSuccessful) {
                    //-- Fix validation state messages
                    updateValidation();
                    return;
                }

                $.ajax({
                    url: 'ajax/create_customer.php',
                    type: 'POST',
                    data: {
                        name: $name.val(),
                        email: $email.val(),
                        token: '<?php echo hesk_token_echo(0); ?>'
                    },
                    dataType: 'json',
                    success: function(data) {
                        <?php if ($hesk_settings['multi_eml']): ?>
                        var selectize = $('input[name="customer_type"]').val() === 'CUSTOMER' ?
                            createCustomerSelectize :
                            createFollowerSelectize;

                        createCustomerSelectize[0].selectize.addOption(data);
                        createFollowerSelectize[0].selectize.addOption(data);
                        <?php else: ?>
                        var selectize = createCustomerSelectize;
                        createCustomerSelectize[0].selectize.addOption(data);
                        <?php endif; ?>

                        if (typeof selectize[0].selectize.getValue() === 'string') {
                            selectize[0].selectize.setValue(data.id);
                        } else {
                            var currentCustomers = selectize[0].selectize.getValue();
                            currentCustomers.push(data.id);
                            selectize[0].selectize.setValue(currentCustomers);
                        }
                        $name.val('');
                        $email.val('');
                        $('[data-modal-id="create-customer"]').find('[data-action="cancel"]').click();
                    },
                    error: function(err) {
                        emailValid = false;
                        emailFailureReason = JSON.parse(err.responseText).message;
                        updateValidation();
                    }
                });
            }

            function updateNameValidation() {
                let nameIsEmpty = ($name.val().trim() === '');
                nameValid = !nameIsEmpty;
                if (nameIsEmpty) {
                    nameValid = false;
                } else {
                    nameValid = true;
                }
            }

            function updateValidation(forceClearNameError = false, forceClearEmailError = false) {
                // Need to re-fire this always, as otherwise if closing popup, old nameValid=true value might persist
                updateNameValidation();

                var anyFailure = false;
                /*
                There are situations where we might delibarately clear errors for clearer user experience, even if inputs are empty on modal open,
                BUT at same time keeping other validation/disabled buttons as they are.
                 */
                let clearNameError = forceClearNameError || nameValid;
                if (!nameValid) {
                    anyFailure = true;
                    if (!forceClearNameError) {
                        $name.parent().addClass('error');
                        $name.parent().find('.form-control__error').text('<?php echo hesk_makeJsString($hesklang['this_field_is_required']); ?>');
                    }
                }
                if (clearNameError) {
                    $name.parent().removeClass('error');
                    $name.parent().find('.form-control__error').text('');
                }

                let clearEmailError = forceClearEmailError || emailValid;
                if (!emailValid) {
                    anyFailure = true;

                    if (!forceClearEmailError && emailFailureReason !== '') {
                        $email.parent().addClass('error');
                        $email.parent().find('.form-control__error').text(emailFailureReason);
                    }
                }
                if (clearEmailError) {
                    $email.parent().removeClass('error');
                    $email.parent().find('.form-control__error').text('');
                }

                if (anyFailure) {
                    $saveButton.addClass('disabled');
                } else {
                    $saveButton.removeClass('disabled');
                }
            }
        </script>
    </div>
</div>
database.inc.php
wget 'https://lists2.roe3.org/hesk/inc/database.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');} 


function hesk_dbCollate()
{
    global $hesklang;

    // MySQL vesions prior to 5.6 don't support some collations
    if ( in_array($hesklang['_COLLATE'], array('utf8_croatian_ci', 'utf8_german2_ci', 'utf8_vietnamese_ci')) )
    {
        if ( version_compare( hesk_dbResult( hesk_dbQuery('SELECT VERSION() AS version') ), '5.6', '<') )
        {
            $hesklang['_COLLATE'] = 'utf8_general_ci';
        }
    }

    return hesk_dbEscape($hesklang['_COLLATE']);

} // END hesk_dbCollate()


function hesk_dbSetNames()
{
	global $hesk_settings, $hesk_db_link;

    mysql_set_charset('utf8', $hesk_db_link);

} // END hesk_dbSetNames()


function hesk_dbFormatEmail($email, $field = 'email')
{
	$email = hesk_dbLike($email);

    return " `".hesk_dbEscape($field)."` LIKE '".hesk_dbEscape($email)."' ";

} // END hesk_dbFormatEmail()


function hesk_dbTime()
{
	$res = hesk_dbQuery("SELECT NOW()");
	return strtotime(hesk_dbResult($res,0,0));
} // END hesk_dbTime()


function hesk_dbSetTimezone()
{
    global $hesk_settings;

    hesk_dbQuery('SET time_zone = "'.hesk_timeToHHMM(date('Z')).'"');

    return true;
} // END hesk_dbSetTimezone()


function hesk_dbEscape($in)
{
	global $hesk_db_link;

    $in = mysql_real_escape_string(stripslashes($in), $hesk_db_link);
    $in = str_replace('`','&#96;',$in);

    return $in;
} // END hesk_dbEscape()


function hesk_dbLike($in)
{
	return str_replace( array('\\', '_', '%'), array('\\\\', '\\\\_', '\\\\%'), $in); // '
} // END hesk_dbLike()


function hesk_dbConnect()
{
	global $hesk_settings;
	global $hesk_db_link;
    global $hesklang;

    // Do we have an existing active link?
    if ($hesk_db_link)
    {
        return $hesk_db_link;
    }

    // Is mysql supported?
    if ( ! function_exists('mysql_connect') )
    {
    	die($hesklang['emp']);
    }

    // Connect to the database
    $hesk_db_link = @mysql_connect($hesk_settings['db_host'], $hesk_settings['db_user'], $hesk_settings['db_pass']);

	// Errors?
    if ( ! $hesk_db_link)
    {
    	if ($hesk_settings['debug_mode'])
        {
			hesk_error("$hesklang[cant_connect_db]</p><p>$hesklang[mysql_said]:<br />".mysql_error()."</p>");
        }
        else
        {
			hesk_error("$hesklang[cant_connect_db]</p><p>$hesklang[contact_webmsater] <a href=\"mailto:$hesk_settings[webmaster_mail]\">$hesk_settings[webmaster_mail]</a></p>");
        }
    }

    if ( ! @mysql_select_db($hesk_settings['db_name'], $hesk_db_link))
    {
    	if ($hesk_settings['debug_mode'])
        {
			hesk_error("$hesklang[cant_connect_db]</p><p>$hesklang[mysql_said]:<br />".mysql_error()."</p>");
        }
        else
        {
			hesk_error("$hesklang[cant_connect_db]</p><p>$hesklang[contact_webmsater] <a href=\"mailto:$hesk_settings[webmaster_mail]\">$hesk_settings[webmaster_mail]</a></p>");
        }
    }

    // Check MySQL/PHP version and set encoding to utf8
    hesk_dbSetNames();

    // Set the correct timezone
    hesk_dbSetTimezone();

    return $hesk_db_link;

} // END hesk_dbConnect()


function hesk_dbClose()
{
	global $hesk_db_link;

    return @mysql_close($hesk_db_link);

} // END hesk_dbClose()


function hesk_dbQuery($query)
{
    global $hesk_last_query;
    global $hesk_db_link;
    global $hesklang, $hesk_settings;

    if ( ! $hesk_db_link && ! hesk_dbConnect())
    {
        return false;
    }

    $hesk_last_query = $query;

    #echo "<p>EXPLAIN $query</p>\n";

    if ($res = @mysql_query($query, $hesk_db_link))
    {
    	return $res;
    } else {
        hesk_dbHandleFailure($query);
    }

} // END hesk_dbQuery()

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

    $valid_response_types = array('json', 'throw');

    if (!isset($hesk_settings['db_failure_response']) || !in_array($hesk_settings['db_failure_response'], $valid_response_types)) {
        if ($hesk_settings['debug_mode']) {
            hesk_error("$hesklang[cant_sql]: $query</p><p>$hesklang[mysql_said]:<br />".mysql_error()."</p>");
        } else {
            hesk_error("$hesklang[cant_sql]</p><p>$hesklang[contact_webmsater] <a href=\"mailto:$hesk_settings[webmaster_mail]\">$hesk_settings[webmaster_mail]</a></p>");
        }
    } elseif ($hesk_settings['db_failure_response'] === 'json') {
        header('Content-Type: application/json');
        http_response_code(500);
        if ($hesk_settings['debug_mode']) {
            print json_encode(array(
                'status' => 'failure',
                'title' => $hesklang['cant_sql'],
                'message' => mysql_error()
            ));
        } else {
            print json_encode(array(
                'status' => 'failure',
                'title' => $hesklang['cant_sql'],
                'message' => sprintf('%s: %s', $hesklang['contact_webmsater'], $hesk_settings['webmaster_mail'])
            ));
        }
        exit();
    } elseif ($hesk_settings['db_failure_response'] === 'throw') {
        $message = $hesk_settings['debug_mode'] ? mysql_error() : $hesklang['cant_sql'];
        throw new Exception($message);
    }
}


function hesk_dbFetchAssoc($res)
{

    return @mysql_fetch_assoc($res);

} // END hesk_FetchAssoc()


function hesk_dbFetchRow($res)
{

    return @mysql_fetch_row($res);

} // END hesk_FetchRow()


function hesk_dbResult($res, $row = 0, $column = 0)
{

    return @mysql_result($res, $row, $column);

} // END hesk_dbResult()


function hesk_dbInsertID()
{
	global $hesk_db_link;

    if ($lastid = @mysql_insert_id($hesk_db_link))
    {
        return $lastid;
    }

} // END hesk_dbInsertID()


function hesk_dbFreeResult($res)
{

    return mysql_free_result($res);

} // END hesk_dbFreeResult()


function hesk_dbNumRows($res)
{

    return @mysql_num_rows($res);

} // END hesk_dbNumRows()


function hesk_dbAffectedRows()
{
	global $hesk_db_link;

    return @mysql_affected_rows($hesk_db_link);

} // END hesk_dbAffectedRows()
database_mysqli.inc.php
wget 'https://lists2.roe3.org/hesk/inc/database_mysqli.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}


function hesk_dbCollate()
{
    global $hesklang;

    // MySQL vesions prior to 5.6 don't support some collations
    if ( in_array($hesklang['_COLLATE'], array('utf8_croatian_ci', 'utf8_german2_ci', 'utf8_vietnamese_ci')) )
    {
        if ( version_compare( hesk_dbResult( hesk_dbQuery('SELECT VERSION() AS version') ), '5.6', '<') )
        {
            $hesklang['_COLLATE'] = 'utf8_general_ci';
        }
    }

    return hesk_dbEscape($hesklang['_COLLATE']);

} // END hesk_dbCollate()


function hesk_dbSetNames()
{
	global $hesk_settings, $hesk_db_link;

    mysqli_set_charset($hesk_db_link, 'utf8');

} // END hesk_dbSetNames()


function hesk_dbFormatEmail($email, $field = 'email')
{
	$email = hesk_dbLike($email);

    return " `".hesk_dbEscape($field)."` LIKE '".hesk_dbEscape($email)."' ";
} // END hesk_dbFormatEmail()


function hesk_dbTime()
{
	$res = hesk_dbQuery("SELECT NOW()");
	return strtotime(hesk_dbResult($res,0,0));
} // END hesk_dbTime()


function hesk_dbSetTimezone()
{
    global $hesk_settings;

    hesk_dbQuery('SET time_zone = "'.hesk_timeToHHMM(date('Z')).'"');

    return true;
} // END hesk_dbSetTimezone()


function hesk_dbEscape($in)
{
	global $hesk_db_link;

    $in = mysqli_real_escape_string($hesk_db_link, stripslashes($in));
    $in = str_replace('`','&#96;',$in);

    return $in;
} // END hesk_dbEscape()


function hesk_dbLike($in)
{
	return str_replace( array('\\', '_', '%'), array('\\\\', '\\\\_', '\\\\%'), $in); // '
} // END hesk_dbLike()


function hesk_dbConnect()
{
	global $hesk_settings;
	global $hesk_db_link;
    global $hesklang;

    // Do we have an existing active link?
    if ($hesk_db_link)
    {
        return $hesk_db_link;
    }

    // Is mysqli supported?
    if ( ! function_exists('mysqli_connect') )
    {
    	die($hesklang['emp']);
    }

    // We want pre-PHP 8.1 behavior for now
    mysqli_report(MYSQLI_REPORT_OFF);

	// Do we need a special port? Check and connect to the database
	if ( strpos($hesk_settings['db_host'], ':') )
	{
		list($hesk_settings['db_host_no_port'], $hesk_settings['db_port']) = explode(':', $hesk_settings['db_host']);
		$hesk_db_link = @mysqli_connect($hesk_settings['db_host_no_port'], $hesk_settings['db_user'], $hesk_settings['db_pass'], $hesk_settings['db_name'], intval($hesk_settings['db_port']) );
	}
	else
	{
		$hesk_db_link = @mysqli_connect($hesk_settings['db_host'], $hesk_settings['db_user'], $hesk_settings['db_pass'], $hesk_settings['db_name']);
	}

	// Errors?
    if ( ! $hesk_db_link)
    {
    	if ($hesk_settings['debug_mode'])
        {
			hesk_error("$hesklang[cant_connect_db]</p><p>$hesklang[mysql_said]:<br />(".mysqli_connect_errno().") ".mysqli_connect_error()."</p>");
        }
        else
        {
			hesk_error("$hesklang[cant_connect_db]</p><p>$hesklang[contact_webmsater] <a href=\"mailto:$hesk_settings[webmaster_mail]\">$hesk_settings[webmaster_mail]</a></p>");
        }
    }

    // Check MySQL/PHP version and set encoding to utf8
    hesk_dbSetNames();

    // Set the correct timezone
    hesk_dbSetTimezone();

    return $hesk_db_link;

} // END hesk_dbConnect()


function hesk_dbClose()
{
	global $hesk_db_link;

    return @mysqli_close($hesk_db_link);

} // END hesk_dbClose()

function hesk_dbQuery($query)
{
    global $hesk_last_query;
    global $hesk_db_link;
    global $hesklang, $hesk_settings;

    if ( ! $hesk_db_link && ! hesk_dbConnect())
    {
        return false;
    }

    $hesk_last_query = $query;

    # echo "<p>EXPLAIN $query</p>\n";

    if ($res = @mysqli_query($hesk_db_link, $query))
    {
    	return $res;
    } else {
        hesk_dbHandleFailure($query);
    }
} // END hesk_dbQuery()

function hesk_dbHandleFailure($query) {
    global $hesk_settings, $hesklang, $hesk_db_link;

    $valid_response_types = array('json', 'throw');

    if (!isset($hesk_settings['db_failure_response']) || !in_array($hesk_settings['db_failure_response'], $valid_response_types)) {
        if ($hesk_settings['debug_mode']) {
            hesk_error("$hesklang[cant_sql]: $query</p><p>$hesklang[mysql_said]:<br />".mysqli_error($hesk_db_link)."</p>");
        } else {
            hesk_error("$hesklang[cant_sql]</p><p>$hesklang[contact_webmsater] <a href=\"mailto:$hesk_settings[webmaster_mail]\">$hesk_settings[webmaster_mail]</a></p>");
        }
    } elseif ($hesk_settings['db_failure_response'] === 'json') {
        header('Content-Type: application/json');
        http_response_code(500);
        if ($hesk_settings['debug_mode']) {
            print json_encode(array(
                'status' => 'failure',
                'title' => $hesklang['cant_sql'],
                'message' => mysqli_error($hesk_db_link),
                'query' => $query
            ));
        } else {
            print json_encode(array(
                'status' => 'failure',
                'title' => $hesklang['cant_sql'],
                'message' => sprintf('%s: %s', $hesklang['contact_webmsater'], $hesk_settings['webmaster_mail'])
            ));
        }
        exit();
    } elseif ($hesk_settings['db_failure_response'] === 'throw') {
        $message = $hesk_settings['debug_mode'] ? mysqli_error($hesk_db_link) : $hesklang['cant_sql'];
        throw new Exception($message);
    }
}

function hesk_dbFetchAssoc($res)
{

    return @mysqli_fetch_assoc($res);

} // END hesk_FetchAssoc()


function hesk_dbFetchRow($res)
{

    return @mysqli_fetch_row($res);

} // END hesk_FetchRow()


function hesk_dbResult($res, $row = 0, $column = 0)
{
	$i=0;
	$res->data_seek(0);

	while ($tmp = @mysqli_fetch_array($res, MYSQLI_NUM))
    {
		if ($i==$row)
        {
        	return $tmp[$column];
        }
		$i++;
	}

	return '';

} // END hesk_dbResult()


function hesk_dbInsertID()
{
	global $hesk_db_link;

    if ($lastid = @mysqli_insert_id($hesk_db_link))
    {
        return $lastid;
    }

} // END hesk_dbInsertID()


function hesk_dbFreeResult($res)
{

    return @mysqli_free_result($res);

} // END hesk_dbFreeResult()


function hesk_dbNumRows($res)
{

    return @mysqli_num_rows($res);

} // END hesk_dbNumRows()


function hesk_dbAffectedRows()
{
	global $hesk_db_link;

    return @mysqli_affected_rows($hesk_db_link);

} // END hesk_dbAffectedRows()
email_functions.inc.php
wget 'https://lists2.roe3.org/hesk/inc/email_functions.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {
    die('Invalid attempt');
}

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
use PHPMailer\PHPMailer\SMTP;

// Load required includes if not already
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');

function hesk_notifyCustomer($email_template = 'new_ticket')
{
    global $hesk_settings, $hesklang, $ticket;

    // Demo mode
    if (defined('HESK_DEMO')) {
        return true;
    }

    // We don't need to attach any original ticket files when marking as "Resolved" without replying
    if ($email_template == 'ticket_closed') {
        $ticket['attachments'] = '';
    }

    $ticket['email_original'] = $ticket['email'];
    $hesk_settings['EMAIL_IDS'] = array();

    $customers = hesk_get_customers_for_ticket($ticket['id']);
    $ticket['customers'] = $customers;

    $language_to_customers = [];
    foreach ($customers as $customer) {
        if ($customer['email'] === null || $customer['email'] === '') {
            continue;
        }

        // Make sure customer gets response in correct language
        $language = HESK_DEFAULT_LANGUAGE;
        if (isset($customer['language'])) {
            $language = $customer['language'];
        } elseif (isset($ticket['language'])) {
            $language = $ticket['language'];
        }

        if (!isset($language)) {
            $language_to_customers[$language] = [];
        }
        $language_to_customers[$language][] = $customer;
    }

    foreach ($language_to_customers as $language => $customers) {
        hesk_setLanguage($language);

        $subject = hesk_getEmailSubject($email_template, $ticket);
        $customer_emails = hesk_getEmailsForCustomers($customers);

        // Customers will have to login to reply, no need to complicate
        if ($hesk_settings['customer_accounts_required'] && $hesk_settings['customer_accounts']) {
            list($message, $html_message, $mail_direct_attachment) = hesk_getEmailMessage($email_template, $ticket);
            hesk_mail($customer_emails['requester'], $customer_emails['followers'], $subject, $message, $html_message, $ticket['trackid'], $mail_direct_attachment);
        } else {
            // Customers do not have to log in to reply.
            // To make sure the correct replier is used, send each customer an email with a unique ticket tracking URL address with their own email address
            $ticket['email'] = $customer_emails['requester'];
            list($message, $html_message, $mail_direct_attachment) = hesk_getEmailMessage($email_template, $ticket);
            hesk_mail($customer_emails['requester'], array(), $subject, $message, $html_message, $ticket['trackid'], $mail_direct_attachment);

            foreach ($customer_emails['followers'] as $email) {
                $ticket['email'] = $email;
                list($message, $html_message, $mail_direct_attachment) = hesk_getEmailMessage($email_template, $ticket);
                hesk_mail($email, array(), $subject, $message, $html_message, $ticket['trackid'], $mail_direct_attachment);
            }
        }

        // Reset language if needed
        hesk_resetLanguage();
    }

    $ticket['email'] = $ticket['email_original'];

    if (count($hesk_settings['EMAIL_IDS'])) {
        $sql_values = array();
        foreach ($hesk_settings['EMAIL_IDS'] as $eid) {
            $sql_values[] = "('".hesk_dbEscape($eid)."', ".$ticket['id'].", 1)";
        }
        hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."email_id_to_ticket` (`email_id`, `ticket_id`, `from_hesk`) VALUES " . implode(', ', $sql_values));
    }
    unset($hesk_settings['EMAIL_IDS']);

    return true;

} // END hesk_notifyCustomer()

function hesk_getEmailsForCustomers($customers) {
    $requester = null;
    $followers = [];
    foreach ($customers as $customer) {
        if ($customer['customer_type'] === 'REQUESTER') {
            $requester = $customer['email'];
        } else {
            $followers[] = $customer['email'];
        }
    }

    return [
        'requester' => $requester,
        'followers' => $followers
    ];
}


function hesk_notifyCollaborators($collaborator_ids, $email_template, $type = 'notify_collaborator_customer_reply', $exclude_users = array())
{
    global $hesk_settings, $hesklang, $ticket;

    // Demo mode
    if (defined('HESK_DEMO')) {
        return true;
    }

    if (count($exclude_users)) {
        foreach ($collaborator_ids as $k => $v) {
            if (in_array($v, $exclude_users)) {
                unset($collaborator_ids[$k]);
            }
        }
    }

    if (count($collaborator_ids) == 0) {
        return true;
    }

    $admins = array();
    $res = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "users` WHERE `active` = 1 AND `id` IN (".implode(',', $collaborator_ids).") ORDER BY `language`");
    while ($user = hesk_dbFetchAssoc($res)) {
        if (empty($user[$type])) {
            continue;
        }
        $admins[] = $user;
    }

    if (count($admins) > 0) {
        /* Make sure each user gets email in his/her preferred language */
        $current_language = 'NONE';
        $recipients = array();

        /* Loop through staff */
        foreach ($admins as $admin) {
            /* If admin language is NULL force default HESK language */
            if (!$admin['language'] || !isset($hesk_settings['languages'][$admin['language']])) {
                $admin['language'] = HESK_DEFAULT_LANGUAGE;
            }

            /* Generate message or add email to the list of recepients */
            if ($admin['language'] == $current_language) {
                /* We already have the message, just add email to the recipients list */
                $recipients[] = $admin['email'];
            } else {
                /* Send email messages in previous languages (if required) */
                if ($current_language != 'NONE') {
                    /* Send e-mail to staff */
                    hesk_mail(implode(',', $recipients), [], $subject, $message, $html_message);

                    /* Reset list of email addresses */
                    $recipients = array();
                }

                /* Set new language */
                hesk_setLanguage($admin['language']);

                /* Format staff email subject and message for this language */
                $subject = hesk_getEmailSubject($email_template, $ticket);
                list($message, $html_message, $mail_direct_attachment) = hesk_getEmailMessage($email_template, $ticket, 1);

                /* Add email to the recipients list */
                $recipients[] = $admin['email'];

                /* Remember the last processed language */
                $current_language = $admin['language'];
            }
        }

        /* Send email messages to the remaining staff */
        hesk_mail(implode(',', $recipients), [], $subject, $message, $html_message, $ticket['trackid'], $mail_direct_attachment);

        /* Reset language to original one */
        hesk_resetLanguage();
    }

    return true;

} // END hesk_notifyCollaborators()


function hesk_notifyAssignedStaff($autoassign_owner, $email_template, $type = 'notify_assigned', $type_collaborators = false, $exclude_users = array())
{
    global $hesk_settings, $hesklang, $ticket;

    // Demo mode
    if (defined('HESK_DEMO')) {
        return true;
    }

    $ticket['owner'] = intval($ticket['owner']);

    // This will be list of staff we need to lookup from the DB
    $lookup = array();

    // This will be staff that we need to notify
    $admins = array();

    if ($autoassign_owner === false) {
        $lookup[] = $ticket['owner'];
    } else {
        $admins[] = $autoassign_owner;
        $hesk_settings['user_data'][$ticket['owner']] = $autoassign_owner;
    }

    if ($type_collaborators !== false && count($ticket['collaborators'])) {
        $lookup = array_merge($lookup, $ticket['collaborators']);
    }

    $lookup = array_unique($lookup);

    if (count($exclude_users)) {
        foreach ($lookup as $k => $v) {
            if (in_array($v, $exclude_users)) {
                unset($lookup[$k]);
            }
        }
    }

    // print_r($lookup);

    // Get user info from the DB if we need it
    if (count($lookup)) {
        $res = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "users` WHERE `active` = 1 AND `id` IN (".implode(',', $lookup).") ORDER BY `language`");

        while ($user = hesk_dbFetchAssoc($res)) {
            $hesk_settings['user_data'][$user['id']] = $user;

            // Owner, but doesn't want to be notified
            if ($user['id'] == $ticket['owner'] && empty($user[$type])) {
                continue;
            }

            // Collaborator, but doesn't want to be notified
            if ($user['id'] != $ticket['owner'] && $type_collaborators !== false && empty($user[$type_collaborators])) {
                continue;
            }

            // Wants to be notified
            $admins[] = $user;
        }
    }

    if (count($exclude_users)) {
        foreach ($admins as $k => $v) {
            if (in_array($v, $exclude_users)) {
                unset($admins[$k]);
            }
        }
    }

    // print_r($admins);

    // We have staff to notify; make sure they get emails in ther preferred langauge
    if (count($admins) > 0) {

        $to = array();
        $cc = array();
        $current_language = 'NONE';

        foreach ($admins as $admin) {
            // If admin language is NULL force default HESK language
            if (!$admin['language'] || !isset($hesk_settings['languages'][$admin['language']])) {
                $admin['language'] = HESK_DEFAULT_LANGUAGE;
            }

            // Generate message or add email to the list of recepients
            if ($admin['language'] == $current_language) {
                // We already have the message, just add email to the recipients list
                // Owner will be "To", Collaborators in "Cc"
                if ($admin['id'] == $ticket['owner']) {
                    $to[] = $admin['email'];
                } else {
                    $cc[] = $admin['email'];
                }
            } else {
                // Send email messages in previous languages (if required)
                if ($current_language != 'NONE') {
                    hesk_mail(implode(',', $to), $cc, $subject, $message, $html_message);

                    // Reset recipients
                    $to = array();
                    $cc = array();
                }

                // Set new language
                hesk_setLanguage($admin['language']);

                // Format staff email subject and message for this language
                $subject = hesk_getEmailSubject($email_template, $ticket);
                list($message, $html_message, $mail_direct_attachment) = hesk_getEmailMessage($email_template, $ticket, 1);

                // Add email to the recipients list
                if ($admin['id'] == $ticket['owner']) {
                    $to[] = $admin['email'];
                } else {
                    $cc[] = $admin['email'];
                }

                // Remember the last processed language
                $current_language = $admin['language'];
            }
        }

        // Send email messages to the remaining staff
        hesk_mail(implode(',', $to), $cc, $subject, $message, $html_message, $ticket['trackid'], $mail_direct_attachment);

        // Reset language to original one
        hesk_resetLanguage();
    }

    return true;

} // END hesk_notifyAssignedStaff()


function hesk_notifyStaff($email_template, $sql_where, $is_ticket = 1, $exclude_users = array())
{
    global $hesk_settings, $hesklang, $ticket;

    // Demo mode
    if (defined('HESK_DEMO')) {
        return true;
    }

    $admins = array();

    $res = hesk_dbQuery("SELECT `id`,`email`,`language`,`isadmin`,`categories` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "users` WHERE `active` = 1 AND ( $sql_where ) ORDER BY `language`");
    while ($myuser = hesk_dbFetchAssoc($res)) {
        /* Is this an administrator? */
        if ($myuser['isadmin']) {
            $admins[$myuser['id']] = array('email' => $myuser['email'], 'language' => $myuser['language']);
            continue;
        }

        /* Not admin, is he/she allowed this category? */
        if ( ! function_exists('hesk_getCategoriesForUser')) {
            require(HESK_PATH . 'inc/admin_functions.inc.php');
        }
        $myuser['categories'] = hesk_getCategoriesForUser($myuser['id']);
        if (in_array($ticket['category'], $myuser['categories'])) {
            $admins[$myuser['id']] = array('email' => $myuser['email'], 'language' => $myuser['language']);
            continue;
        }
    }

    if (count($exclude_users)) {
        foreach ($admins as $k => $v) {
            if (in_array($k, $exclude_users)) {
                unset($admins[$k]);
            }
        }
    }

    if (count($admins) > 0) {
        /* Make sure each user gets email in his/her preferred language */
        $current_language = 'NONE';
        $recipients = array();

        /* Loop through staff */
        foreach ($admins as $admin) {
            /* If admin language is NULL force default HESK language */
            if (!$admin['language'] || !isset($hesk_settings['languages'][$admin['language']])) {
                $admin['language'] = HESK_DEFAULT_LANGUAGE;
            }

            /* Generate message or add email to the list of recepients */
            if ($admin['language'] == $current_language) {
                /* We already have the message, just add email to the recipients list */
                $recipients[] = $admin['email'];
            } else {
                /* Send email messages in previous languages (if required) */
                if ($current_language != 'NONE') {
                    /* Send e-mail to staff */
                    hesk_mail(implode(',', $recipients), [], $subject, $message, $html_message);

                    /* Reset list of email addresses */
                    $recipients = array();
                }

                /* Set new language */
                hesk_setLanguage($admin['language']);

                /* Format staff email subject and message for this language */
                $subject = hesk_getEmailSubject($email_template, $ticket);
                list($message, $html_message, $mail_direct_attachment) = hesk_getEmailMessage($email_template, $ticket, $is_ticket);

                /* Add email to the recipients list */
                $recipients[] = $admin['email'];

                /* Remember the last processed language */
                $current_language = $admin['language'];
            }
        }

        /* Send email messages to the remaining staff */
        hesk_mail(implode(',', $recipients), [], $subject, $message, $html_message, $ticket['trackid'], $mail_direct_attachment);

        /* Reset language to original one */
        hesk_resetLanguage();
    }

    return true;

} // END hesk_notifyStaff()


function hesk_notifyStaffOfPendingApprovals($num)
{
    global $hesk_settings, $hesklang, $ticket;

    // Demo mode
    if (defined('HESK_DEMO')) {
        return true;
    }

    $admins = array();
    $res = hesk_dbQuery("SELECT `email`,`language` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "users` WHERE `active` = 1 AND (`notify_customer_approval` = '1' AND (`isadmin` = '1' OR `heskprivileges` LIKE '%can_man_customers%')) ORDER BY `language`");
    while ($myuser = hesk_dbFetchAssoc($res)) {
        $admins[] = array('email' => $myuser['email'], 'language' => $myuser['language']);
    }

    if (count($admins) == 0) {
        return true;
    }

    $email_template = 'new_customer_approval';
    $verification_url = $hesk_settings['hesk_url'] . '/' . $hesk_settings['admin_dir'] . '/manage_customers.php';

    /* Make sure each user gets email in his/her preferred language */
    $current_language = 'NONE';
    $recipients = array();

    /* Loop through staff */
    foreach ($admins as $admin) {
        /* If admin language is NULL force default HESK language */
        if (!$admin['language'] || !isset($hesk_settings['languages'][$admin['language']])) {
            $admin['language'] = HESK_DEFAULT_LANGUAGE;
        }

        /* Generate message or add email to the list of recepients */
        if ($admin['language'] == $current_language) {
            /* We already have the message, just add email to the recipients list */
            $recipients[] = $admin['email'];
        } else {
            /* Send email messages in previous languages (if required) */
            if ($current_language != 'NONE') {
                /* Send e-mail to staff */
                hesk_mail(implode(',', $recipients), [], $subject, $message, $html_message);

                /* Reset list of email addresses */
                $recipients = array();
            }

            /* Set new language */
            hesk_setLanguage($admin['language']);

            // Format email subject and message
            $subject = hesk_getEmailSubject($email_template, '', 0);
            list($message, $html_message) = hesk_getEmailMessage($email_template, $admin, 0, 0, 1);

            // Replace message special tags
            list($message, $html_message) = hesk_replace_email_tag('%%SITE_TITLE%%', hesk_msgToPlain($hesk_settings['site_title'], 1, 0), $message, $html_message);
            list($message, $html_message) = hesk_replace_email_tag('%%SITE_URL%%', $hesk_settings['site_url'] . ' ', $message, $html_message);
            list($message, $html_message) = hesk_replace_email_tag('%%VERIFICATION_URL%%', $verification_url, $message, $html_message);
            list($message, $html_message) = hesk_replace_email_tag('%%NUM%%', $num, $message, $html_message);

            /* Add email to the recipients list */
            $recipients[] = $admin['email'];

            /* Remember the last processed language */
            $current_language = $admin['language'];
        }
    }

    /* Send email messages to the remaining staff */
    hesk_mail(implode(',', $recipients), [], $subject, $message, $html_message);

    /* Reset language to original one */
    hesk_resetLanguage();

    return true;

} // END hesk_notifyStaffOfPendingApprovals()


function hesk_sendOverdueTicketReminder($ticket, $users)
{
    if (defined('HESK_DEMO')) {
        return true;
    }

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

    return true;
}

function hesk_sendCustomerRegistrationEmail($user, $verification_token, $email_template = 'customer_verify_registration') {
    global $hesklang, $hesk_settings;

    if (defined('HESK_DEMO')) {
        return true;
    }

    hesk_setLanguage($user['language']);

    // Format email subject and message
    $subject = hesk_getEmailSubject($email_template, '', 0);
    list($message, $html_message) = hesk_getEmailMessage($email_template,
        $user,
        0,
        0,
        1);

    // Replace message special tags
    $verification_url = $hesk_settings['hesk_url'] . '/verify_registration.php?email='.urlencode($user['email']).'&verificationToken='.urlencode($verification_token);
    list($message, $html_message) = hesk_replace_email_tag('%%NAME%%', hesk_full_name_to_first_name(hesk_msgToPlain($user['name'], 1, 1)), $message, $html_message);
    list($message, $html_message) = hesk_replace_email_tag('%%SITE_TITLE%%', hesk_msgToPlain($hesk_settings['site_title'], 1, 0), $message, $html_message);
    list($message, $html_message) = hesk_replace_email_tag('%%SITE_URL%%', $hesk_settings['site_url'] . ' ', $message, $html_message);
    list($message, $html_message) = hesk_replace_email_tag('%%VERIFICATION_URL%%', $verification_url, $message, $html_message);

    // Send email
    hesk_mail($user['email'], [], $subject, $message, $html_message);

    // Reset language if needed
    hesk_resetLanguage();
}

function hesk_sendCustomerRegistrationApprovedEmail($user) {
    global $hesklang, $hesk_settings;

    if (defined('HESK_DEMO')) {
        return true;
    }

    hesk_setLanguage($user['language']);

    // Format email subject and message
    $subject = hesk_getEmailSubject('customer_approved', '', 0);
    list($message, $html_message) = hesk_getEmailMessage('customer_approved',
        $user,
        0,
        0,
        1);

    // Replace message special tags
    list($message, $html_message) = hesk_replace_email_tag('%%NAME%%', hesk_full_name_to_first_name(hesk_msgToPlain($user['name'], 1, 1)), $message, $html_message);
    list($message, $html_message) = hesk_replace_email_tag('%%SITE_TITLE%%', hesk_msgToPlain($hesk_settings['site_title'], 1, 0), $message, $html_message);
    list($message, $html_message) = hesk_replace_email_tag('%%SITE_URL%%', $hesk_settings['site_url'] . ' ', $message, $html_message);
    list($message, $html_message) = hesk_replace_email_tag('%%CUSTOMER_LOGIN_URL%%', $hesk_settings['hesk_url'] . '/login.php', $message, $html_message);

    // Send email
    hesk_mail($user['email'], [], $subject, $message, $html_message);

    // Reset language if needed
    hesk_resetLanguage();
}

function hesk_sendCustomerRegistrationRejectedEmail($user) {
    global $hesklang, $hesk_settings;

    if (defined('HESK_DEMO')) {
        return true;
    }

    hesk_setLanguage($user['language']);

    // Format email subject and message
    $subject = hesk_getEmailSubject('customer_rejected', '', 0);
    list($message, $html_message) = hesk_getEmailMessage('customer_rejected',
        $user,
        0,
        0,
        1);

    // Replace message special tags
    list($message, $html_message) = hesk_replace_email_tag('%%NAME%%', hesk_full_name_to_first_name(hesk_msgToPlain($user['name'], 1, 1)), $message, $html_message);
    list($message, $html_message) = hesk_replace_email_tag('%%SITE_TITLE%%', hesk_msgToPlain($hesk_settings['site_title'], 1, 0), $message, $html_message);
    list($message, $html_message) = hesk_replace_email_tag('%%SITE_URL%%', $hesk_settings['site_url'] . ' ', $message, $html_message);

    // Send email
    hesk_mail($user['email'], [], $subject, $message, $html_message);

    // Reset language if needed
    hesk_resetLanguage();
}

function hesk_sendCustomerTicket2EmailFailure($email_info, $template) {
    global $hesklang, $hesk_settings;

    if (defined('HESK_DEMO')) {
        return true;
    }

    // Format email subject and message
    $subject = hesk_getEmailSubject($template, '', 0);
    list($message, $html_message) = hesk_getEmailMessage($template, null, 0, 0, 1);

    // Replace message special tags
    list($message, $html_message) = hesk_replace_email_tag('%%NAME%%', hesk_full_name_to_first_name(hesk_msgToPlain($email_info['name'], 1, 1)), $message, $html_message);
    list($message, $html_message) = hesk_replace_email_tag('%%SUBJECT%%', hesk_msgToPlain($email_info['subject'], 1, 1), $message, $html_message);
    list($message, $html_message) = hesk_replace_email_tag('%%CUSTOMER_REGISTER_URL%%', $hesk_settings['hesk_url'] . '/register.php', $message, $html_message);
    list($message, $html_message) = hesk_replace_email_tag('%%SITE_TITLE%%', hesk_msgToPlain($hesk_settings['site_title'], 1, 0), $message, $html_message);
    list($message, $html_message) = hesk_replace_email_tag('%%SITE_URL%%', $hesk_settings['site_url'] . ' ', $message, $html_message);

    // Send email
    hesk_mail($email_info['email'], [], $subject, $message, $html_message);

    // Reset language if needed
    hesk_resetLanguage();
}

function hesk_getCustomerEmailFilenames()
{
    return [
        'forgot_ticket_id',
        'new_reply_by_staff',
        'new_ticket',
        'new_ticket_by_staff',
        'ticket_closed',
        'survey',
        'customer_reset_password',
        'customer_verify_registration',
        'customer_verify_new_email',
        'customer_approved',
        'customer_rejected',
        'email_rejected_can_self_register',
        'email_rejected_cannot_self_register'
    ];
} // END hesk_getCustomerEmails()


function hesk_validEmails()
{
    global $hesklang;

    return array(

        /*** Emails sent to CLIENT ***/

        // --> Send reminder about existing tickets
        'forgot_ticket_id' => $hesklang['forgot_ticket_id'],

        // --> Staff replied to a ticket
        'new_reply_by_staff' => $hesklang['new_reply_by_staff'],

        // --> New ticket submitted
        'new_ticket' => $hesklang['ticket_received'],

        // --> New ticket submitted by staff
        'new_ticket_by_staff' => $hesklang['new_ticket_by_staff'], 

        // --> Ticket closed
        'ticket_closed' => $hesklang['ticket_closed'],

        // --> Follow-up survey email
        'survey' => $hesklang['follow_up_email'],

        // --> Customer password reset email
        'customer_reset_password' => $hesklang['customer_reset_password'],

        // --> Customer Registration email
        'customer_verify_registration' => $hesklang['customer_verify_registration'],

        // --> Customer change email
        'customer_verify_new_email' => $hesklang['customer_verify_new_email'],

        // --> Customer registration approved
        'customer_approved' => $hesklang['customer_approved'],

        // --> Customer registration rejected
        'customer_rejected' => $hesklang['customer_rejected'],

        // --> Email-to-ticket rejection emails
        'email_rejected_can_self_register' => $hesklang['email_rejected'],
        'email_rejected_cannot_self_register' => $hesklang['email_rejected'],

        /*** Emails sent to STAFF ***/

        // --> Ticket moved to a new category
        'category_moved' => $hesklang['category_moved'],

        // --> Client replied to a ticket
        'new_reply_by_customer' => $hesklang['new_reply_by_customer'],

        // --> New ticket submitted
        'new_ticket_staff' => $hesklang['new_ticket_staff'],

        // --> New ticket assigned to staff
        'ticket_assigned_to_you' => $hesklang['ticket_assigned_to_you'],

        // --> New private message
        'new_pm' => $hesklang['new_pm'],

        // --> New note by someone to a ticket assigned to you
        'new_note' => $hesklang['new_note'],

        // --> Staff password reset email
        'reset_password' => $hesklang['reset_password'],

        // --> Overdue ticket email
        'overdue_ticket' => $hesklang['overdue_ticket'],

        // --> Staff notification of escalated ticket
        'ticket_escalated' => $hesklang['ticket_escalated'],

        // --> Email notifying of new customer accounts pending approval
        'new_customer_approval' => $hesklang['new_customer_approval'],

        // --> Added as a collaborator
        'collaborator_added' => $hesklang['collaborator_added'],

        // --> Staff replied to a collaborator ticket
        'collaborator_staff_reply' => $hesklang['collaborator_staff_reply'],

        // --> New note in a collaborator ticket
        'collaborator_note' => $hesklang['collaborator_note'],

        // --> A collaborator ticket is resolved
        'collaborator_resolved' => $hesklang['collaborator_resolved'],

        // --> A collaborator ticket is overdue
        'collaborator_overdue' => $hesklang['collaborator_overdue'],

        /*** Emails sent to STAFF or CUSTOMERS ***/

        // --> MFA Verification email
        'mfa_verification' => $hesklang['mfa_verification']

    );
} // END hesk_validEmails()


function hesk_mail($to, $cc, $subject, $message, $html_message, $tracking_ID = null, $mail_direct_attachment = [])
{
    global $hesk_settings, $hesklang;

    // Demo mode
    if (defined('HESK_DEMO')) {
        return true;
    }

    // usleep(100);

    // Let's check our recipients
    if (empty($to)) {
         if (empty($cc)) {
            // No recipients at all
            return true;
         } else {
            // If we don't have a To, let's use the first Cc as To; spam filters don't like empty To headers.
            $to = array_shift($cc);
         }
    }

    // Empty CCs?
    if ($cc === null) {
        $cc = [];
    }

    // Stop if we find anything suspicious in the headers
    $ccstring = implode('',$cc);
    if (preg_match("/\n|\r|\t|%0A|%0D|%08|%09/", $to . $ccstring . $subject)) {
        return false;
    }

    // Encode subject to UTF-8
    $subject = hesk_html_entity_decode($subject);

    // Setup "name <email>" for headers
    if ($hesk_settings['noreply_name']) {
        $hesk_settings['from_name'] = $hesk_settings['noreply_name'];
    } else {
        $hesk_settings['from_name'] = $hesk_settings['noreply_mail'];
    }

    // Uncomment for debugging
    # echo "<p>TO: $to<br >SUBJECT: $subject<br >MSG: $message</p>";
    # return true;

    // Remove duplicate recipients
    if ($to !== null && $to !== '') {
        $to_arr = array_unique(explode(',', $to));
        $to_arr = array_values($to_arr);
    } else {
        $to_arr = [];
    }
    $cc_arr = array_unique($cc);

    // Lowercase all emails
    $to_arr = array_map('strtolower', $to_arr);
    $cc_arr = array_map('strtolower', $cc_arr);

    // Remove muted emails
    $muted = hesk_get_muted_emails();

    if (count($muted['emails'])) {
        $to_arr = array_diff($to_arr, $muted['emails']);
        $cc_arr = array_diff($cc_arr, $muted['emails']);
    }

    if (count($muted['domains'])) {
        $to_arr_copy = $to_arr;
        $to_arr = [];

        foreach ($to_arr_copy as $to) {
            $is_muted = false;

            foreach ($muted['domains'] as $domain) {
                if (hesk_str_ends_with($to, $domain)) {
                    $is_muted = true;
                    break;
                }
            }

            if ($is_muted == false) {
                $to_arr[] = $to;
            }
        }

        $cc_arr_copy = $cc_arr;
        $cc_arr = [];

        foreach ($cc_arr_copy as $to) {
            $is_muted = false;

            foreach ($muted['domains'] as $domain) {
                if (hesk_str_ends_with($to, $domain)) {
                    $is_muted = true;
                    break;
                }
            }

            if ($is_muted == false) {
                $cc_arr[] = $to;
            }
        }
    }

    // Let's check our recipients again after muted check
    if (empty($to_arr)) {
         if (empty($cc_arr)) {
            return true;
         } else {
            $to_arr[] = array_shift($cc_arr);
         }
    }

    // Check the number of email recipients
    if ($hesk_settings['email_max_recipients']) {
        $to_num = count($to_arr);
        if ($to_num >= $hesk_settings['email_max_recipients']) {
            $to_arr = array_splice($to_arr, 0, $hesk_settings['email_max_recipients']);
            $cc_arr = [];
        } elseif (($to_num + count($cc_arr)) > $hesk_settings['email_max_recipients']) {
            $cc_arr = array_splice($cc_arr, 0, $hesk_settings['email_max_recipients'] - $to_num);
        }
    }

    // Start output buffering so that any errors don't break headers
    ob_start();

    try {
        $mailer = new PHPMailer(true);
        $mailer->XMailer = ' ';

        if ($hesk_settings['smtp']) {
            $mailer->SMTPDebug = SMTP::DEBUG_SERVER;
            $mailer->isSMTP();
            $mailer->Host = $hesk_settings['smtp_host_name'];
            $mailer->Port = $hesk_settings['smtp_host_port'];

            if (strlen($hesk_settings['smtp_user']) || strlen($hesk_settings['smtp_password']) || $hesk_settings['smtp_oauth_provider']) {
                $mailer->SMTPAuth = true;
                $mailer->Username = $hesk_settings['smtp_user'];

                if ($hesk_settings['smtp_conn_type'] == 'oauth') {
                    require_once(HESK_PATH . 'inc/oauth_functions.inc.php');
                    require_once(HESK_PATH . 'inc/mail/HeskOAuthTokenProvider.php');

                    $oauthTokenProvider = new \PHPMailer\PHPMailer\HeskOAuthTokenProvider();
                    $oauthTokenProvider->username = $hesk_settings['smtp_user'];
                    $oauthTokenProvider->provider = $hesk_settings['smtp_oauth_provider'];

                    $mailer->AuthType = 'XOAUTH2';
                    $mailer->setOAuth($oauthTokenProvider);
                } else {
                    $mailer->Password = hesk_htmlspecialchars_decode($hesk_settings['smtp_password']);
                }
            }

            $mailer->Timeout = $hesk_settings['smtp_timeout'];
            if ($hesk_settings['smtp_noval_cert']) {
                $mailer->SMTPOptions = array(
                    'ssl' => array(
                        'verify_peer' => false,
                        'verify_peer_name' => false,
                        'allow_self_signed' => true
                    )
                );
            }
            if ($hesk_settings['smtp_enc'] == 'ssl') {
                $mailer->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
            } elseif ($hesk_settings['smtp_enc'] == 'tls') {
                $mailer->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
            }
        }

        $mailer->setFrom($hesk_settings['noreply_mail'], $hesk_settings['noreply_name']);
        $mailer->addReplyTo($hesk_settings['noreply_mail'], $hesk_settings['noreply_name']);
        foreach ($to_arr as $to) {
            $mailer->addAddress($to);
        }
        foreach ($cc_arr as $address) {
            $mailer->addCC($address);
        }
        $mailer->Subject = $subject;
        $mailer->CharSet = $hesklang['ENCODING'];

        // Save ticket ID in a custom header
        if ($tracking_ID !== null) {
            $mailer->addCustomHeader('X-Hesk-Tracking_ID', $tracking_ID);
        }

        // Do we auto-generate the AltBody from the HTML template?
        if ($hesk_settings['email_formatting'] == 2) {
            $mailer->msgHTML($html_message);
        } else {
            // HTML email?
            if ($hesk_settings['email_formatting']) {
                $mailer->isHTML();
            }

            // Body holds plaintext if we're not sending HTML
            $mailer->Body = $hesk_settings['email_formatting'] ?
                $html_message :
                $message;

            // Only include plain text in Alt if both HTML and plain text are being sent
            if ($hesk_settings['email_formatting'] == 3) {
                $mailer->AltBody = $message;
            }
        }
        // Attachments
        if(!empty($mail_direct_attachment)){
            foreach ($mail_direct_attachment as $a => $b) {
                if (file_exists($b)) {
                    $mailer->addAttachment($b); //Put real path of attachments
                }
            }
        }
    } catch (Exception $e) {
        if ($hesk_settings['debug_mode']) {
            $error = $hesklang['cnsm'] . ' ' . $to . '<br /><br />' . $hesklang['error'] . ': ' . htmlspecialchars($mailer->ErrorInfo);
            if ($debug_log = ob_get_contents()) {
                $error .= '<br /><br /><textarea name="smtp_log" rows="10" cols="60">' . $debug_log . '</textarea>';
            }
            $_SESSION['HESK_2ND_NOTICE'] = true;
            $_SESSION['HESK_2ND_MESSAGE'] = $hesklang['esf'] . ' ' . $error;
        } else {
            $_SESSION['HESK_2ND_NOTICE'] = true;
            $_SESSION['HESK_2ND_MESSAGE'] = $hesklang['esf'] . ' ' . $hesklang['contact_webmsater'] . ' <a href="mailto:' . $hesk_settings['webmaster_mail'] . '">' . $hesk_settings['webmaster_mail'] . '</a>';
        }

        ob_end_clean();
        return false;
    }

    try {
        ob_start();
        $mailer->send();
        if (isset($hesk_settings['EMAIL_IDS'])) {
            $hesk_settings['EMAIL_IDS'][] = $mailer->getLastMessageID();
        }
        ob_end_clean();
    } catch (Exception $e) {
        if ($hesk_settings['debug_mode']) {
            $error = $hesklang['cnsm'] . ' ' . $to . '<br /><br />' . $hesklang['error'] . ': ' . htmlspecialchars($mailer->ErrorInfo);
            if ($debug_log = ob_get_contents()) {
                $error .= '<br /><br /><textarea name="smtp_log" rows="10" cols="60">' . $debug_log . '</textarea>';
            }
            $_SESSION['HESK_2ND_NOTICE'] = true;
            $_SESSION['HESK_2ND_MESSAGE'] = $hesklang['esf'] . ' ' . $error;
        } else {
            $_SESSION['HESK_2ND_NOTICE'] = true;
            $_SESSION['HESK_2ND_MESSAGE'] = $hesklang['esf'] . ' ' . $hesklang['contact_webmsater'] . ' <a href="mailto:' . $hesk_settings['webmaster_mail'] . '">' . $hesk_settings['webmaster_mail'] . '</a>';
        }

        ob_end_clean();
        return false;
    }

    ob_end_clean();
    return true;
} // END hesk_mail()


function hesk_getEmailSubject($eml_file, $ticket = '', $is_ticket = 1, $strip = 0)
{
    global $hesk_settings, $hesklang;

    // Demo mode
    if (defined('HESK_DEMO')) {
        return '';
    }

    /* Get list of valid emails */
    $valid_emails = hesk_validEmails();

    /* Verify this is a valid email include */
    if (!isset($valid_emails[$eml_file])) {
        hesk_error($hesklang['inve']);
    } else {
        $msg = $valid_emails[$eml_file];
    }

    /* If not a ticket-related email return subject as is */
    if (!$ticket) {
        return $msg;
    }

    /* Strip slashes from the subject only if it's a new ticket */
    if ($strip) {
        $ticket['subject'] = stripslashes($ticket['subject']);
    }

    /* Not a ticket, but has some info in the $ticket array */
    if (!$is_ticket) {
        return str_replace('%%SUBJECT%%', $ticket['subject'], $msg);
    }

    /* Set category title */
    $ticket['category'] = hesk_msgToPlain(hesk_getCategoryName($ticket['category']), 1, 0);

    /* Get priority */
    $ticket['priority'] = hesk_get_priority_name($ticket['priority']);

    /* Set status */
    $ticket['status'] = hesk_get_status_name($ticket['status']);

    // Convert any entities in site title to plain text
    $site_title = hesk_msgToPlain($hesk_settings['site_title'], 1, 0);

    /* Replace all special tags */
    $msg = str_replace('%%SITE_TITLE%%', $site_title, $msg);
    $msg = str_replace('%%SUBJECT%%', $ticket['subject'], $msg);
    $msg = str_replace('%%TRACK_ID%%', $ticket['trackid'], $msg);
    $msg = str_replace('%%CATEGORY%%', $ticket['category'], $msg);
    $msg = str_replace('%%PRIORITY%%', $ticket['priority'], $msg);
    $msg = str_replace('%%STATUS%%', $ticket['status'], $msg);

    return $msg;

} // hesk_getEmailSubject()


function hesk_getEmailMessage($eml_file, $ticket, $is_admin = 0, $is_ticket = 1, $just_message = 0)
{
    global $hesk_settings, $hesklang;

    // Demo mode
    if (defined('HESK_DEMO')) {
        return '';
    }

    /* Get list of valid emails */
    $valid_emails = hesk_validEmails();

    /* Verify this is a valid email include */
    if (!isset($valid_emails[$eml_file])) {
        hesk_error($hesklang['inve']);
    }

    /* Get email template */
    $orig_eml_file = $eml_file;
    $eml_file = 'language/' . $hesk_settings['languages'][$hesk_settings['language']]['folder'] . '/emails/' . $orig_eml_file . '.txt';
    $html_eml_file = 'language/' . $hesk_settings['languages'][$hesk_settings['language']]['folder'] . '/html_emails/' . $orig_eml_file . '.txt';

    if (file_exists(HESK_PATH . $eml_file)) {
        $msg = file_get_contents(HESK_PATH . $eml_file);
    } else {
        hesk_error($hesklang['emfm'] . ': ' . $eml_file);
    }

    if (file_exists(HESK_PATH . $html_eml_file)) {
        $html_msg = file_get_contents(HESK_PATH . $html_eml_file);
    } else {
        hesk_error($hesklang['emfm'] . ': ' . $html_eml_file);
    }

    /* Return just the message without any processing? */
    if ($just_message) {
        return array($msg, $html_msg);
    }

    // Convert any entities in site title to plain text
    $site_title = hesk_msgToPlain($hesk_settings['site_title'], 1, 0);

    // Create a HTML-version of the message if needed
    if (isset($ticket['message']) && ! isset($ticket['message_html']) ) {
        $ticket['message_html'] = '';
    }

    /* If it's not a ticket-related mail (like "a new PM") just process quickly */
    if (!$is_ticket) {
        $trackingURL = $hesk_settings['hesk_url'] . '/' . $hesk_settings['admin_dir'] . '/mail.php?a=read&id=' . intval($ticket['id']);

        list($msg, $html_msg) = hesk_replace_email_tag('%%NAME%%', $ticket['name'], $msg, $html_msg);
        list($msg, $html_msg) = hesk_replace_email_tag('%%SUBJECT%%', $ticket['subject'], $msg, $html_msg);
        list($msg, $html_msg) = hesk_replace_email_tag('%%TRACK_URL%%', $trackingURL . ' ', $msg, $html_msg);
        list($msg, $html_msg) = hesk_replace_email_tag('%%SITE_TITLE%%', $site_title, $msg, $html_msg);
        list($msg, $html_msg) = hesk_replace_email_tag('%%SITE_URL%%', $hesk_settings['site_url'] . ' ', $msg, $html_msg);
        list($msg, $html_msg) = hesk_replace_email_tag('%%FIRST_NAME%%', hesk_full_name_to_first_name($ticket['name']), $msg, $html_msg);

        if (isset($ticket['message'])) {
            $msg = str_replace('%%MESSAGE%%', $ticket['message'], $msg);
            $html_msg = str_replace('%%MESSAGE%%', $ticket['message_html'], $html_msg);
        }
        return array($msg, $html_msg);
    }

    $is_customer_email = in_array(basename($eml_file, '.txt'), hesk_getCustomerEmailFilenames());

    $hesk_settings['e_param'] = $hesk_settings['email_view_ticket'] && ! $hesk_settings['customer_accounts_required'] ? '&e=' . rawurlencode($ticket['email']) : '';

    /* Generate the ticket URLs */
    $trackingURL = $hesk_settings['hesk_url'];
    $trackingURL .= $is_admin ? '/' . $hesk_settings['admin_dir'] . '/admin_ticket.php' : '/ticket.php';
    $trackingURL .= '?track=' . $ticket['trackid'] . ($is_admin ? '' : $hesk_settings['e_param']) . '&Refresh=' . rand(10000, 99999);

    /* Set category title */
    $ticket['category'] = hesk_msgToPlain(hesk_getCategoryName($ticket['category']), 1, 0);

    /* Set priority title */
    $ticket['priority'] = hesk_get_priority_name($ticket['priority']);

    /* Get owner name */
    if ($owner_array = hesk_getStaffNameArray($ticket['owner'])) {
        if ($is_customer_email) {
            $ticket['owner'] = hesk_msgToPlain($owner_array['nickname'], 1, 0);
        } else {
            $ticket['owner'] = hesk_msgToPlain($owner_array['name'], 1, 0);
        }
    } else {
        $ticket['owner'] = $hesklang['unas'];
    }

    /* Set status */
    $ticket['status'] = hesk_get_status_name($ticket['status']);

    // Get name of the person who posted the last message
    if (!isset($ticket['last_reply_by'])) {
        $ticket['last_reply_by'] = hesk_getReplierNameArray($ticket);
    }

    /* Replace all special tags */
    if (!function_exists('hesk_get_customers_for_ticket')) {
        require_once(HESK_PATH . 'inc/customer_accounts.inc.php');
    }

    if (!isset($ticket['customers'])) {
        // Apparently the caller didn't get the customers for us :(
        $ticket['customers'] = hesk_get_customers_for_ticket($ticket['id']);
    }
    $customer_info = hesk_getCustomersByRequesterAndFollowers($ticket['customers']);
    $customer_info = hesk_ticketToPlain($customer_info, 1, 0);

    list($msg, $html_msg) = hesk_replace_email_tag('%%REQUESTER%%', hesk_output_customer_name_and_email($customer_info['requester'], false), $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%REQUESTER_NAME%%', $customer_info['requester']['name'], $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%REQUESTER_FIRST_NAME%%', hesk_full_name_to_first_name($customer_info['requester']['name']), $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%REQUESTER_EMAIL%%', $customer_info['requester']['email'], $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%FOLLOWERS%%', hesk_emailFormatFollowers($customer_info['followers'], 'BOTH'), $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%FOLLOWER_NAMES%%', hesk_emailFormatFollowers($customer_info['followers'], 'NAMES'), $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%FOLLOWER_EMAILS%%', hesk_emailFormatFollowers($customer_info['followers'], 'EMAILS'), $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%SUBJECT%%', $ticket['subject'], $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%TRACK_ID%%', $ticket['trackid'], $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%TRACK_URL%%', $trackingURL . ' ', $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%SITE_TITLE%%', $site_title, $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%SITE_URL%%', $hesk_settings['site_url'] . ' ', $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%CATEGORY%%', $ticket['category'], $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%PRIORITY%%', $ticket['priority'], $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%OWNER%%', $ticket['owner'], $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%STATUS%%', $ticket['status'], $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%CREATED%%', $ticket['dt'], $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%UPDATED%%', $ticket['lastchange'], $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%DUE_DATE%%', $ticket['due_date'], $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%ID%%', $ticket['id'], $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%TIME_WORKED%%', $ticket['time_worked'], $msg, $html_msg);

    if (is_string($ticket['last_reply_by'])) {
        list($msg, $html_msg) = hesk_replace_email_tag('%%LAST_REPLY_BY%%', $ticket['last_reply_by'], $msg, $html_msg);
    } elseif ($is_customer_email) {
        list($msg, $html_msg) = hesk_replace_email_tag('%%LAST_REPLY_BY%%', $ticket['last_reply_by']['nickname'], $msg, $html_msg);
    } else {
        list($msg, $html_msg) = hesk_replace_email_tag('%%LAST_REPLY_BY%%', $ticket['last_reply_by']['name'], $msg, $html_msg);
    }

    // These fields are deprecated, but keeping them for backwards compatibility
    // %%NAME%% can be set to Staff name is some cases
    if (in_array($orig_eml_file, array('new_note', 'collaborator_staff_reply'))) {
        list($msg, $html_msg) = hesk_replace_email_tag('%%NAME%%', $_SESSION['name'], $msg, $html_msg);
    } else {
        list($msg, $html_msg) = hesk_replace_email_tag('%%NAME%%', $customer_info['requester']['name'], $msg, $html_msg);
    }
    list($msg, $html_msg) = hesk_replace_email_tag('%%FIRST_NAME%%', hesk_full_name_to_first_name($customer_info['requester']['name']), $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%EMAIL%%', $customer_info['requester']['email'], $msg, $html_msg);

    if (isset($ticket['ESCALATED_BY_RULE'])) {
        list($msg, $html_msg) = hesk_replace_email_tag('%%ESCALATED_BY_RULE%%', $ticket['ESCALATED_BY_RULE'], $msg, $html_msg);
    }

    /* All custom fields */
    for ($i = 1; $i <= 100; $i++) {
        $k = 'custom' . $i;

        if (isset($hesk_settings['custom_fields'][$k])) {
            $v = $hesk_settings['custom_fields'][$k];

            switch ($v['type']) {
                case 'checkbox':
                    $ticket[$k] = str_replace("<br />", "\n", $ticket[$k]);
                    break;
                case 'date':
                    $ticket[$k] = hesk_custom_date_display_format($ticket[$k], $v['value']['date_format']);
                    break;
            }

            list($msg, $html_msg) = hesk_replace_email_tag('%%' . strtoupper($k) . '%%', $ticket[$k], $msg, $html_msg);
        } else {
            list($msg, $html_msg) = hesk_replace_email_tag('%%' . strtoupper($k) . '%%', '', $msg, $html_msg);
        }
    }

    // Let's handle the attachments tag
    $att_links = array();
    $html_att_links = array();
    $mail_direct_attachment = [];

    // Is message tag in email template?
    if (strpos($msg, '%%MESSAGE%%') !== false || strpos($html_msg, '%%MESSAGE%%') !== false) {
        if ($hesk_settings['attachments']['use'] && isset($ticket['attachments']) && strlen($ticket['attachments'])) {
            $att = explode(',', substr($ticket['attachments'], 0, -1));

            if ($hesk_settings['attachments']['attachment_in_email_type'] == '1') {
                //get all attachment id,name and size

                $att_ids = array();
                foreach ($att as $myatt) {
                    list($att_id, $att_name) = explode('#', $myatt);
                    $att_ids[] = intval($att_id);
                }

                $result = hesk_dbQuery("SELECT `att_id`,`saved_name`, `size`,`real_name` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "attachments` WHERE `att_id` IN (".implode(',', $att_ids).")");
                $get_attachment_name_size = [];
                while ($get_all_attachment = hesk_dbFetchAssoc($result)) {
                    $get_attachment_name_size[] = [
                        'att_id' => $get_all_attachment['att_id'],
                        'saved_name' => $get_all_attachment['saved_name'],
                        'att_name' => $get_all_attachment['real_name'],
                        'size' => $get_all_attachment['size']
                    ];
                }

                $d = 1;
                foreach ($get_attachment_name_size as $x => $y) {

                    $directory = $hesk_settings['attach_dir']; // get attachment directory;
                    $r_path = dirname( __FILE__, 2);
                    if ($hesk_settings['attachments']['direct_attachment_in_email'] == "0" &&  $y['size'] < $hesk_settings['attachments']['file_max_size'] && $hesk_settings['attachments']['direct_attachment_in_email_no_of_files'] == '3') {
                        $mail_direct_attachment[] = $r_path.'/'.$directory.'/'.$y['saved_name'];
                    } elseif ($hesk_settings['attachments']['direct_attachment_in_email'] == "0" && $y['size'] < $hesk_settings['attachments']['file_max_size'] && $hesk_settings['attachments']['direct_attachment_in_email_no_of_files'] == '2' && $d <= $hesk_settings['attachments']['first_x_attachments']){
                        $mail_direct_attachment[] = $r_path.'/'.$directory.'/'.$y['saved_name'];
                        $d = $d + 1;
                    } elseif ($hesk_settings['attachments']['direct_attachment_in_email'] == "1" && $hesk_settings['attachments']['direct_attachment_in_email_no_of_files'] == '2' && $d <= $hesk_settings['attachments']['first_x_attachments']){
                        $mail_direct_attachment[] = $r_path.'/'.$directory.'/'.$y['saved_name'];
                        $d = $d + 1;
                    } elseif ($hesk_settings['attachments']['direct_attachment_in_email'] == "1" && $hesk_settings['attachments']['direct_attachment_in_email_no_of_files'] == '3'){
                        $mail_direct_attachment[] = $r_path.'/'.$directory.'/'.$y['saved_name'];
                    } else {
                        $att_id = $y['att_id'];
                        $att_name = $y['att_name'];
                        $url_path = $is_customer_email ? '' : '/admin';
                        $att_links[] = $att_name . "\n" . $hesk_settings['hesk_url'] . $url_path . '/download_attachment.php?att_id=' . $att_id . '&track=' . $ticket['trackid'] . $hesk_settings['e_param'];
                        $html_att_links[] = '<a href="'.$hesk_settings['hesk_url'] . $url_path . '/download_attachment.php?att_id=' . $att_id . '&amp;track=' . $ticket['trackid'] . str_replace('&', '&amp;', $hesk_settings['e_param']).'">'.$att_name.'</a>';
                    }
                }
                
            } else {
                //Direct Link to attchments in emails
                foreach ($att as $myatt) {
                    list($att_id, $att_name) = explode('#', $myatt);
                    $url_path = $is_customer_email ? '' : '/admin';
                    $att_links[] = $att_name . "\n" . $hesk_settings['hesk_url'] . $url_path . '/download_attachment.php?att_id=' . $att_id . '&track=' . $ticket['trackid'] . $hesk_settings['e_param'];
                    $html_att_links[] = '<a href="'.$hesk_settings['hesk_url'] . $url_path . '/download_attachment.php?att_id=' . $att_id . '&amp;track=' . $ticket['trackid'] . str_replace('&', '&amp;', $hesk_settings['e_param']).'">'.$att_name.'</a>';
                }
                //Direct Link to attchments in emails
            }
        }
        //Show text based on email link and direct attachment
        $fatt = $hesklang['fatt'];
        if (!empty($att_links) && !empty($mail_direct_attachment)) {
            $fatt = $hesklang['fatt_2'];
        }

        $att_links = implode(" \n\n", $att_links);
        $html_att_links = implode("<br/>", $html_att_links);

        $msg = str_replace('%%ATTACHMENTS%%', $att_links, $msg, $count_plain);
        $html_msg = str_replace('%%ATTACHMENTS%%', $html_att_links, $html_msg, $count_html);

        // Is message tag in email template?
        if (strpos($msg, '%%MESSAGE%%') !== false || strpos($html_msg, '%%MESSAGE%%') !== false) {
            // If there are attachments to this email and the %%ATTACHMENTS%% tag was not present, add links to attachments below the message
            if ($hesk_settings['attachments']['use'] && isset($ticket['attachments']) && strlen($ticket['attachments'])) {
                if ($count_plain == 0 && $att_links !=='') {
                    $ticket['message'] .= "\n\n" . $fatt . "\n\n" . $att_links;
                }
                if ($count_html == 0  && $att_links !=='') {
                    $ticket['message_html'] .= "<br/><br/>" . $fatt . "<br/>" .  $html_att_links;
                }
            }

            // Replace message
            $msg = str_replace('%%MESSAGE%%', $ticket['message'], $msg);
            $html_msg = str_replace('%%MESSAGE%%', $ticket['message_html'], $html_msg);

            // For customer notifications: if we allow email piping/pop 3 fetching and
            // stripping quoted replies add an "reply above this line" tag
            if (!$is_admin && ($hesk_settings['email_piping'] || $hesk_settings['pop3'] || $hesk_settings['imap']) && $hesk_settings['strip_quoted']) {
                $msg = $hesklang['EMAIL_HR'] . "\n\n" . $msg;
                $html_msg = $hesklang['EMAIL_HR'] . '<br/><br/>' . $html_msg;
            }
        }
    }
    return array($msg, $html_msg, $mail_direct_attachment);

} // END hesk_getEmailMessage

function hesk_getCustomersByRequesterAndFollowers($customers) {
    $requester = null;
    $followers = [];
    foreach ($customers as $customer) {
        if ($customer['customer_type'] === 'REQUESTER') {
            $requester = $customer;
        } else {
            $followers[] = $customer;
        }
    }

    if ( ! is_array($requester)) {
        $requester['name'] = '';
        $requester['email'] = '';
    }

    return [
        'requester' => $requester,
        'followers' => $followers
    ];
}

function hesk_emailFormatFollowers($followers, $format) {
    $output = [];
    foreach ($followers as $follower) {
        if ($format === 'BOTH') {
            $output[] = hesk_output_customer_name_and_email($follower, false);
        } elseif ($format === 'NAMES' && $follower['name'] !== null && $follower['name'] !== '') {
            $output[] = $follower['name'];
        } elseif ($format === 'EMAILS' && $follower['email'] !== null && $follower['email'] !== '') {
            $output[] = $follower['email'];
        }
    }

    return implode(', ', $output);
}

function hesk_replace_email_tag($tag, $value, $message, $html_message, $nl2br = false, $html_value = false) {
    if ($html_value) {
        return array(
            str_replace($tag, $value, $message),
            str_replace($tag, ($nl2br ? nl2br($html_value) : $html_value), $html_message)
        );
    }
    return array(
        str_replace($tag, $value, $message),
        str_replace($tag, ($nl2br ? nl2br(hesk_htmlspecialchars(trim($value))) : hesk_htmlspecialchars(trim($value))), $html_message)
    );
}

function hesk_encodeIfNotAscii($str, $escape_header = false)
{
    // Match anything outside of ASCII range
    if (preg_match('/[^\x00-\x7F]/', $str)) {
        return "=?UTF-8?B?" . base64_encode($str) . "?=";
    }

    // Do we need to wrap the header in double quotes?
    if ($escape_header && preg_match("/[^-A-Za-z0-9!#$%&'*+\/=?^_`{|}~\\s]+/", $str)) {
        return '"' . str_replace('"', '\\"', $str) . '"';
    }

    return $str;
} // END hesk_encodeIfNotAscii()


function hesk_PMtoMainAdmin($landmark)
{
    global $hesk_settings, $hesklang;

    $offer_license = file_exists(HESK_PATH.'hesk_license.php') ? "" : "<h3>&raquo; Look professional</h3>\r\n\r\n<p>To not only support Hesk development but also look more professional, <a href=\"https://www.hesk.com/get/hesk3-license\">remove &quot;Powered by&quot; links</a> from your help desk.</p>\r\n\r\n";

    switch ($landmark) {
        case 100:
            $subject = "Congratulations on your 100th ticket!";
            $message = "</p><div style=\"text-align:justify; padding-left: 10px; padding-right: 10px;\">\r\n\r\n<h2 style=\"padding-left:0px\">You are now part of the Hesk family, and we want to serve you better!</h2>\r\n\r\n<h3>&raquo; Rate us</h3>\r\n\r\n<p>Positive ratings and reviews motivate us to continue developing Hesk. Please take a moment to:</p>\r\n\r\n<ul>\r\n<li>rate or review Hesk at <a href=\"https://softaculous.com/rate/HESK\" rel=\"nofollow\">Softaculous</a></li>\r\n<li>rate or review Hesk at <a href=\"https://alternativeto.net/software/hesk/about/\" rel=\"nofollow\">AlternativeTo</a></li>\r\n</ul>\r\n\r\n<h3>&raquo; Stay updated</h3>\r\n\r\n<p>Hesk regularly receives improvements and bug fixes; make sure you know about them!</p>\r\n<ul>\r\n<li>for fast notifications, <a href=\"https://x.com/HESKdotCOM\" rel=\"nofollow\">follow Hesk on <b>X</b></a></li>\r\n<li>for email notifications, subscribe to our low-volume, zero-spam <a href=\"https://www.hesk.com/newsletter.php\">newsletter</a></li>\r\n</ul>\r\n\r\n<h3>&raquo; Look professional</h3>\r\n\r\n<p><a href=\"https://www.hesk.com/get/hesk3-license\">Remove &quot;Powered by&quot; links</a> to support Hesk development and make it look more professional.</p>\r\n\r\n&nbsp;\r\n\r\n<h3>&raquo; Upgrade to Hesk Cloud for the ultimate experience</h3>\r\n\r\n<p>Experience the best of Hesk by moving your help desk into the Hesk Cloud:</p>\r\n<ul>\r\n<li>exclusive advanced modules,</li>\r\n<li>automated updates,</li>\r\n<li>free migration of your existing Hesk tickets and settings,</li>\r\n<li>we take care of maintenance, server setup and optimization, backups, and more!</li>\r\n</ul>\r\n\r\n<p>&nbsp;<br><a href=\"https://www.hesk.com/get/hesk3-cloud\" class=\"btn btn--blue-border\" style=\"text-decoration:none\">Click here to learn more about Hesk Cloud</a></p>\r\n\r\n<p>&nbsp;</p>\r\n\r\n<p>Best regards,</p>\r\n\r\n<p>Klemen Stirn<br>\r\nFounder<br>\r\n<a href=\"https://www.hesk.com\">https://www.hesk.com</a></p>\r\n\r\n<p>&nbsp;</p>\r\n\r\n</div><p>";
            break;
        case 1000:
            $subject = "We're excited about your 1,000th ticket!";
            $message = "</p><div style=\"text-align:justify; padding-left: 10px; padding-right: 10px;\">\r\n\r\n<h2 style=\"padding-left:0px\">With 1,000 support tickets under the hood, you\'ve become a Hesk Power User. Congratulations!</h2>\r\n\r\n<h3>&raquo; Rate us</h3>\r\n\r\n<p>Positive ratings and reviews motivate us to continue developing Hesk. Please take a moment to:</p>\r\n\r\n<ul>\r\n<li>rate or review Hesk at <a href=\"https://softaculous.com/rate/HESK\" rel=\"nofollow\">Softaculous</a></li>\r\n<li>rate or review Hesk at <a href=\"https://alternativeto.net/software/hesk/about/\" rel=\"nofollow\">AlternativeTo</a></li>\r\n</ul>\r\n\r\n<h3>&raquo; Stay updated</h3>\r\n\r\n<p>Hesk regularly receives improvements and bug fixes; make sure you know about them!</p>\r\n<ul>\r\n<li>for fast notifications, <a href=\"https://x.com/HESKdotCOM\" rel=\"nofollow\">follow Hesk on <b>X</b></a></li>\r\n<li>for email notifications, subscribe to our low-volume, zero-spam <a href=\"https://www.hesk.com/newsletter.php\">newsletter</a></li>\r\n</ul>\r\n\r\n<h3>&raquo; Look professional</h3>\r\n\r\n<p><a href=\"https://www.hesk.com/get/hesk3-license\">Remove &quot;Powered by&quot; links</a> to support Hesk development and make it look more professional.</p>\r\n\r\n&nbsp;\r\n\r\n<h3>&raquo; Upgrade to Hesk Cloud for the ultimate experience</h3>\r\n\r\n<p>Experience the best of Hesk by moving your help desk into the Hesk Cloud:</p>\r\n<ul>\r\n<li>exclusive advanced modules,</li>\r\n<li>automated updates,</li>\r\n<li>free migration of your existing Hesk tickets and settings,</li>\r\n<li>we take care of maintenance, server setup and optimization, backups, and more!</li>\r\n</ul>\r\n\r\n<p>&nbsp;<br><a href=\"https://www.hesk.com/get/hesk3-cloud\" class=\"btn btn--blue-border\" style=\"text-decoration:none\">Click here to learn more about Hesk Cloud</a></p>\r\n\r\n<p>&nbsp;</p>\r\n\r\n<p>Best regards,</p>\r\n\r\n<p>Klemen Stirn<br>\r\nFounder<br>\r\n<a href=\"https://www.hesk.com\">https://www.hesk.com</a></p>\r\n\r\n<p>&nbsp;</p>\r\n\r\n</div><p>";
            break;
        case 10000:
            $subject = "Wow, you've reached 10,000 tickets!";
            $message = "</p><div style=\"text-align:justify; padding-left: 10px; padding-right: 10px;\">\r\n\r\n<h2 style=\"padding-left:0px\">You received 10,000 support tickets, outstanding! You are officially a Hesk Hero!</h2>\r\n\r\n<h3>&raquo; Rate us</h3>\r\n\r\n<p>Positive ratings and reviews motivate us to continue developing Hesk. Please take a moment to:</p>\r\n\r\n<ul>\r\n<li>rate or review Hesk at <a href=\"https://softaculous.com/rate/HESK\" rel=\"nofollow\">Softaculous</a></li>\r\n<li>rate or review Hesk at <a href=\"https://alternativeto.net/software/hesk/about/\" rel=\"nofollow\">AlternativeTo</a></li>\r\n</ul>\r\n\r\n<h3>&raquo; Stay updated</h3>\r\n\r\n<p>Hesk regularly receives improvements and bug fixes; make sure you know about them!</p>\r\n<ul>\r\n<li>for fast notifications, <a href=\"https://x.com/HESKdotCOM\" rel=\"nofollow\">follow Hesk on <b>X</b></a></li>\r\n<li>for email notifications, subscribe to our low-volume, zero-spam <a href=\"https://www.hesk.com/newsletter.php\">newsletter</a></li>\r\n</ul>\r\n\r\n<h3>&raquo; Look professional</h3>\r\n\r\n<p><a href=\"https://www.hesk.com/get/hesk3-license\">Remove &quot;Powered by&quot; links</a> to support Hesk development and make it look more professional.</p>\r\n\r\n&nbsp;\r\n\r\n<h3>&raquo; Upgrade to Hesk Cloud for the ultimate experience</h3>\r\n\r\n<p>Experience the best of Hesk by moving your help desk into the Hesk Cloud:</p>\r\n<ul>\r\n<li>exclusive advanced modules,</li>\r\n<li>automated updates,</li>\r\n<li>free migration of your existing Hesk tickets and settings,</li>\r\n<li>we take care of maintenance, server setup and optimization, backups, and more!</li>\r\n</ul>\r\n\r\n<p>&nbsp;<br><a href=\"https://www.hesk.com/get/hesk3-cloud\" class=\"btn btn--blue-border\" style=\"text-decoration:none\">Click here to learn more about Hesk Cloud</a></p>\r\n\r\n<p>&nbsp;</p>\r\n\r\n<p>Best regards,</p>\r\n\r\n<p>Klemen Stirn<br>\r\nFounder<br>\r\n<a href=\"https://www.hesk.com\">https://www.hesk.com</a></p>\r\n\r\n<p>&nbsp;</p>\r\n\r\n</div><p>";
            break;
        default:
            return false;
    }

    // Insert private message for main admin
    hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."mail` (`id`, `from`, `to`, `subject`, `message`, `dt`, `read`, `deletedby`) VALUES (NULL, 9999, 1, '".hesk_dbEscape($subject)."', '{$message}', NOW(), '0', 9999)");
    $pm_id = hesk_dbInsertID();

    // Notify admin
    $res = hesk_dbQuery("SELECT `name`,`email` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."users` WHERE `id`=1");
    $row = hesk_dbFetchAssoc($res);

    $pm = array(
        'name'    => 'HESK.com',
        'subject' => $subject,
        'message' => 'Please log in to see the message',
        'id'      => $pm_id,
    );

    // Format email subject and message for recipient
    $subject = hesk_getEmailSubject('new_pm',$pm,0);
    list($message, $html_message) = hesk_getEmailMessage('new_pm',$pm,1,0);

    // Send e-mail
    hesk_mail($row['email'], [], $subject, $message, $html_message);

    return true;

} // END hesk_PMtoMainAdmin()


function hesk_get_muted_emails() {
    global $hesk_settings;

    $muted = array(
        'emails' => array(),
        'domains' => array(),
    );

    $res = hesk_dbQuery("SELECT `email`,`type` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."muted_emails`");

    while ($row=hesk_dbFetchAssoc($res)) {
        if ($row['type']) {
            $muted['emails'][] = $row['email'];
        } else {
            $muted['domains'][] = $row['email'];
        }
    }

    return $muted;
} // END hesk_get_muted_emails()

export_functions.inc.php
wget 'https://lists2.roe3.org/hesk/inc/export_functions.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

/*** FUNCTIONS ***/

function hesk_export_to_XML($sql, $export_selected = false, $export_history = false, $export_replies = false)
{
    global $hesk_settings, $hesklang, $ticket, $my_cat;

	// We'll need HH:MM:SS format for hesk_date() here
	$hesk_settings['format_timestamp'] = 'H:i:s';

	// Get staff names
	$admins = array();
	$result = hesk_dbQuery("SELECT `id`,`name` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."users` ORDER BY `name` ASC");
	while ($row=hesk_dbFetchAssoc($result))
	{
		$admins[$row['id']]=hesk_msgToPlain($row['name'], 1, 0);
	}

    // Get category names
    if ( ! isset($my_cat))
    {
        $my_cat = array();
        $res2 = hesk_dbQuery("SELECT `id`, `name` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` WHERE " . hesk_myCategories('id') . " ORDER BY `cat_order` ASC");
        while ($row=hesk_dbFetchAssoc($res2))
        {
            $my_cat[$row['id']] = hesk_msgToPlain($row['name'], 1, 0);
        }
    }

	// This will be the export directory
	$export_dir = HESK_PATH.$hesk_settings['cache_dir'].'/';

	// This will be the name of the export and the XML file
    $export_name = 'hesk_export_'.date('Y-m-d_H-i-s').'_'.mt_rand(100000,999999);
    $save_to = $export_dir . $export_name . '.xml';

	// Do we have the export directory?
	if ( is_dir($export_dir) || ( @mkdir($export_dir, 0777) && is_writable($export_dir) ) )
    {
        // Is there an index.htm file?
        if ( ! file_exists($export_dir.'index.htm'))
        {
            @file_put_contents($export_dir.'index.htm', '');
        }

		// Cleanup old files
		hesk_purge_cache('export', 86400);
    }
    else
    {
    	hesk_error($hesklang['ede']);
    }

	// Make sure the file can be saved and written to
	@file_put_contents($save_to, '');
	if ( ! file_exists($save_to) )
	{
		hesk_error($hesklang['eef']);
	}

	// Start generating the report message and generating the export
	$success_msg = '';
	$flush_me = '<br /><br />';
	$flush_me .= hesk_date() . " | {$hesklang['inite']} ";

    // Is this export of a date or date range?
    if ($export_selected === false)
    {
        global $date_from, $date_to;

        if ($date_from == $date_to)
        {
            $flush_me .= "(" . hesk_date($date_from, true, true, true, $hesk_settings['format_date']) . ")";
        }
        else
        {
            $flush_me .= "(" . hesk_date($date_from, true, true, true, $hesk_settings['format_date']) . " - " . hesk_date($date_to, true, true, true, $hesk_settings['format_date']) . ")";
        }
    }

    $flush_me .= "<br />\n";

	// Start generating file contents
    $tmp = '<?xml version="1.0" encoding="UTF-8"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
 xmlns:o="urn:schemas-microsoft-com:office:office"
 xmlns:x="urn:schemas-microsoft-com:office:excel"
 xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
 xmlns:html="http://www.w3.org/TR/REC-html40">
 <OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office">
  <AllowPNG/>
 </OfficeDocumentSettings>
 <ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
  <WindowHeight>8250</WindowHeight>
  <WindowWidth>16275</WindowWidth>
  <WindowTopX>360</WindowTopX>
  <WindowTopY>90</WindowTopY>
  <ProtectStructure>False</ProtectStructure>
  <ProtectWindows>False</ProtectWindows>
 </ExcelWorkbook>
 <Styles>
  <Style ss:ID="Default" ss:Name="Normal">
   <Alignment ss:Vertical="Bottom"/>
   <Borders/>
   <Font ss:FontName="Calibri" x:CharSet="238" x:Family="Swiss" ss:Size="11"
    ss:Color="#000000"/>
   <Interior/>
   <NumberFormat/>
   <Protection/>
  </Style>
  <Style ss:ID="s62">
   <NumberFormat ss:Format="General Date"/>
  </Style>
  <Style ss:ID="s63">
   <NumberFormat ss:Format="Short Date"/>
  </Style>
  <Style ss:ID="s65">
   <NumberFormat ss:Format="[h]:mm:ss"/>
  </Style>
 </Styles>
 <Worksheet ss:Name="Sheet1">
  <Table>
';

	// Define column width
	$tmp .= '
	<Column ss:AutoFitWidth="0" ss:Width="50"/>
	<Column ss:AutoFitWidth="0" ss:Width="84"/>
	<Column ss:AutoFitWidth="0" ss:Width="100"/>
	<Column ss:AutoFitWidth="0" ss:Width="100"/>
	<Column ss:AutoFitWidth="0" ss:Width="100"/>
	<Column ss:AutoFitWidth="0" ss:Width="100"/>
	<Column ss:AutoFitWidth="0" ss:Width="90"/>
	<Column ss:AutoFitWidth="0" ss:Width="90"/>
	<Column ss:AutoFitWidth="0" ss:Width="90"/>
	<Column ss:AutoFitWidth="0" ss:Width="87"/>
	<Column ss:AutoFitWidth="0" ss:Width="57.75"/>
	<Column ss:AutoFitWidth="0" ss:Width="57.75"/>
	<Column ss:AutoFitWidth="0" ss:Width="100"/>
	<Column ss:AutoFitWidth="0" ss:Width="100"/>
	<Column ss:AutoFitWidth="0" ss:Width="80"/>
	<Column ss:AutoFitWidth="0" ss:Width="50"/>
	<Column ss:AutoFitWidth="0" ss:Width="50"/>
	<Column ss:AutoFitWidth="0" ss:Width="70"/>
	<Column ss:AutoFitWidth="0" ss:Width="70"/>
	';

	foreach ($hesk_settings['custom_fields'] as $k=>$v)
	{
		if ($v['use'])
		{
			$tmp .= '<Column ss:AutoFitWidth="0" ss:Width="80"/>' . "\n";
		}
	}

    if ($export_history) {
        $tmp .= '<Column ss:AutoFitWidth="0" ss:Width="100"/>' . "\n";
    }

    if ($export_replies) {
        $tmp .= '<Column ss:AutoFitWidth="0" ss:Width="100"/>' . "\n";
    }


	// Define first row (header)
	$tmp .= '
	<Row>
	<Cell><Data ss:Type="String">#</Data></Cell>
	<Cell><Data ss:Type="String">'.$hesklang['trackID'].'</Data></Cell>
	<Cell><Data ss:Type="String">'.$hesklang['date'].'</Data></Cell>
    <Cell><Data ss:Type="String">'.$hesklang['last_update'].'</Data></Cell>
    <Cell><Data ss:Type="String">'.$hesklang['first_reply_at'].'</Data></Cell>
    <Cell><Data ss:Type="String">'.$hesklang['resolved_at'].'</Data></Cell>
	<Cell><Data ss:Type="String">'.$hesklang['name'].'</Data></Cell>
	<Cell><Data ss:Type="String">'.$hesklang['email'].'</Data></Cell>
	<Cell><Data ss:Type="String">'.$hesklang['followers'].'</Data></Cell>
	<Cell><Data ss:Type="String">'.$hesklang['category'].'</Data></Cell>
	<Cell><Data ss:Type="String">'.$hesklang['priority'].'</Data></Cell>
	<Cell><Data ss:Type="String">'.$hesklang['status'].'</Data></Cell>
	<Cell><Data ss:Type="String">'.$hesklang['subject'].'</Data></Cell>
	<Cell><Data ss:Type="String">'.$hesklang['message'].'</Data></Cell>
	<Cell><Data ss:Type="String">'.$hesklang['owner'].'</Data></Cell>
	<Cell><Data ss:Type="String">'.$hesklang['replies'].'</Data></Cell>
	<Cell><Data ss:Type="String">'.$hesklang['replies'] . ' (' . $hesklang['staff'] .')'.'</Data></Cell>
	<Cell><Data ss:Type="String">'.$hesklang['ts'].'</Data></Cell>
	<Cell><Data ss:Type="String">'.$hesklang['due_date'].'</Data></Cell>
	';

	foreach ($hesk_settings['custom_fields'] as $k=>$v)
	{
		if ($v['use'])
		{
			$tmp .= '<Cell><Data ss:Type="String">'.$v['name'].'</Data></Cell>' . "\n";
		}
	}

    if ($export_history) {
        $tmp .= '<Cell><Data ss:Type="String">'.$hesklang['thist'].'</Data></Cell>' . "\n";
    }

    if ($export_replies) {
        $tmp .= '<Cell><Data ss:Type="String">'.$hesklang['reply_messages'].'</Data></Cell>' . "\n";
    }

    $tmp .= '<Cell><Data ss:Type="String">'.$hesklang['ticket_url'].'</Data></Cell>' . "\n";
	$tmp .= "</Row>\n";

	// Write what we have by now into the XML file
	file_put_contents($save_to, $tmp, FILE_APPEND);
	$flush_me .= hesk_date() . " | {$hesklang['gXML']}<br />\n";

	// OK, now start dumping data and writing it into the file
    $tickets_exported = 0;
	$save_after = 100;
    $this_round = 0;
    $tmp = '';

    $result = hesk_dbQuery($sql);
	while ($ticket=hesk_dbFetchAssoc($result))
	{
        $ticket['status'] = hesk_get_status_name($ticket['status']);
        $ticket['priority'] = hesk_get_priority_name($ticket['priority']);
		$ticket['archive'] = !($ticket['archive']) ? $hesklang['no'] : $hesklang['yes'];
		$ticket['message'] = hesk_msgToPlain($ticket['message'], 1, 0);
		$ticket['subject'] = hesk_msgToPlain($ticket['subject'], 1, 0);
        $ticket['owner'] = isset($admins[$ticket['owner']]) ? $admins[$ticket['owner']] : '';
		$ticket['category'] = isset($my_cat[$ticket['category']]) ? $my_cat[$ticket['category']] : '';

        if (!function_exists('hesk_get_customers_for_ticket')) {
            require_once(HESK_PATH . 'inc/customer_accounts.inc.php');
        }
        $customers = hesk_get_customers_for_ticket($ticket['id']);
        if (defined('HESK_DEMO')) {
            array_walk($customers, function(&$k) {
                $k['email'] = 'hidden@demo.com';
            });
        }
        $found_requester = false;
        $requester = [];
        $followers = [];
        foreach ($customers as $customer) {
            if ($customer['customer_type'] === 'REQUESTER') {
                $found_requester = true;
                $requester = $customer;
            } elseif ($customer['customer_type'] === 'FOLLOWER') {
                $followers[] = $customer;
            }
        }
        if (!$found_requester) {
            // Ticket has been anonymized
            $requester['name'] = $hesklang['anon_name'];
            $requester['email'] = $hesklang['anon_email'];
        }
        $follower_names = array_map(function($follower) { return format_display_name($follower); }, $followers);

		// Format for export dates
		$hesk_settings['format_timestamp'] = "Y-m-d\TH:i:s\.000";

		// Create row for the XML file
		$tmp .= '
<Row>
<Cell><Data ss:Type="Number">'.$ticket['id'].'</Data></Cell>
<Cell><Data ss:Type="String"><![CDATA['.hesk_escape_CDATA($ticket['trackid']).']]></Data></Cell>
<Cell ss:StyleID="s62"><Data ss:Type="DateTime">'.hesk_date($ticket['dt'], true).'</Data></Cell>
<Cell ss:StyleID="s62"><Data ss:Type="DateTime">'.hesk_date($ticket['lastchange'], true).'</Data></Cell>
';

        if (empty($ticket['firstreply'])) {
            $tmp .= '<Cell><Data ss:Type="String"></Data></Cell>' . "\n";
        } else {
            $tmp .= '<Cell ss:StyleID="s62"><Data ss:Type="DateTime">'.hesk_date($ticket['firstreply'], true).'</Data></Cell>' . "\n";
        }

        if (empty($ticket['closedat'])) {
            $tmp .= '<Cell><Data ss:Type="String"></Data></Cell>'."\n";
        } else {
            $tmp .= '<Cell ss:StyleID="s62"><Data ss:Type="DateTime">'.hesk_date($ticket['closedat'], true).'</Data></Cell>'."\n";
        }

        $tmp .= '<Cell><Data ss:Type="String"><![CDATA['.hesk_escape_CDATA(hesk_msgToPlain($requester['name'], 1, 0)).']]></Data></Cell>
<Cell><Data ss:Type="String"><![CDATA['.hesk_escape_CDATA($requester['email']).']]></Data></Cell>
<Cell><Data ss:Type="String"><![CDATA['.hesk_escape_CDATA(implode(',', $follower_names)).']]></Data></Cell>
<Cell><Data ss:Type="String"><![CDATA['.hesk_escape_CDATA($ticket['category']).']]></Data></Cell>
<Cell><Data ss:Type="String"><![CDATA['.hesk_escape_CDATA($ticket['priority']).']]></Data></Cell>
<Cell><Data ss:Type="String"><![CDATA['.hesk_escape_CDATA($ticket['status']).']]></Data></Cell>
<Cell><Data ss:Type="String"><![CDATA['.hesk_escape_CDATA($ticket['subject']).']]></Data></Cell>
<Cell><Data ss:Type="String"><![CDATA['.hesk_escape_CDATA($ticket['message']).']]></Data></Cell>
<Cell><Data ss:Type="String"><![CDATA['.hesk_escape_CDATA($ticket['owner']).']]></Data></Cell>
<Cell><Data ss:Type="Number">'.$ticket['replies'].'</Data></Cell>
<Cell><Data ss:Type="Number">'.$ticket['staffreplies'].'</Data></Cell>
<Cell><Data ss:Type="String"><![CDATA['.hesk_escape_CDATA($ticket['time_worked']).']]></Data></Cell>
';

        // Due date
        if (empty($ticket['due_date']))
        {
            $tmp .= '<Cell><Data ss:Type="String"></Data></Cell>'."\n";
        }
        else
        {
            $tmp .= '<Cell ss:StyleID="s63"><Data ss:Type="DateTime">'.hesk_date($ticket['due_date'], true).'</Data></Cell>'."\n";
        }

		// Add custom fields
		foreach ($hesk_settings['custom_fields'] as $k=>$v)
		{
			if ($v['use'])
			{
	        	switch ($v['type'])
	            {
	            	case 'date':
                    	$tmp_dt = hesk_custom_date_display_format($ticket[$k], 'Y-m-d\T00:00:00.000');
	                	$tmp .= strlen($tmp_dt) ? '<Cell ss:StyleID="s63"><Data ss:Type="DateTime">'.$tmp_dt : '<Cell><Data ss:Type="String">';
                        $tmp .= "</Data></Cell> \n";
	                    break;
	                default:
						$tmp .= '<Cell><Data ss:Type="String"><![CDATA['.hesk_escape_CDATA(hesk_msgToPlain($ticket[$k], 1, 0)).']]></Data></Cell>  ' . "\n";
	            }
			}
		}

        if ($export_history) {
            $tmp .= '<Cell><Data ss:Type="String"><![CDATA['.hesk_escape_CDATA($ticket['history']).']]></Data></Cell>' . "\n";
        }

        if ($export_replies) {

            $tmp .= '<Cell><Data ss:Type="String"><![CDATA[';

            if ($ticket['replies']) {
                $replies = hesk_dbQuery("SELECT `replies`.*, `customers`.`name` AS `customer_name`, `customers`.`email` AS `customer_email`, `users`.`name` AS `staff_name`
                    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') );

                while ($reply = hesk_dbFetchAssoc($replies)) {
                    if ($reply['staffid']) {
                        $reply['name'] = $reply['staff_name'] === null ?
                            $hesklang['staff_deleted'] :
                            $reply['staff_name'];
                    } else {
                        if ($reply['customer_name'] === null || $reply['customer_name'] == '') {
                            if ($reply['customer_email'] !== null && strlen($reply['customer_email'])) {
                                $reply['name'] = $reply['customer_email'];
                            } else {
                                $reply['name'] = $hesklang['anon_name'];
                            }
                        } else {
                            $reply['name'] = $reply['customer_name'];
                        }
                    }

                    $reply['message'] = hesk_msgToPlain($reply['message'], 1, 0);

                    $tmp .= hesk_escape_CDATA(
                        $hesklang['reply_by'] . ' ' . $reply['name'] . "\n" .
                        $hesklang['date'] . ' ' . hesk_date($reply['dt'], true) . "\n" .
                        $reply['message'] . "\n---\n"
                    );
                }
            }

            $tmp .= ']]></Data></Cell>'."\n";
        }

        // Include a link to ticket
        if ($hesk_settings['email_view_ticket'] && isset($requester['email'])) {
            $tmp .= '<Cell><Data ss:Type="String">'.$hesk_settings['hesk_url'].'/ticket.php?track='.urlencode($ticket['trackid']).'&amp;e='.urlencode($requester['email']).'</Data></Cell>';
        } else {
            $tmp .= '<Cell><Data ss:Type="String">'.$hesk_settings['hesk_url'].'/ticket.php?track='.urlencode($ticket['trackid']).'</Data></Cell>';
        }
		$tmp .= "</Row>\n";

		// Write every 100 rows into the file
		if ($this_round >= $save_after)
		{
			file_put_contents($save_to, $tmp, FILE_APPEND);
			$this_round = 0;
			$tmp = '';
			usleep(1);
		}

        $tickets_exported++;
        $this_round++;
	} // End of while loop

	// Go back to the HH:MM:SS format for hesk_date()
	$hesk_settings['format_timestamp'] = 'H:i:s';

	// Append any remaining rows into the file
	if ($this_round > 0)
	{
		file_put_contents($save_to, $tmp, FILE_APPEND);
	}

	// If any tickets were exported, continue, otherwise cleanup
	if ($tickets_exported > 0)
	{
		// Finish the XML file
	    $tmp = '
  </Table>
  <WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
   <PageSetup>
    <Header x:Margin="0.3"/>
    <Footer x:Margin="0.3"/>
    <PageMargins x:Bottom="0.75" x:Left="0.7" x:Right="0.7" x:Top="0.75"/>
   </PageSetup>
   <Selected/>
   <Panes>
    <Pane>
     <Number>3</Number>
     <ActiveRow>4</ActiveRow>
    </Pane>
   </Panes>
   <ProtectObjects>False</ProtectObjects>
   <ProtectScenarios>False</ProtectScenarios>
  </WorksheetOptions>
 </Worksheet>
 <Worksheet ss:Name="Sheet2">
  <Table ss:ExpandedColumnCount="1" ss:ExpandedRowCount="1" x:FullColumns="1"
   x:FullRows="1" ss:DefaultRowHeight="15">
  </Table>
  <WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
   <PageSetup>
    <Header x:Margin="0.3"/>
    <Footer x:Margin="0.3"/>
    <PageMargins x:Bottom="0.75" x:Left="0.7" x:Right="0.7" x:Top="0.75"/>
   </PageSetup>
   <ProtectObjects>False</ProtectObjects>
   <ProtectScenarios>False</ProtectScenarios>
  </WorksheetOptions>
 </Worksheet>
 <Worksheet ss:Name="Sheet3">
  <Table ss:ExpandedColumnCount="1" ss:ExpandedRowCount="1" x:FullColumns="1"
   x:FullRows="1" ss:DefaultRowHeight="15">
  </Table>
  <WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
   <PageSetup>
    <Header x:Margin="0.3"/>
    <Footer x:Margin="0.3"/>
    <PageMargins x:Bottom="0.75" x:Left="0.7" x:Right="0.7" x:Top="0.75"/>
   </PageSetup>
   <ProtectObjects>False</ProtectObjects>
   <ProtectScenarios>False</ProtectScenarios>
  </WorksheetOptions>
 </Worksheet>
</Workbook>
';
		file_put_contents($save_to, $tmp, FILE_APPEND);

		// Log how many rows we exported
		$flush_me .= hesk_date() . " | " . sprintf($hesklang['nrow'], $tickets_exported) . "<br />\n";

		// We will convert XML to Zip to save a lot of space
		$save_to_zip = $export_dir.$export_name.'.zip';

		// Log start of Zip creation
		$flush_me .= hesk_date() . " | {$hesklang['cZIP']}<br />\n";

		// Preferrably use the zip extension
	    if (extension_loaded('zip'))
	    {
		    $save_to_zip = $export_dir.$export_name.'.zip';

			$zip = new ZipArchive;
			$res = $zip->open($save_to_zip, ZipArchive::CREATE);
			if ($res === TRUE)
			{
				$zip->addFile($save_to, "{$export_name}.xml");
				$zip->close();
			}
			else
			{
				die("{$hesklang['eZIP']} <$save_to_zip>\n");
			}

	    }
		// Some servers have ZipArchive class enabled anyway - can we use it?
		elseif ( class_exists('ZipArchive') )
		{
			require(HESK_PATH . 'inc/zip/Zip.php');
			$zip = new Zip();
			$zip->addLargeFile($save_to, "{$export_name}.xml");
			$zip->finalize();
			$zip->setZipFile($save_to_zip);
		}
		// If not available, use a 3rd party Zip class included with HESK
		else
		{
			require(HESK_PATH . 'inc/zip/pclzip.lib.php');
			$zip = new PclZip($save_to_zip);
			$zip->add($save_to, PCLZIP_OPT_REMOVE_ALL_PATH);
		}

		// Delete XML, just leave the Zip archive
		hesk_unlink($save_to);

		// Echo memory peak usage
		$flush_me .= hesk_date() . " | " . sprintf($hesklang['pmem'], (@memory_get_peak_usage(true) / 1048576)) . "<br />\r\n";

		// We're done!
		$flush_me .= hesk_date() . " | {$hesklang['fZIP']}<br /><br />";

        // Success message
        $referer = isset($_SERVER['HTTP_REFERER']) ? hesk_input($_SERVER['HTTP_REFERER']) : 'export.php';
        $referer = str_replace('&amp;','&',$referer);
        if (strpos($referer, 'export.php'))
        {
            $referer = 'export.php';
        }

        $success_msg .= $hesk_settings['debug_mode'] ? $flush_me : '<br /><br />';
        $success_msg .= $hesklang['step1'] . ': <a href="' . $save_to_zip . '">' . $hesklang['ch2d'] . '</a><br /><br />' . $hesklang['step2'] . ': <a href="export.php?delete='.urlencode($export_name).'&amp;goto='.urlencode($referer).'&amp;token='.hesk_token_echo(0).'">' . $hesklang['dffs'] . '</a>';
	}
    // No tickets exported, cleanup
    else
    {
		hesk_unlink($save_to);
    }

    return array($success_msg, $tickets_exported);

} // END hesk_export_to_XML()


function hesk_escape_CDATA($in)
{
    return str_replace(']]>', ']]]]><![CDATA[>', $in);
} // END hesk_escape_CDATA()

function format_display_name($row) {
    if ($row['name']) {
        return $row['email'] ? "{$row['name']} <{$row['email']}>" : $row['name'];
    }

    return $row['email'];
}
favicon.inc.php
wget 'https://lists2.roe3.org/hesk/inc/favicon.inc.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
 *
 */

// Check if this is a valid include
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}
?>

<link rel="icon" href="<?php echo HESK_PATH; ?>img/favicon.ico" />
footer.inc.php
wget 'https://lists2.roe3.org/hesk/inc/footer.inc.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
 *
 */

// Check if this is a valid include
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

// Users online
if (defined('SHOW_ONLINE'))
{
    hesk_printOnline();
}

/*******************************************************************************
The code below handles HESK licensing and must be included in the template.

Removing this code is a direct violation of the HESK End User License Agreement,
will void all support and may result in unexpected behavior.

To purchase a HESK license and support future HESK development please visit:
https://www.hesk.com/buy.php
*******************************************************************************/
$hesk_settings['hesk_license']('GZvb3RlciBjbGFzcz0iZm9vdGVyIiBzdHlsZT0iZGlzcGxhe
TpibG9jayAhaW1wb3J0YW50OyI+PHAgY2xhc3M9InRleHQtY2VudGVyIiBzdHlsZT0iZGlzcGxheTpib
G9jayAhaW1wb3J0YW50OyI+UG93ZXJlZCBieSA8YSBocmVmPSJodHRwczovL3d3dy5oZXNrLmNvbSIgY
2xhc3M9ImxpbmsiIHN0eWxlPSJkaXNwbGF5OmlubGluZSAhaW1wb3J0YW50OyI+SGVscCBEZXNrIFNvZ
nR3YXJlPC9hPiA8c3BhbiBjbGFzcz0iZm9udC13ZWlnaHQtYm9sZCIgc3R5bGU9ImRpc3BsYXk6aW5sa
W5lICFpbXBvcnRhbnQ7Ij5IRVNLPC9zcGFuPjxicj4mbmJzcDs8L3A+PC9mb290ZXI+',"\120",
"2c6d33b92c7068eed222cfc904a0e6019a579a8d");
/*******************************************************************************
END LICENSE CODE
*******************************************************************************/
?>

<?php if ($hesk_settings['extend_admin'] && isset($_SESSION['id']) && ! isset($_SESSION['HESK_USER'])): ?>
    <iframe src="<?php echo HESK_PATH . $hesk_settings['admin_dir'] . '/admin_extend_session.php'; ?>" height="10" width="10" style="border:none;"></iframe>
<?php endif; ?>

</main> <!-- End main -->
<?php
if (isset($login_wrapper)) {
    echo '</div> <!-- End wrapper login -->';
}
?>
</div> <!-- End wrapper -->
<input type="hidden" name="HESK_PATH" value="<?php echo HESK_PATH; ?>">
<script src="<?php echo HESK_PATH; ?>js/svg4everybody.min.js"></script>
<?php
// Do we need the calendar functions?
if (defined('CALENDAR'))
{
?>
<script src="<?php echo HESK_PATH; ?>js/datepicker.min.js"></script>
<script type="text/javascript">
(function ($) { $.fn.datepicker.language['en'] = {
    days: ['<?php echo $hesklang['d0']; ?>', '<?php echo $hesklang['d1']; ?>', '<?php echo $hesklang['d2']; ?>', '<?php echo $hesklang['d3']; ?>', '<?php echo $hesklang['d4']; ?>', '<?php echo $hesklang['d5']; ?>', '<?php echo $hesklang['d6']; ?>'],
    daysShort: ['<?php echo $hesklang['sun']; ?>', '<?php echo $hesklang['mon']; ?>', '<?php echo $hesklang['tue']; ?>', '<?php echo $hesklang['wed']; ?>', '<?php echo $hesklang['thu']; ?>', '<?php echo $hesklang['fri']; ?>', '<?php echo $hesklang['sat']; ?>'],
    daysMin: ['<?php echo $hesklang['su']; ?>', '<?php echo $hesklang['mo']; ?>', '<?php echo $hesklang['tu']; ?>', '<?php echo $hesklang['we']; ?>', '<?php echo $hesklang['th']; ?>', '<?php echo $hesklang['fr']; ?>', '<?php echo $hesklang['sa']; ?>'],
    months: ['<?php echo $hesklang['m1']; ?>','<?php echo $hesklang['m2']; ?>','<?php echo $hesklang['m3']; ?>','<?php echo $hesklang['m4']; ?>','<?php echo $hesklang['m5']; ?>','<?php echo $hesklang['m6']; ?>', '<?php echo $hesklang['m7']; ?>','<?php echo $hesklang['m8']; ?>','<?php echo $hesklang['m9']; ?>','<?php echo $hesklang['m10']; ?>','<?php echo $hesklang['m11']; ?>','<?php echo $hesklang['m12']; ?>'],
    monthsShort: ['<?php echo $hesklang['ms01']; ?>','<?php echo $hesklang['ms02']; ?>','<?php echo $hesklang['ms03']; ?>','<?php echo $hesklang['ms04']; ?>','<?php echo $hesklang['ms05']; ?>','<?php echo $hesklang['ms06']; ?>', '<?php echo $hesklang['ms07']; ?>','<?php echo $hesklang['ms08']; ?>','<?php echo $hesklang['ms09']; ?>','<?php echo $hesklang['ms10']; ?>','<?php echo $hesklang['ms11']; ?>','<?php echo $hesklang['ms12']; ?>'],
    today: '<?php echo hesk_slashJS($hesklang['r1']); ?>',
    clear: '<?php echo hesk_slashJS($hesklang['clear']); ?>',
    dateFormat: '<?php echo hesk_slashJS($hesk_settings['format_datepicker_js']); ?>',
    timeFormat: '<?php echo hesk_slashJS($hesk_settings['format_time']); ?>',
    firstDay: <?php echo $hesklang['first_day_of_week']; ?>
}; })(jQuery);
</script>
<?php
}
?>

<script type="text/javascript" src="<?php echo HESK_PATH; ?>js/app<?php echo $hesk_settings['debug_mode'] ? '' : '.min'; ?>.js?<?php echo $hesk_settings['hesk_version']; ?>"></script>

<?php
// Any adjustments to datepicker?
if (isset($hesk_settings['datepicker'])):
    ?>
    <script>

    function convertDateToUTC(date) { return new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds()); }

    $(document).ready(function () {
        const myDP = {};
        <?php
        foreach ($hesk_settings['datepicker'] as $selector => $data) {
            echo "
                myDP['{$selector}'] = $('{$selector}').datepicker(".((isset($data['position']) && is_string($data['position'])) ? "{position: '{$data['position']}'}" : "").");
            ";
            if (isset($data['timestamp']) && ($ts = intval($data['timestamp']))) {
                if ( ! empty($hesk_settings['datepicker'][$selector]['fromDB'])) {
                    echo "myDP['{$selector}'].data('datepicker').selectDate(convertDateToUTC(new Date({$ts} * 1000)));";
                } else {
                    echo "myDP['{$selector}'].data('datepicker').selectDate(new Date({$ts} * 1000));";
                }
            }
        }
        ?>
        $('.showme').click(function (e) {
            $(this).addClass('active');
            $(this).parent()
                .find('.datepicker')
                .data("datepicker")
                .show();
        });
    });
    </script>
    <?php
endif;

// Auto-select first empty or error field on non-staff pages?
if (defined('AUTOFOCUS'))
{
?>
<script language="javascript">
(function(){
	var forms = document.forms || [];
	for(var i = 0; i < forms.length; i++)
    {
		for(var j = 0; j < forms[i].length; j++)
        {
			if(
				!forms[i][j].readonly != undefined &&
				forms[i][j].type != "hidden" &&
				forms[i][j].disabled != true &&
				forms[i][j].style.display != 'none' &&
				(forms[i][j].className == 'isError' || forms[i][j].className == 'isNotice' || forms[i][j].value == '')
			)
	        {
				forms[i][j].focus();
				return;
			}
		}
	}
})();
</script>
<?php
}
// Do we need the calendar functions?
if (defined('RRULE'))
{
?>
<script src="<?php echo HESK_PATH; ?>js/rrule_es5.js"></script>
<?php
}

// Apply status coloring to drop-down box; needs to be called after app.js
if (isset($hesk_settings['print_status_select_box_jquery']))
{
    hesk_print_status_select_box_jquery();
}

echo '
</body>
</html>
';

$hesk_settings['security_cleanup']('exit');
header.inc.php
wget 'https://lists2.roe3.org/hesk/inc/header.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

// We'll use this later
$onload='';

define('TEMPLATE_PATH', HESK_PATH . "theme/{$hesk_settings['site_theme']}/");

$assetVersion = $hesk_settings['hesk_version'];
if ($hesk_settings['debug_mode']) {
    // Note: For some reason, even with browser force cache refresh it's not always refreshing, so this is a way to force it additionally in debug mode if necessary.
    $assetVersion .='_' . time();
}
?>
<!DOCTYPE html>
<html lang="<?php echo $hesk_settings['languages'][$hesk_settings['language']]['folder'] ?>">
<head>
	<title><?php echo (isset($hesk_settings['tmp_title']) ? $hesk_settings['tmp_title'] : $hesk_settings['hesk_title']); ?></title>
	<meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=Edge">
    <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0">
    <?php include(HESK_PATH . 'inc/favicon.inc.php'); ?>
    <meta name="format-detection" content="telephone=no">

    <?php
    // Do we need to load JS/CSS for attachments? Needs to go before our app.css
    if (defined('ATTACHMENTS')) {
        ?>
        <link rel="stylesheet" href="<?php echo HESK_PATH; ?>css/dropzone.min.css?<?php echo $assetVersion; ?>" type="text/css" />
        <script src="<?php echo HESK_PATH; ?>js/dropzone.min.js?<?php echo $assetVersion; ?>"></script>
        <?php
    }
    ?>

    <link rel="stylesheet" media="all" href="<?php echo HESK_PATH; ?>css/app<?php echo $hesk_settings['debug_mode'] ? '' : '.min'; ?>.css?<?php echo $assetVersion; ?>">
    <script src="<?php echo HESK_PATH; ?>js/jquery-3.5.1.min.js"></script>

    <?php
    // Do we need to load customer-side theme/variables? (i.e. needed for live changes of theme settings with properly calculated colors)
    if (defined('LOAD_CUSTOMER_THEME_VARS')) {
        ?>
        <link id="default_theme" rel="stylesheet" media="all" href="<?php echo TEMPLATE_PATH; ?>customer/css/core/0_00_default_theme_vars.css?<?php echo $assetVersion; ?>" />
        <?php
        // Check if use has any special CSS themes selected, and if they exist, load their CSS.
        // NOTE! Theme vars overrides have to be loaded in first, as they are then used as part of color calculations in 0_01_variables.css file!
        $attempt_load_theme = isset($hesk_settings['customer_theme']) && $hesk_settings['customer_theme'] !== '';
        if ($attempt_load_theme) {
            $loaded_theme = $hesk_settings['customer_theme'];

            $theme_path = TEMPLATE_PATH.'customer/css/themes/'.$loaded_theme.'.css';
            if (file_exists($theme_path)) { ?>
                <link id="loaded_theme" rel="stylesheet" media="all" href="<?php echo $theme_path; ?>?<?php echo $assetVersion; ?>" />
                <?php
            }
        }
        ?>
        <link rel="stylesheet" media="all" href="<?php echo TEMPLATE_PATH; ?>customer/css/core/0_01_variables.css?<?php echo $assetVersion; ?>" />
    <?php
    }
    ?>

    <?php
    // Do we need to load CSV parsing?
    if (defined('CSV')) {
        ?>
        <script src="<?php echo HESK_PATH; ?>js/jquery.csv.min.js?<?php echo $assetVersion; ?>"></script>
        <?php
    }
    ?>
    <script src="<?php echo HESK_PATH; ?>js/selectize.min.js?<?php echo $assetVersion; ?>"></script>
    <script type="text/javascript" src="<?php echo HESK_PATH; ?>js/hesk_javascript<?php echo $hesk_settings['debug_mode'] ? '' : '.min'; ?>.js?<?php echo $assetVersion; ?>"></script>

    <?php
	/* Tickets shouldn't be indexed by search engines */
	if (defined('HESK_NO_ROBOTS'))
	{
		?>
		<meta name="robots" content="noindex, nofollow" />
		<?php
	}

	/* If page requires WYSIWYG editor include TinyMCE Javascript */
	if (defined('WYSIWYG') && ($hesk_settings['staff_ticket_formatting'] == 2 || $hesk_settings['kb_wysiwyg'] || defined('HTML_EMAIL_TEMPLATE')))
	{
        require(HESK_PATH . 'inc/tiny_mce/tinymce.inc.php');
		?>
		<script type="text/javascript" src="<?php echo HESK_PATH; ?>inc/tiny_mce/7.9.1/tinymce.min.js"></script>
		<?php
	}

    /* If page styles <code> blocks */
    if (defined('STYLE_CODE'))
    {
        ?>
        <script type="text/javascript" src="<?php echo HESK_PATH; ?>js/prism.js?<?php echo $assetVersion; ?>"></script>
        <link rel="stylesheet" media="all" href="<?php echo HESK_PATH; ?>css/prism.css?<?php echo $assetVersion; ?>">
        <?php
    }

	/* If page requires timer load Javascript */
	if (defined('TIMER'))
	{
		?>
		<script type="text/javascript" src="<?php echo HESK_PATH; ?>inc/timer/hesk_timer.js"></script>
		<?php

        /* Need to load default time or a custom one? */
        if ( isset($_SESSION['time_worked']) )
        {
        	$t = hesk_getHHMMSS($_SESSION['time_worked']);
			$onload .= "load_timer('time_worked', " . $t[0] . ", " . $t[1] . ", " . $t[2] . ");";
            unset($t);
        }
        else
        {
        	$onload .= "load_timer('time_worked', 0, 0, 0);";
        }

		/* Autostart timer? */
		if ( ! empty($_SESSION['autostart']) )
		{
			$onload .= "ss();";
		}
	}

	// Use ReCaptcha
	if (defined('RECAPTCHA'))
	{
		echo '<script src="https://www.google.com/recaptcha/api.js?hl='.$hesklang['RECAPTCHA'].'" async defer></script>';
        echo '<script language="Javascript" type="text/javascript">
        function recaptcha_submitForm() {
            document.getElementById("form1").submit();
        }
        </script>';
	}

	// Auto reload
	if (defined('AUTO_RELOAD') && hesk_checkPermission('can_view_tickets',0))
	{
		?>
		<script type="text/javascript">
		var count = <?php echo empty($_SESSION['autoreload']) ? 30 : intval($_SESSION['autoreload']); ?>;
		var reloadcounter;
		var countstart = count;

		function heskReloadTimer()
		{
			count=count-1;
			if (count <= 0)
			{
				clearInterval(reloadcounter);
				window.location.reload();
				return;
			}

			document.getElementById("timer").innerHTML = "(" + count + ")";
		}

		function heskCheckReloading()
		{
			if (<?php if ($_SESSION['autoreload']) echo "getCookie('autorefresh') == null || "; ?>getCookie('autorefresh') == '1')
			{
				document.getElementById("reloadCB").checked=true;
				document.getElementById("timer").innerHTML = "(" + count + ")";
				reloadcounter = setInterval(heskReloadTimer, 1000);
			}
		}

		function toggleAutoRefresh(cb)
		{
			if (cb.checked)
			{
				setCookie('autorefresh', '1');
				document.getElementById("timer").innerHTML = "(" + count + ")";
				reloadcounter = setInterval(heskReloadTimer, 1000);
			}
			else
			{
				setCookie('autorefresh', '0');
				count = countstart;
				clearInterval(reloadcounter);
				document.getElementById("timer").innerHTML = "";
			}
		}

		</script>
		<?php
	}

    // Timeago
    if (defined('TIMEAGO'))
    {
        ?>
        <script type="text/javascript" src="<?php echo HESK_PATH; ?>js/timeago/jquery.timeago.js?<?php echo $assetVersion; ?>"></script>
        <?php
        // Load language file if not English
        if ($hesklang['TIMEAGO_LANG_FILE'] != 'jquery.timeago.en.js')
        {
            ?>
            <script type="text/javascript" src="<?php echo HESK_PATH; ?>js/timeago/locales/<?php echo $hesklang['TIMEAGO_LANG_FILE']; ?>?<?php echo $assetVersion; ?>"></script>
            <?php
        }
        ?>
        <script type="text/javascript">
        jQuery(document).ready(function() {
            $("time.timeago").timeago();
        });
        </script>
        <?php
    }

    // Back to top button
    if (defined('BACK2TOP'))
    {
        ?>
        <script type="text/javascript">
        jQuery(document).ready(function() {
            var offset = 800;
            var duration = 250;
            jQuery(window).scroll(function() {
                if (jQuery(this).scrollTop() > offset) {
                    jQuery('.back-to-top').fadeIn(duration);
                } else {
                    jQuery('.back-to-top').fadeOut(duration);
                }
            });

            jQuery('.back-to-top').click(function(event) {
                event.preventDefault();
                jQuery('html, body').animate({scrollTop: 0}, duration);
                return false;
            })
        });
        </script>
        <?php
    }
	?>

    <script type="text/javascript" src="<?php echo HESK_PATH; ?>js/zebra_tooltips.min.js?<?php echo $assetVersion; ?>"></script>
    <link rel="stylesheet" href="<?php echo HESK_PATH; ?>css/zebra_tooltips.css">
    <script type="text/javascript">
    $(document).ready(function() {
        // show tooltips for any element that has a class named "tooltip"
        // the content of the tooltip will be taken from the element's "title" attribute
        new $.Zebra_Tooltips($('.tooltip'), {animation_offset: 0, animation_speed: 100, hide_delay: 0, show_delay: 0, vertical_alignment: 'above', vertical_offset: 5});
    });
    </script>

    <?php if ($hesk_settings['admin_css']): ?>
    <link rel="stylesheet" href="<?php echo $hesk_settings['admin_css_url']; ?>">
    <?php endif; ?>

    <?php if ($hesk_settings['admin_js']): ?>
    <script type="text/javascript" src="<?php echo $hesk_settings['admin_js_url']; ?>"></script>
    <?php endif; ?>

</head>
<body onload="<?php echo $onload; unset($onload); ?>">
<a href="#maincontent" class="skiplink"><?php echo $hesklang['skip_to_main_content']; ?></a>

<div class="wrapper">
index.htm
wget 'https://lists2.roe3.org/hesk/inc/index.htm'
View Content
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<HTML><HEAD>
<TITLE>403 Forbidden</TITLE>
</HEAD><BODY>
<H1>Forbidden</H1>
You don't have permission to access this folder.<P>
<hr />
</BODY></HTML>
knowledgebase_functions.inc.php
wget 'https://lists2.roe3.org/hesk/inc/knowledgebase_functions.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

hesk_kbCategoriesArray();

/*** FUNCTIONS ***/

function hesk_kbCategoriesArray($public_only = true)
{
    global $hesk_settings, $hesklang;

    $res = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."kb_categories` ORDER BY `cat_order` ASC");

    $categories = array();

    while ($category = hesk_dbFetchAssoc($res))
    {
        $category['children'] = array();
        $category['descendants'] = array();
        $categories[$category['id']] = $category;
    }

    // Get the full parent path for each category
    foreach ($categories as $id => $category)
    {
        $categories[$id]['parents'] = array();

        // Top category? Just translate name.
        if ($category['parent'] == 0)
        {
            $categories[$id]['name'] = $hesklang['kb_text'];
            continue;
        }

        $current_parrent = $category['parent'];
        $categories[$id]['parents'][] = $current_parrent;

        $i = 0;
        while ($current_parrent > 0 && isset($categories[$current_parrent]) && $categories[$current_parrent]['id'] != $categories[$current_parrent]['parent'] && $i < 1000)
        {
            if (($current_parrent = $categories[$current_parrent]['parent']) > 0)
            {
                $categories[$id]['parents'][] = $current_parrent;
            }
            $i++;
        }

        $categories[$id]['parents'] = array_reverse($categories[$id]['parents']);
    }

    // Get children for each category
    foreach ($categories as $id => $category) {
        if ($category['parent'] == 0) {
            continue;
        }

        $categories[$category['parent']]['children'][] = $id;

        foreach ($category['parents'] as $parent) {
            $categories[$parent]['descendants'][] = $id;
        }
    }

    // If a parent is private, make sure all sub-categories are private too
    foreach ($categories as $id => $category) {
        if ($category['type'] == 0) {
            continue;
        }

        foreach ($category['descendants'] as $descendant) {
            if (isset($categories[$descendant])) {
                $categories[$descendant]['type'] = 1;
            }
        }
    }

    // Uncomment if you want to remove direct children from "descendants"
    /*
    foreach ($categories as $id => $category) {
        if (count($category['children']) == 0 || count($category['descendants']) == 0) {
            continue;
        }

        $categories[$id]['descendants'] = array_diff($categories[$id]['descendants'], $categories[$id]['children']);
    }
    */

    $hesk_settings['all_kb_categories'] = $categories;

    // Extract public categories only
    foreach ($categories as $id => $category) {
        if ($category['type'] == 1) {
            unset($categories[$id]);

            // Remove also from children and descendant arrays
            foreach ($categories as $i => $c) {
                if (($key = array_search($id, $categories[$i]['children'])) !== false) {
                    unset($categories[$i]['children'][$key]);
                }
                if (($key = array_search($id, $categories[$i]['descendants'])) !== false) {
                    unset($categories[$i]['descendants'][$key]);
                }
            }
        }
    }
    $hesk_settings['public_kb_categories'] = $categories;
    $hesk_settings['public_kb_categories_ids'] = array_keys($categories);

    return true;
} // END hesk_kbCategoriesArray()


function hesk_kbArticleContentPreview($txt)
{
	global $hesk_settings;

	// Strip HTML tags
	$txt = strip_tags($txt);

	// If text is larger than article preview length, shorten it
	if (hesk_mb_strlen($txt) > $hesk_settings['kb_substrart'])
	{
		// The quick but not 100% accurate way (number of chars displayed may be lower than the limit)
		return hesk_mb_substr($txt, 0, $hesk_settings['kb_substrart']) . '...';

		// If you want a more accurate, but also slower way, use this instead
		// return hesk_htmlentities( hesk_mb_substr( hesk_html_entity_decode($txt), 0, $hesk_settings['kb_substrart'] ) ) . '...';
	}

	return $txt;
} // END hesk_kbArticleContentPreview()


function hesk_kbTopArticles($how_many, $index = 1)
{
	global $hesk_settings, $hesklang;

	$articles = array();
	// Index page or KB main page?
	if ($index)
	{
		// Disabled?
		if (!$hesk_settings['kb_index_popart'])
		{
			return $articles;
		}
	}
	else
	{
		// Disabled?
		if (!$hesk_settings['kb_popart'])
		{
			return $articles;
		}
    }

    // Make sure "public" categories are not nested in a "private" one
    $sql_top = ' AND `t2`.`id` IN ('.implode(',', $hesk_settings['public_kb_categories_ids']).') ';

    /* Get list of articles from the database */
    $res = hesk_dbQuery("SELECT `t1`.`id`,`t1`.`catid`,`t1`.`subject`,`t1`.`views`, `t1`.`content`, `t2`.`name` AS `category`, `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`.`type`='0' AND `t2`.`type`='0' {$sql_top}
                        ORDER BY `t1`.`sticky` DESC, `t1`.`views` DESC, `t1`.`art_order` ASC LIMIT ".intval($how_many));

    $articles = array();

    // Remember what articles are printed for "Top" so we don't print them again in "Latest"
    $hesk_settings['kb_top_articles_printed'] = array();

	while ($article = hesk_dbFetchAssoc($res))
	{
        // Top category? Translate name
        if ($article['catid'] == 1)
        {
            $article['category'] = $hesklang['kb_text'];
        }

        $hesk_settings['kb_top_articles_printed'][] = $article['id'];
        $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;
	}

	return $articles;
} // END hesk_kbTopArticles()


function hesk_kbLatestArticles($how_many, $index = 1)
{
	global $hesk_settings, $hesklang;

	$articles = array();
	// Index page or KB main page?
	if ($index)
	{
		// Disabled?
		if ( ! $hesk_settings['kb_index_latest'])
		{
			return $articles;
		}

		// Show title in italics
		$font_weight = 'i';
	}
	else
	{
		// Disabled?
		if ( ! $hesk_settings['kb_latest'])
		{
			return $articles;
		}

		// Show title in bold
		$font_weight = 'b';
    }

    // Don't include articles that have already been printed under "Top" articles
    $sql_top = '';
    if (isset($hesk_settings['kb_top_articles_printed']) && count($hesk_settings['kb_top_articles_printed']))
    {
        $sql_top = ' AND `t1`.`id` NOT IN ('.implode(',', $hesk_settings['kb_top_articles_printed']).')';
    }

    // Make sure "public" categories are not nested in a "private" one
    $sql_top .= ' AND `t2`.`id` IN ('.implode(',', $hesk_settings['public_kb_categories_ids']).') ';

    /* Get list of articles from the database */
    $res = hesk_dbQuery("SELECT `t1`.`id`,`t1`.`catid`,`t1`.`subject`,`t1`.`dt`,`t1`.`views`, `t1`.`content`, `t1`.`rating`, `t1`.`votes`, `t2`.`name` AS `category` 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' {$sql_top}
                        ORDER BY `t1`.`dt` DESC LIMIT ".intval($how_many));

	while ($article = hesk_dbFetchAssoc($res))
	{
        // Top category? Translate name
        if ($article['catid'] == 1)
        {
            $article['category'] = $hesklang['kb_text'];
        }

        $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;
	}

	return $articles;
} // END hesk_kbLatestArticles()


function hesk_kbSearchLarge($admin = '')
{
	global $hesk_settings, $hesklang;

	$action = 'knowledgebase.php';

	if ($admin)
	{
		if ( ! $hesk_settings['kb_search'])
		{
			return '';
		}
		$action = 'knowledgebase_private.php';
	}
	elseif ($hesk_settings['kb_search'] != 2)
	{
		return '';
	}
	?>
    <form action="<?php echo $action; ?>" method="get" style="display: inline; margin: 10px" name="searchform" class="form">
        <div class="form-group">
            <label for="kb_largesearch"><?php echo $hesklang['ask']; ?></label>
            <div style="display: flex">
                <input id="kb_largesearch" type="text" name="search" class="searchfield form-control" style="flex-grow: 1; margin-right: 10px">
                <button class="btn btn-full" type="submit" title="<?php echo $hesklang['search']; ?>" class="searchbutton" style="display: inline-block; height: 40px !important">
                    <?php echo $hesklang['search']; ?>
                </button>
            </div>
        </div>
        <!-- START KNOWLEDGEBASE SUGGEST -->
        <div id="kb_suggestions" style="display:none">
            <img src="<?php echo HESK_PATH; ?>img/loading.gif" width="24" height="24" alt="" border="0" style="vertical-align:text-bottom" /> <i><?php echo $hesklang['lkbs']; ?></i>
        </div>

        <script type="text/javascript"><!--
            hesk_suggestKBsearch(<?php echo $admin; ?>);
            //-->
        </script>
        <!-- END KNOWLEDGEBASE SUGGEST -->
    </form>
	<?php
} // END hesk_kbSearchLarge()


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

	if ($hesk_settings['kb_search'] != 1)
	{
		return '';
	}
    ?>

	<td style="text-align:right" valign="top" width="300">
		<div style="display:inline;">
			<form action="knowledgebase.php" method="get" style="display: inline; margin: 0;">
			<input type="text" name="search" class="searchfield sfsmall" />
			<input type="submit" value="<?php echo $hesklang['search']; ?>" title="<?php echo $hesklang['search']; ?>" class="searchbutton sbsmall" />
			</form>
		</div>
	</td>

	<?php
} // END hesk_kbSearchSmall()


function hesk_detect_bots()
{
	$botlist = array('googlebot', 'msnbot', 'slurp', 'alexa', 'teoma', 'froogle',
	'gigabot', 'inktomi', 'looksmart', 'firefly', 'nationaldirectory',
	'ask jeeves', 'tecnoseek', 'infoseek', 'webfindbot', 'girafabot',
	'crawl', 'www.galaxy.com', 'scooter', 'appie', 'fast', 'webbug', 'spade', 'zyborg', 'rabaz',
	'baiduspider', 'feedfetcher-google', 'technoratisnoop', 'rankivabot',
	'mediapartners-google', 'crawler', 'spider', 'robot', 'bot/', 'bot-','voila');

	if ( ! isset($_SERVER['HTTP_USER_AGENT']))
    {
    	return false;
    }

    $ua = strtolower($_SERVER['HTTP_USER_AGENT']);

	foreach ($botlist as $bot)
    {
    	if (strpos($ua,$bot) !== false)
        {
        	return true;
        }
    }

	return false;
} // END hesk_detect_bots()
loader.inc.php
wget 'https://lists2.roe3.org/hesk/inc/loader.inc.php'
View Content
<!--Installed Language Changes Add Loader-->
<div id="overlay_loader">
    <div class="main-spinner">
        <span class="sub-spinner"></span>
    </div>
</div>
<!--Installed Language Changes Add Loader-->
manage_customers_functions.inc.php
wget 'https://lists2.roe3.org/hesk/inc/manage_customers_functions.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}


function hesk_customer_tab($session_array='new', $customer_verified = false)
{
	global $hesk_settings, $hesklang;

	$errors = hesk_SESSION(array($session_array, 'errors'));
	$errors = is_array($errors) ? $errors : array();
	?>
    <div class="step-slider">
        <div class="step-item step-1">
            <div class="form-group">
                <label for="prof_name"><?php echo $hesklang['name']; ?></label>
                <input type="text" class="form-control <?php echo in_array('name', $errors) ? 'isError' : ''; ?>" id="prof_name" name="name" maxlength="50"
                       value="<?php echo $_SESSION[$session_array]['name']; ?>">
            </div>
            <div class="form-group">
                <label for="prof_email"><?php echo $hesklang['email']; ?></label>
                <input type="text" class="form-control <?php echo in_array('email', $errors) ? 'isError' : ''; ?>" name="email" maxlength="255" id="prof_user"
                       value="<?php echo $_SESSION[$session_array]['email']; ?>" aria-label="<?php echo $hesklang['email']; ?>">
            </div>
            <?php if ($hesk_settings['customer_accounts']): ?>
            <section class="item--section customer-password">
                <h4>
                    <?php echo $hesklang['pass']; ?>
                </h4>
                <div class="optional">
                    (<?php echo $hesklang['optional']; ?>)
                </div>
                <div class="form-group">
                    <label for="prof_newpass"><?php echo (empty($_SESSION[$session_array]['id']) ? $hesklang['pass'] : $hesklang['new_pass']); ?></label>
                    <input type="password" id="prof_newpass" name="newpass" autocomplete="off" class="form-control <?php echo in_array('passwords', $errors) ? 'isError' : ''; ?>"
                           value="<?php echo isset($_SESSION[$session_array]['cleanpass']) ? $_SESSION[$session_array]['cleanpass'] : ''; ?>"
                           onkeyup="hesk_checkPassword(this.value)">
                </div>
                <div class="form-group">
                    <label for="prof_newpass2"><?php echo (empty($_SESSION[$session_array]['id']) ? $hesklang['confirm_pass'] : $hesklang['confirm_new_pass']); ?></label>
                    <input type="password" class="form-control <?php echo in_array('passwords', $errors) ? 'isError' : ''; ?>" id="prof_newpass2" name="newpass2" autocomplete="off"
                           value="<?php echo isset($_SESSION[$session_array]['cleanpass']) ? $_SESSION[$session_array]['cleanpass'] : ''; ?>">
                </div>
                <div class="form-group">
                    <label><?php echo $hesklang['pwdst']; ?></label>
                    <div style="border: 1px solid #d4d6e3; width: 100%; height: 14px">
                        <div id="progressBar" style="font-size: 1px; height: 12px; width: 0px; border: none;">
                        </div>
                    </div>
                </div>
                <?php if (!$customer_verified) {
                    hesk_show_info($hesklang['customer_account_setting_password_will_verify_user']);
                } ?>
            </section>
            <?php endif; ?>
        </div>
    </div>

    <script>
        if (document.form1.newpass) {
            hesk_checkPassword(document.form1.newpass.value);
        }
    </script>
	<?php
} // END hesk_profile_tab()
mfa_functions.inc.php
wget 'https://lists2.roe3.org/hesk/inc/mfa_functions.inc.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
 *
 */

/* Check if this is a valid include */

use RobThree\Auth\TwoFactorAuth;

if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

// region Email Authentication
function generate_mfa_code() {
    return str_pad(random_int(100000, 999999), 6, '0', STR_PAD_LEFT);
}

function hash_and_store_mfa_verification_code($user_id, $verification_code, $user_type = 'STAFF') {
    global $hesk_settings;

    $hashed_verification_code = hesk_password_hash($verification_code);

    // Allow 20 minutes to verify ("INTERVAL 20 MINUTE" is **very** intuitive...)
    hesk_dbQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "mfa_verification_tokens` (`user_id`, `verification_token`, `expires_at`, `user_type`)
                    VALUES (" . intval($user_id) . ", 
                        '" . hesk_dbEscape($hashed_verification_code) . "', 
                        NOW() + INTERVAL 20 MINUTE, 
                        '".hesk_dbEscape($user_type)."')");
}

function send_mfa_email($name, $email, $verification_code) {
    global $hesk_settings, $hesklang;

    // Demo mode
    if (defined('HESK_DEMO')) {
        return '';
    }

    // Prepare and send email
    if (!function_exists('hesk_mail')) {
        require(HESK_PATH . 'inc/email_functions.inc.php');
    }

    // Get the email message
    list($msg, $html_msg) = hesk_getEmailMessage('mfa_verification',array(),1,0,1);

    // Replace message special tags
    $name = hesk_msgToPlain($name, 1, 1);
    list($msg, $html_msg) = hesk_replace_email_tag('%%NAME%%', $name, $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%FIRST_NAME%%', hesk_full_name_to_first_name($name), $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%SITE_TITLE%%', hesk_msgToPlain($hesk_settings['site_title'], 1, 0), $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%SITE_URL%%', $hesk_settings['site_url'] . ' ', $msg, $html_msg);
    list($msg, $html_msg) = hesk_replace_email_tag('%%VERIFICATION_CODE%%', $verification_code, $msg, $html_msg);

    // Send email
    return hesk_mail($email, [], str_replace('%%VERIFICATION_CODE%%', $verification_code, $hesklang['mfa_verification']), $msg, $html_msg);
}

// Note: Deletes all verification codes upon successful validation
function is_mfa_email_code_valid($user_id, $verification_code, $user_type = 'STAFF') {
    global $hesk_settings;

    //-- Purge any verification codes that are expired
    hesk_dbQuery("DELETE FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "mfa_verification_tokens` 
            WHERE `expires_at` < NOW()");

    //-- Also purge all but the last 3 codes for a given user
    $existing_tokens_rs = hesk_dbQuery("SELECT `id` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."mfa_verification_tokens` WHERE `user_id` = ".intval($user_id)." AND `user_type` = '".hesk_dbEscape($user_type)."' ORDER BY `id` DESC");
    $index = 0;
    while ($row = hesk_dbFetchAssoc($existing_tokens_rs)) {
        if ($index++ < 3) {
            continue;
        }

        hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."mfa_verification_tokens` 
            WHERE `id` = ".intval($row['id']));
    }

    //-- Grab remaining verification codes and see if one matches
    $res = hesk_dbQuery("SELECT `verification_token` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "mfa_verification_tokens` 
            WHERE `user_id` = " . intval($user_id) . " 
            AND `user_type` = '" . hesk_dbEscape($user_type) . "'");

    while ($row = hesk_dbFetchAssoc($res)) {
        if (hesk_password_verify($verification_code, $row['verification_token'])) {
            delete_mfa_codes($user_id, $user_type);
            return true;
        }
    }

    return false;
}

function delete_mfa_codes($user_id, $user_type = 'STAFF') {
    global $hesk_settings;

    hesk_dbQuery("DELETE FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "mfa_verification_tokens`
                WHERE `user_id` = " . intval($user_id) . "
                    AND `user_type` = '".hesk_dbEscape($user_type)."'");
}
// endregion

// region App-based Authentication
function build_tfa_instance() {
    global $hesk_settings;

    return new TwoFactorAuth($hesk_settings['hesk_title']);
}

function is_mfa_app_code_valid($user_id, $verification_code, $secret = null, $user_type = 'STAFF') {
    global $hesk_settings, $hesklang;

    $tfa = build_tfa_instance();

    try {
        $tfa->ensureCorrectTime(array(new \RobThree\Auth\Providers\Time\HttpTimeProvider()));
    } catch (\RobThree\Auth\TwoFactorAuthException $e) {
        hesk_error(sprintf($hesklang['mfa_server_time_issue'], $e->getMessage()));
    }

    if ($secret === null) {
        $table = $user_type === 'STAFF' ? 'users' : 'customers';
        $res = hesk_dbQuery("SELECT `mfa_secret` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . $table."` WHERE `id` = " . intval($user_id));
        $row = hesk_dbFetchAssoc($res);
        $secret = $row['mfa_secret'];
    }

    return $tfa->verifyCode($secret, $verification_code);
}
// endregion

// region Backup Codes
function generate_and_store_mfa_backup_codes($user_id, $delete_old_codes = true, $user_type = 'STAFF') {
    global $hesk_settings;

    if ($delete_old_codes) {
        delete_mfa_backup_codes($user_id, $user_type);
    }

    $codes = array();
    for ($code_index = 0; $code_index < 10; $code_index++) {
        $unique_code_generated = false;
        do {
            $code = generate_backup_code();
            if (!in_array($code, $codes)) {
                $codes[] = $code;
                $unique_code_generated = true;
                store_backup_code($user_id, $code, $user_type);
            }
        } while (!$unique_code_generated);
    }

    return $codes;
}

function generate_backup_code() {
    $valid_chars = '0123456789abcdef';
    $code = '';
    for ($char_index = 0; $char_index < 8; $char_index++) {
        $code .= $valid_chars[random_int(0, strlen($valid_chars) - 1)];
    }

    return $code;
}

function store_backup_code($user_id, $code, $user_type = 'STAFF') {
    global $hesk_settings;

    $hashed_code = hesk_password_hash($code);

    hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."mfa_backup_codes` (`user_id`, `code`, `user_type`)
        VALUES (".intval($user_id).", '".hesk_dbEscape($hashed_code)."', '".hesk_dbEscape($user_type)."')");
}

function delete_mfa_backup_codes($user_id, $user_type = 'STAFF') {
    global $hesk_settings;

    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."mfa_backup_codes` 
        WHERE `user_id` = ".intval($user_id).
            " AND `user_type` = '".hesk_dbEscape($user_type)."'");
}

function delete_mfa_backup_code($user_id, $hashed_code, $user_type = 'STAFF') {
    global $hesk_settings;

    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."mfa_backup_codes` 
        WHERE `user_id` = ".intval($user_id)." AND `code` = '".hesk_dbEscape($hashed_code)."' AND `user_type` = '".hesk_dbEscape($user_type)."'");
}

function verify_mfa_backup_code($user_id, $code, $user_type = 'STAFF') {
    global $hesk_settings;

    // Allow spaces, dashes, etc... in the backup code for easier printout
    $code = preg_replace('/[^0-9a-f]/', '', strtolower($code));

    $res = hesk_dbQuery("SELECT `code` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."mfa_backup_codes` 
        WHERE `user_id` = ".intval($user_id)." 
            AND `user_type` = '".hesk_dbEscape($user_type)."'");
    while ($row = hesk_dbFetchAssoc($res)) {
        if (hesk_password_verify($code, $row['code'])) {
            delete_mfa_backup_code($user_id, $row['code'], $user_type);
            return true;
        }
    }

    return false;
}
// endregion
oauth_functions.inc.php
wget 'https://lists2.roe3.org/hesk/inc/oauth_functions.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {
    die('Invalid attempt');
}

function hesk_get_oauth_redirect_url() {
    global $hesk_settings;

    return $hesk_settings['hesk_url'] . '/' . $hesk_settings['admin_dir'] . '/oauth_providers.php';
}


function hesk_oauth_prepare_provider_state($provider_id)
{
    $state = bin2hex(random_bytes(32));

    if ( ! isset($_SESSION['oauth_provider_state']) || ! is_array($_SESSION['oauth_provider_state'])) {
        $_SESSION['oauth_provider_state'] = array();
    }

    $_SESSION['oauth_provider_state'][$state] = intval($provider_id);

    return $state;
} // End hesk_oauth_prepare_provider_state()


function hesk_oauth_verify_provider_state($state)
{
    $state = strtolower(trim($state));

    if ( ! preg_match('/\A[a-f0-9]{64}\z/', $state)) {
        return false;
    }

    if ( ! isset($_SESSION['oauth_provider_state'][$state])) {
        return false;
    }

    $provider_id = intval($_SESSION['oauth_provider_state'][$state]);
    unset($_SESSION['oauth_provider_state'][$state]);

    if (empty($_SESSION['oauth_provider_state'])) {
        unset($_SESSION['oauth_provider_state']);
    }

    return $provider_id > 0 ? $provider_id : false;
} // End hesk_oauth_verify_provider_state()

function hesk_oauth_fetch_and_store_initial_token($provider, $code, $redirect_to = './oauth_providers.php') {
    global $hesk_settings, $hesklang;

    // Grab an access token and refresh token from the server
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $provider['token_url']);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 6);
    curl_setopt($ch, CURLOPT_POST, 1);

    if ($provider['no_val_ssl']) {
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    }

    $redirect_url = hesk_get_oauth_redirect_url();
    $post_fields = array(
        'grant_type' => 'authorization_code',
        'client_id' => $provider['client_id'],
        'scope' => $provider['scope'],
        'redirect_uri' => $redirect_url,
        'client_secret' => $provider['client_secret'],
        'code' => $code,
        'access_type' => 'offline'
    );
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_fields, '', '&'));
    $response = curl_exec($ch);
    if ($response === false) {
        $http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        die("cURL {$hesklang['error']}: $http_status<br>\n{$hesklang['error']}: $error");
    }
    hesk_curl_close($ch);

    $decoded_response = json_decode($response, true);

    if ($decoded_response === false) {
        //-- We didn't get a JSON response; this should never happen!
        hesk_process_messages($hesklang['oauth_error_unknown'], $redirect_to);
    } else {
        if (isset($decoded_response['error'])) {
            //-- Error occurred; go back to settings with an error prompt
            $error = isset($decoded_response['error_description']) ? $decoded_response['error_description'] : $hesklang['oauth_error_unknown'];
            $error = hesk_htmlspecialchars($error);

            $debug = '';
            if ($hesk_settings['debug_mode']) {
                $debug = "Response: " . var_export($response, true) . "\n" .
                    "JSON: " . var_export($decoded_response, true) . "\n";
                $debug = '<br><br><textarea rows="10" cols="60">Debug info: ' . "\n" . hesk_htmlspecialchars($debug) . '</textarea>';
            }

            hesk_process_messages(
                $error . $debug,
                $redirect_to
            );
        }

        // Did we get an access token at all?
        if ( ! isset($decoded_response['access_token'])) {
            hesk_process_messages(
                $hesklang['oauth_error_no_token'] . ($hesk_settings['debug_mode'] ? '<br><br><textarea rows="10" cols="60">Debug info: ' . "\n" . hesk_htmlspecialchars(
                    "Response: " . var_export($response, true) . "\n" .
                    "JSON: " . var_export($decoded_response, true) . "\n"
                ) . '</textarea>' : ''),
                $redirect_to
            );
        }

        //-- Save tokens to DB, return to email settings as we probably came from there.
        hesk_store_oauth_token($provider['id'], $decoded_response['access_token'], 'access_token', $decoded_response['expires_in']);

        // Refresh tokens are long-lasting, so we won't store an expiry as we'll always attempt to use it.
        if (isset($decoded_response['refresh_token'])) {
            hesk_store_oauth_token($provider['id'], $decoded_response['refresh_token'], 'refresh_token');
        }

        //-- This provider is now verified
        hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."oauth_providers` SET `verified` = 1 WHERE `id`={$provider['id']}");
        hesk_process_messages($hesklang['oauth_provider_saved'] . '<br><br>' . sprintf($hesklang['oauth_provider_use'], $hesklang['settings'], $hesklang['tab_6']), $redirect_to, 'SUCCESS');
    }
}

function hesk_store_oauth_token($provider_id, $token, $token_type, $expiry_in_seconds = null) {
    global $hesk_settings;

    // If we have another token with the same provider ID and type, purge it
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."oauth_tokens` 
        WHERE `provider_id` = '".intval($provider_id)."' 
            AND `token_type` = '".hesk_dbEscape($token_type)."'");

    // Store the new token. Subtracting 15 from the expiry to ensure that we won't attempt to use an expired access token down the road
    $expiry_time = $expiry_in_seconds === null ?
        'NULL' :
        "NOW() + INTERVAL ".intval($expiry_in_seconds - 15)." SECOND";
    hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."oauth_tokens` (`provider_id`, `token_value`, `token_type`, `expires`)
    VALUES ('".intval($provider_id)."',
            '".hesk_dbEscape($token)."',
            '".hesk_dbEscape($token_type)."',
            ".$expiry_time.")");
}

function hesk_fetch_access_token($provider_id) {
    global $hesk_settings;

    // Check if we have a token that is still valid for the next 30 seconds
    $res = hesk_dbQuery("SELECT `token_value` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."oauth_tokens`
        WHERE `provider_id` = '".hesk_dbEscape($provider_id)."'
            AND `token_type` = 'access_token'
            AND `expires` >= NOW() + INTERVAL 30 SECOND");

    if ($row = hesk_dbFetchAssoc($res)) {
        return $row['token_value'];
    }

    // No token available.  Fetch a new token via its refresh token, store the new access / refresh, and return the new access token.
    $res = hesk_dbQuery("SELECT `token_value` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."oauth_tokens`
        WHERE `provider_id` = '".hesk_dbEscape($provider_id)."'
            AND `token_type` = 'refresh_token'");

    if ($row = hesk_dbFetchAssoc($res)) {
        return hesk_retrieve_new_access_token($provider_id, $row['token_value']);
    }

    //-- Something went horribly wrong.  We should never *not* have a refresh token.
    return false;
}

function hesk_retrieve_new_access_token($provider_id, $refresh_token) {
    global $hesk_settings, $hesklang;

    $provider_rs = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."oauth_providers` WHERE `id` = ".intval($provider_id));
    $provider = hesk_dbFetchAssoc($provider_rs);

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $provider['token_url']);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 6);
    curl_setopt($ch, CURLOPT_POST, 1);

    if ($provider['no_val_ssl']) {
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    }

    $redirect_url = hesk_get_oauth_redirect_url();
    $post_fields = array(
        'grant_type' => 'refresh_token',
        'client_id' => $provider['client_id'],
        'scope' => $provider['scope'],
        'redirect_uri' => $redirect_url,
        'client_secret' => $provider['client_secret'],
        'refresh_token' => $refresh_token,
        'access_type' => 'offline'
    );
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_fields, '', '&'));
    $response = curl_exec($ch);
    if ($response === false) {
        $http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        die("cURL {$hesklang['error']}: $http_status<br>\n{$hesklang['error']}: $error");
    }
    hesk_curl_close($ch);

    $decoded_response = json_decode($response, true);

    if ($decoded_response === false) {
        // Something terribly bad happened if we get here.
        return false;
    }

    // Did we get an access token at all?
    if ( ! isset($decoded_response['access_token'])) {
        return false;
    }

    //-- Save tokens to DB
    hesk_store_oauth_token($provider_id, $decoded_response['access_token'], 'access_token', $decoded_response['expires_in']);

    if (isset($decoded_response['refresh_token'])) {
        hesk_store_oauth_token($provider_id, $decoded_response['refresh_token'], 'refresh_token');
    }

    return $decoded_response['access_token'];
}
pipe_functions.inc.php
wget 'https://lists2.roe3.org/hesk/inc/pipe_functions.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

// Include all functions needed for email piping
hesk_load_database_functions();
require(HESK_PATH . 'inc/email_functions.inc.php');
require(HESK_PATH . 'inc/posting_functions.inc.php');
require(HESK_PATH . 'inc/mail/rfc822_addresses.php');
require(HESK_PATH . 'inc/mail/mime_parser.php');
require(HESK_PATH . 'inc/mail/email_parser.php');
require_once(HESK_PATH . 'inc/customer_accounts.inc.php');

/*** FUNCTIONS ***/

function hesk_email2ticket($results, $protocol = 0, $set_category = 1, $set_priority = -1)
{
	global $hesk_settings, $hesklang, $hesk_db_link, $ticket;

    if ( ! is_array($results)) {
        return hesk_cleanExit('Email parser returned invalid data; skipping ticket creation.');
    }

	// Process "Reply-To:" or "From:" email
	$tmpvar['email'] = isset($results['reply-to'][0]['address']) ? hesk_validateEmail($results['reply-to'][0]['address'],'ERR',0) : hesk_validateEmail($results['from'][0]['address'],'ERR',0);

    // Email missing, invalid or banned?
    if ( ! $tmpvar['email'])
    {
        return hesk_cleanExit('Customer email is missing or invalid: ' . $tmpvar['email']);
    }
    elseif (hesk_isBannedEmail($tmpvar['email']))
    {
        return hesk_cleanExit('Banned customer email address: ' . $tmpvar['email']);
    }

    $sender_lowercase = strtolower($tmpvar['email']);

    // Ignore emails sent from the same email address we fetch tickets from or we will create a loop
    // -> email sender matches Hesk's "From email"
    if ($sender_lowercase == strtolower($hesk_settings['noreply_mail'])) {
        return hesk_cleanExit('Email from Hesk "From email", ignoring to prevent email loops');
    }

    // -> email sender matches Hesk's "SMTP user"
    if ($hesk_settings['smtp'] && $sender_lowercase == strtolower($hesk_settings['smtp_user'])) {
        return hesk_cleanExit('Email from Hesk "SMTP user", ignoring to prevent email loops');
    }

    // -> email sender matches Hesk's "IMAP user"
    if ($protocol == 2 && $sender_lowercase == strtolower($hesk_settings['imap_user'])) {
        return hesk_cleanExit('Email from Hesk "IMAP user", ignoring to prevent email loops');
    }

    // -> email sender matches Hesk's "POP3 user"
    if ($protocol == 1 && $sender_lowercase == strtolower($hesk_settings['pop3_user'])) {
        return hesk_cleanExit('Email from Hesk "POP3 user", ignoring to prevent email loops');
    }

	// Process "Reply-To:" or "From:" name, convert to UTF-8, set to "[Customer]" if not set
	if ( isset($results['reply-to'][0]['name']) && strlen($results['reply-to'][0]['name']) )
	{
   		$tmpvar['name'] = $results['reply-to'][0]['name'];
		if ( ! empty($results['reply-to'][0]['encoding']) )
		{
			$tmpvar['name'] = hesk_encodeUTF8($tmpvar['name'], $results['reply-to'][0]['encoding']);
		}
	}
	else
	{
   		$tmpvar['name'] = isset($results['from'][0]['name']) ? $results['from'][0]['name'] : '';
		if ( ! empty($results['from'][0]['encoding']) )
		{
			$tmpvar['name'] = hesk_encodeUTF8($tmpvar['name'], $results['from'][0]['encoding']);
		}
	}
	$tmpvar['name'] = hesk_input($tmpvar['name'],'','',1,50) or $tmpvar['name'] = '';

	// Process email subject, convert to UTF-8, set to "[Piped email]" if none set
	$tmpvar['subject'] = isset($results['subject']) ? $results['subject'] : $hesklang['pem'];
    $tmpvar['subject'] = preg_replace("/(\n|\r|\t|%0A|%0D|%08|%09)+/", " ", $tmpvar['subject']);
	if ( ! empty($results['subject_encoding']) )
	{
		$tmpvar['subject'] = hesk_encodeUTF8($tmpvar['subject'], $results['subject_encoding']);
	}
	$tmpvar['subject'] = hesk_input($tmpvar['subject'],'','',1,70) or $tmpvar['subject'] = $hesklang['pem'];

	// Process email message, convert to UTF-8
	$tmpvar['message'] = isset($results['message']) ? $results['message'] : '';
	if ( ! empty($results['encoding']) )
	{
		$tmpvar['message'] = hesk_encodeUTF8($tmpvar['message'], $results['encoding']);
	}
	$tmpvar['message'] = hesk_input($tmpvar['message'],'','',1);

	// Message missing?
	if ( strlen($tmpvar['message']) == 0)
	{
		// Message required? Ignore this email.
		if ($hesk_settings['eml_req_msg'])
		{
			return hesk_cleanExit('Missing email message');
		}

		// Message not required? Assign a default message
		$tmpvar['message'] = $hesklang['def_msg'];

		// Track duplicate emails based on subject
		$message_hash = md5($tmpvar['subject']);
	}
	else
	{
    	$message_hash = md5($tmpvar['message']);
	}

	// Strip quoted reply from email
    $tmpvar['message'] = hesk_stripQuotedText($tmpvar['message']);

	// Convert URLs to links, change newlines to <br />
	$tmpvar['message'] = hesk_makeURL($tmpvar['message']);
	$tmpvar['message'] = nl2br($tmpvar['message']);

    // Store a copy for RTF-version
    $tmpvar['message_html'] = $tmpvar['message'];

	# For debugging purposes
    # die( bin2hex($tmpvar['message']) );
    # die($tmpvar['message']);

    // Try to detect emails from "noreply" addresses
    if ($hesk_settings['pipe_block_noreply'] && hesk_isDoNotReplyEmail($tmpvar))
    {
        return hesk_cleanExit('Email sent from a "noreply" email address: ' . $tmpvar['email']);
    }

    // Try to detect "delivery failed" emails
    if ($hesk_settings['pipe_block_returned'] && hesk_isReturnedEmail($tmpvar))
    {
        return hesk_cleanExit('Looks like a returned email');
    }

    // Get email ID and Reply to ID
    $tmpvar['email_id'] = isset($results['Message-ID']) ? preg_replace('[^a-zA-Z0-9!#$%&\'*+/=?^_`{|}~-]', '', $results['Message-ID']) : '';
    $tmpvar['email_reply_to'] = isset($results['In-Reply-To']) ? preg_replace('[^a-zA-Z0-9!#$%&\'*+/=?^_`{|}~-]', '', $results['In-Reply-To']) : '';

    // Has an email with this exact email ID been processed before?
    if (strlen($tmpvar['email_id'])) {
        $res = hesk_dbQuery("SELECT 1 FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."email_id_to_ticket` WHERE `email_id`='".hesk_dbEscape($tmpvar['email_id'])."' LIMIT 1");
        if (hesk_dbNumRows($res)) {
            return hesk_cleanExit('Email with this message ID has already been processed');
        }
    }

	// Check for email loops
	if (($reason = hesk_isEmailLoop($tmpvar['email'], $message_hash)) !== false)
	{
		return hesk_cleanExit($reason);
	}

    //-- Retrieve (or create) customer
    if ($hesk_settings['customer_accounts'] === 0 || ($hesk_settings['customer_accounts'] === 1 && $hesk_settings['customer_accounts_required'] === 0)) {
        $tmpvar['customer_id'] = hesk_get_or_create_customer($tmpvar['name'], $tmpvar['email']);
    } else {
        // Only allow existing customers to email their tickets/replies
        $tmpvar['customer_id'] = hesk_get_customer_id_by_email($tmpvar['email'], true);
        if ($tmpvar['customer_id'] === null) {
            if ($hesk_settings['pipe_customer_rejection_notification']) {
                hesk_notifyRejectedCustomer($tmpvar);
            }

            $reason = "Could not find customer with email '{$tmpvar['email']}' and helpdesk settings require an account to be created prior to sending tickets via e-mail.";
            return hesk_cleanExit($reason);
        }
    }

    // Process followers (additional people listed in To or Cc)
    $tmpvar['followers'] = array();
    $tmpvar['follower_ids'] = array();
    if ($hesk_settings['multi_eml']) {
        $copied = array();

        // Process "To:" recipients
        if ($hesk_settings['email_include_to']) {
            foreach ($results['to'] as $key => $recipient) {
                $recipient = hesk_processEmailContact($recipient, $protocol);
                if (is_array($recipient)) {
                    $copied[] = $recipient;
                } elseif (is_string($recipient)) {
                    if ($hesk_settings['debug_mode']) {
                        $hesk_settings['DEBUG_LOG']['NOTICES'][] = $recipient;
                    }
                    continue;
                } else {
                    continue;
                }
            }
        }

        // Process "Cc:" recipients
        if ($hesk_settings['email_include_cc']) {
            foreach ($results['cc'] as $key => $recipient) {
                $recipient = hesk_processEmailContact($recipient, $protocol);
                if (is_array($recipient)) {
                    $copied[] = $recipient;
                } elseif (is_string($recipient)) {
                    if ($hesk_settings['debug_mode']) {
                        $hesk_settings['DEBUG_LOG']['NOTICES'][] = $recipient;
                    }
                    continue;
                } else {
                    continue;
                }
            }
        }

        foreach ($copied as $c) {
            // Remove duplicate recipients
            if (in_array($c['customer_id'], $tmpvar['follower_ids'])) {
                continue;
            }

            // Remove "From" email address, it's already included
            if ($c['email'] == $sender_lowercase) {
                continue;
            }

            $tmpvar['followers'][] = $c['email'];
            $tmpvar['follower_ids'][] = $c['customer_id'];
        }
    } // END if $hesk_settings['multi_eml']

	// OK, everything seems OK. Now determine if this is a reply to a ticket or a new ticket
    $tmpvar['trackid'] = '';
    if (isset($results['X-Hesk-Tracking_ID']) && preg_match('/([A-Z0-9]{3}\-[A-Z0-9]{3}\-[A-Z0-9]{4})/', $results['X-Hesk-Tracking_ID'])) {
        // Tracking ID found in custom X-Hesk-Tracking_ID email header
        $tmpvar['trackid'] = $results['X-Hesk-Tracking_ID'];
        if ($hesk_settings['debug_mode']) {
            $hesk_settings['DEBUG_LOG']['NOTICES'][] = 'Tracking ID found in the X-Hesk-Tracking_ID email header';
        }
    } elseif ( preg_match('/\[#([A-Z0-9]{3}\-[A-Z0-9]{3}\-[A-Z0-9]{4})\]/', str_replace(' ', '', $tmpvar['subject']), $matches) ) {
        // Tracking ID found in email subject
        $tmpvar['trackid'] = $matches[1];
        if ($hesk_settings['debug_mode']) {
            $hesk_settings['DEBUG_LOG']['NOTICES'][] = 'Tracking ID found in the email subject';
        }
    } elseif (strlen($tmpvar['email_reply_to'])) {
        // Can we find the ticket ID by email "In-Reply-To" header?
        $res = hesk_dbQuery("SELECT `ticket_id` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."email_id_to_ticket` WHERE `email_id`='".hesk_dbEscape($tmpvar['email_reply_to'])."' LIMIT 1");
        if (hesk_dbNumRows($res)) {
            $ticket_id = hesk_dbResult($res);
            $res = hesk_dbQuery("SELECT `trackid` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `id`=".intval($ticket_id)." LIMIT 1");
            if (hesk_dbNumRows($res)) {
                $tmpvar['trackid'] = hesk_dbResult($res);
                if ($hesk_settings['debug_mode']) {
                    $hesk_settings['DEBUG_LOG']['NOTICES'][] = 'Tracking ID found by email message ID lookup';
                }
            }
        }
    }

	if ($tmpvar['trackid'] != '')
	{
	    // Does it match one in the database?
		$res = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `trackid`='".hesk_dbEscape($tmpvar['trackid'])."' LIMIT 1");
		if (hesk_dbNumRows($res))
		{
			$ticket = hesk_dbFetchAssoc($res);

	        // Do email addresses match?
            $customers = hesk_get_customers_for_ticket($ticket['id']);
            $customer_emails = array_map(function($customer) {return strtolower($customer['email']);}, $customers);

            if (!in_array(strtolower($tmpvar['email']), $customer_emails))
	        {
	        	$tmpvar['trackid'] = '';
	        }

	        // Is this ticket locked? Force create a new one if it is
	        if ($ticket['locked'])
	        {
	        	$tmpvar['trackid'] = '';
	        }
	    }
	    else
	    {
	    	$tmpvar['trackid'] = '';
	    }
	}

	// If tracking ID is empty, generate a new one
	if ( empty($tmpvar['trackid']) )
	{
		$tmpvar['trackid'] = hesk_createID();
	    $is_reply = 0;
	}
	else
	{
		$is_reply = 1;
	}

	// Process attachments
    $tmpvar['attachmment_notices'] = '';
	$tmpvar['attachments'] = '';
	$num = 0;
	if ($hesk_settings['attachments']['use'] && isset($results['attachments'][0]))
	{
	    foreach ($results['attachments'] as $k => $v)
	    {

			// Clean attachment names
			$myatt['real_name'] = hesk_cleanFileName($v['orig_name']);

	    	// Check number of attachments, delete any over max number
	        if ($num >= $hesk_settings['attachments']['max_number'])
	        {
            	$tmpvar['attachmment_notices'] .= sprintf($hesklang['attnum'], $myatt['real_name']) . "\n";
	            continue;
	        }

	        // Check file extension
			$ext = strtolower(strrchr($myatt['real_name'], "."));
			if (!in_array($ext,$hesk_settings['attachments']['allowed_types']))
			{
            	$tmpvar['attachmment_notices'] .= sprintf($hesklang['atttyp'], $myatt['real_name']) . "\n";
				continue;
			}

	        // Check file size
	        $myatt['size'] = $v['size'];
			if ($myatt['size'] > ($hesk_settings['attachments']['max_size']))
			{
            	$tmpvar['attachmment_notices'] .= sprintf($hesklang['attsiz'], $myatt['real_name']) . "\n";
				continue;
			}

			// Generate a random file name
			$useChars='AEUYBDGHJLMNPQRSTVWXZ123456789';
			$tmp = $useChars[mt_rand(0,29)];
			for($j=1;$j<10;$j++)
			{
			    $tmp .= $useChars[mt_rand(0,29)];
			}
		    $myatt['saved_name'] = substr($tmpvar['trackid'] . '_' . md5($tmp . $myatt['real_name']), 0, 200) . $ext;

	        // Rename the temporary file
            $myatt['stored_name'] = isset($v['stored_name']) ? $v['stored_name'] : '';
            if ( ! is_file($myatt['stored_name']) || ! rename($myatt['stored_name'], HESK_PATH.$hesk_settings['attach_dir'].'/'.$myatt['saved_name']))
            {
                 $tmpvar['attachmment_notices'] .= $hesklang['attdel'] . ': ' . $myatt['real_name'] . "\n";
                continue;
            }

	        // Insert into database
	        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'] .',';

	        $num++;
	    }

        if (strlen($tmpvar['attachmment_notices']))
        {
        	$tmpvar['message'] .= "<br /><br />" . hesk_input($hesklang['attrem'],'','',1) . "<br />" . nl2br(hesk_input($tmpvar['attachmment_notices'],'','',1));
            $tmpvar['message_html'] .= "<br /><br />" . hesk_input($hesklang['attrem'],'','',1) . "<br />" . nl2br(hesk_input($tmpvar['attachmment_notices'],'','',1));
        }
	}

	// Delete the temporary files
	deleteAll($results['tempdir']);

	// If this is a reply add a new reply
	if ($is_reply)
	{
		// Set last replier name to customer name
		$ticket['lastreplier'] = ($tmpvar['name'] == '') ? $tmpvar['email'] : $tmpvar['name'];
        $ticket['customer_id'] = hesk_get_or_create_customer($tmpvar['name'], $tmpvar['email']);

		// If staff hasn't replied yet, keep ticket status "New", otherwise set it to "Waiting reply from staff"
		$ticket['status'] = $ticket['status'] ? 1 : 0;

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

		// If customer replied, we assume staff replies have been read (no way to be sure if ticket.php hasn't been opened)
		hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` SET `read` = '1' WHERE `replyto` = '".intval($ticket['id'])."' AND `staffid` != '0' ");

		// Insert reply into database
		hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` (`replyto`,`customer_id`,`message`,`message_html`,`dt`,`attachments`) VALUES ('".intval($ticket['id'])."','".intval($ticket['customer_id'])."','".hesk_dbEscape($tmpvar['message'])."','".hesk_dbEscape($tmpvar['message'])."',NOW(),'".hesk_dbEscape($tmpvar['attachments'])."')");

        // Save the email ID map to ticket for later use
        if ( ! empty($tmpvar['email_id'])) {
            $reply_id = hesk_dbInsertID();
            hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."email_id_to_ticket` (`email_id`, `ticket_id`, `reply_id`, `from_hesk`) VALUES ('".hesk_dbEscape($tmpvar['email_id'])."', ".intval($ticket['id']).", {$reply_id}, 0)");
        }

		// --> Prepare reply message
        $customers = hesk_get_customers_for_ticket($ticket['id']);
        $customer_names = array_map(function($customer) { return $customer['name']; }, $customers);
        $customer_emails = array_map(function($customer) { return $customer['email']; }, $customers);

		// 1. Generate the array with ticket info that can be used in emails
		$info = array(
		'email'			=> implode(';', $customer_emails),
		'category'		=> $ticket['category'],
		'priority'		=> $ticket['priority'],
		'owner'			=> $ticket['owner'],
        'collaborators' => hesk_getTicketsCollaboratorIDs($ticket['id']),
		'trackid'		=> $ticket['trackid'],
		'status'		=> $ticket['status'],
		'name'			=> implode(',', $customer_names),
		'lastreplier'	=> $ticket['lastreplier'],
		'subject'		=> $ticket['subject'],
		'message'		=> stripslashes($tmpvar['message']),
		'attachments'	=> $tmpvar['attachments'],
		'dt'			=> hesk_date($ticket['dt'], true),
		'lastchange'	=> hesk_date($ticket['lastchange'], true),
        'due_date'      => hesk_format_due_date($ticket['due_date']),
        'id'			=> $ticket['id'],
        'time_worked'   => $ticket['time_worked'],
		);

		// 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);

		// --> Process custom fields before sending
		foreach ($hesk_settings['custom_fields'] as $k => $v)
		{
			$ticket[$k] = $v['use'] ? hesk_msgToPlain($ticket[$k], 1) : '';
		}

        // --> 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(",", array_map('intval', $ticket['collaborators']))."))");
        }
        // --> No owner assigned, find and notify appropriate staff, no collaborators
        else {
            hesk_notifyStaff('new_reply_by_customer',"`notify_reply_unassigned`='1'");
        }

		return $ticket['trackid'];

	} // END REPLY

	// Not a reply, but a new ticket. Add it to the database
	$tmpvar['category'] 	= $set_category;
	$tmpvar['priority'] 	= $set_priority < 0 ? hesk_getCategoryPriority($tmpvar['category']) : $set_priority;

	// Auto assign tickets if aplicable
	$tmpvar['owner']   = 0;

    // What protocol did we use to submit the ticket?
    switch ($protocol)
    {
        // POP3 fetching
        case 1:
            $tmpvar['history'] = sprintf($hesklang['thist16'], hesk_date());
            $tmpvar['openedby'] = -2;
            break;

        // IMAP fetching
        case 2:
            $tmpvar['history'] = sprintf($hesklang['thist17'], hesk_date());
            $tmpvar['openedby'] = -3;
            break;

        // Email piping
        default:
            $tmpvar['history'] = sprintf($hesklang['thist11'], hesk_date());
            $tmpvar['openedby'] = -1;
    }

	$autoassign_owner = hesk_autoAssignTicket($tmpvar['category']);

	#print_r($autoassign_owner);

	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;
	}

	// Custom fields will be empty as there is no reliable way of detecting them
	foreach ($hesk_settings['custom_fields'] as $k=>$v)
	{
		$tmpvar[$k] = '';
	}

    if (($default_due_date_info = hesk_getCategoryDueDateInfo($tmpvar['category'])) !== null)
    {
        $due_date = new DateTime('today midnight');
        $due_date->add(DateInterval::createFromDateString("+{$default_due_date_info['amount']} {$default_due_date_info['unit']}s"));
        $tmpvar['due_date'] = hesk_datepicker_format_date($due_date->getTimestamp());

        // Don't set a due date if any unexpected errors
        if ($tmpvar['due_date'] === false) {
            $tmpvar['due_date'] = '';
        }
    }

    // Create ticket
	$ticket = hesk_newTicket($tmpvar);

	// Notify the customer
	if ($hesk_settings['notify_new'])
	{
		$possible_SPAM = false;

		// Do we need to check subject for SPAM tags?
		if ($hesk_settings['notify_skip_spam'])
		{
			foreach ($hesk_settings['notify_spam_tags'] as $tag)
			{
				if ( strpos($tmpvar['subject'], $tag) !== false )
				{
					$possible_SPAM = true;
					break;
				}
			}
		}

		// SPAM tags not found or not checked, send email
		if ($possible_SPAM === false)
		{
			hesk_notifyCustomer();
		}
	}

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

    return $ticket['trackid'];
} // END hesk_email2ticket()


function hesk_processEmailContact($contact, $protocol)
{
    global $hesk_settings, $hesklang;

    // Validate email
    $contact['email'] = hesk_validateEmail($contact['address'], 'ERR', 0);

    // Email missing?
    if ( ! $contact['email']) {
        return false;
    }

    $contact['email'] = strtolower($contact['email']);

    // Ignore emails sent from the same email address we fetch tickets from or we will create a loop
    // -> email sender matches Hesk's "From email"
    if ($contact['email'] == strtolower($hesk_settings['noreply_mail'])) {
        return 'Removed ' . $contact['email'] . ' (set as "From email" in Hesk settings, ignoring to prevent email loops)';
    }

    // -> email sender matches Hesk's "SMTP user"
    if ($hesk_settings['smtp'] && $contact['email'] == strtolower($hesk_settings['smtp_user'])) {
        return 'Removed ' . $contact['email'] . ' (set as "SMTP user" in Hesk settings, ignoring to prevent email loops)';
    }

    // -> email sender matches Hesk's "IMAP user"
    if ($protocol == 2 && $contact['email'] == strtolower($hesk_settings['imap_user'])) {
        return 'Removed ' . $contact['email'] . ' (set as "IMAP user" in Hesk settings, ignoring to prevent email loops)';
    }

    // -> email sender matches Hesk's "POP3 user"
    if ($protocol == 1 && $contact['email'] == strtolower($hesk_settings['pop3_user'])) {
        return 'Removed ' . $contact['email'] . ' (set as "POP3 user" in Hesk settings, ignoring to prevent email loops)';
    }

    // Email banned?
    if (hesk_isBannedEmail($contact['email'])) {
        return 'Removed ' . $contact['email'] . ' (banned email address)';
    }

    // Contact name
    if (strlen($contact['name'])) {
        if ( ! empty($contact['encoding']) ) {
            $contact['name'] = hesk_encodeUTF8($contact['name'], $contact['encoding']);
        }
        $contact['name'] = hesk_input($contact['name'], '', '', 1, 50) or $contact['name'] = '';
    } else {
        $contact['name'] = '';
    }

    // Retrieve (or create) followers
    if ($hesk_settings['customer_accounts'] === 0 || ($hesk_settings['customer_accounts'] === 1 && $hesk_settings['customer_accounts_required'] === 0)) {
        $contact['customer_id'] = hesk_get_or_create_follower($contact['email'], $contact['name']);
    } else {
        // Only allow existing customers to email their tickets/replies
        $contact['customer_id'] = hesk_get_customer_id_by_email($contact['email'], true);
        if ($contact['customer_id'] === null) {
            return 'Removed ' . $contact['email'] . ' (doesn\'t have a customer account)';
        }
    }

    return $contact;
} // END hesk_processEmailContact()


function hesk_encodeUTF8($in, $encoding)
{
    if (empty($in)) {
        return $in;
    }

    $encoding = strtoupper($encoding);

    // It's utf-8, just return it
    if ($encoding == 'UTF-8') {
        return $in;
    }

    // try with iconv
    if (function_exists('iconv') && ($out = iconv($encoding, 'UTF-8', $in)) !== false) {
        return $out;
    }

    // try with MB functions
    if (function_exists('mb_convert_encoding') && ($out = mb_convert_encoding($in, 'UTF-8', $encoding)) !== false) {
        return $out;
    }

    // last resort
    return hesk_iso8859_1_to_utf8($in);
} // END hesk_encodeUTF8()


function hesk_stripQuotedText($message)
{
	global $hesk_settings, $hesklang;

	// Stripping quoted text disabled?
	if ( ! $hesk_settings['strip_quoted'])
	{
		return $message;
	}

	// Loop through available languages and ty to find the tag
	foreach ($hesk_settings['languages'] as $language => $settings)
	{
		if ( ($found = strpos($message, $settings['hr']) ) !== false )
		{
			// "Reply above this line" tag found, strip quoted reply
			$message  = hesk_mb_substr($message, 0, $found);
            $message .= "\n" . $hesklang['qrr'];

            // Set language to the detected language
            hesk_setLanguage($language);
			break;
		}
	}

	return $message;
} // END hesk_stripQuotedText()


function hesk_isDoNotReplyEmail($tmpvar)
{
    // Check noreply email addresses
    if ( preg_match('/not?[\-_\.]?reply@/i', $tmpvar['email']) )
    {
        return true;
    }

    return false;
} // END hesk_isDoNotReplyEmail()


function hesk_isReturnedEmail($tmpvar)
{
	// Check mailer daemon email addresses
	if ( preg_match('/mail(er)?[\-_\.]?daemon@/i', $tmpvar['email']) )
	{
		return true;
	}

	// Check autoreply subjects
	if ( preg_match('/^[\[\(]?Auto(mat(ic|ed))?[ \-]?reply/i', $tmpvar['subject']) )
	{
		return true;
	}

	// Check out of office subjects
	if ( preg_match('/^Out of Office/i', $tmpvar['subject']) )
	{
		return true;
	}

	// Check delivery failed email subjects
	if (
	preg_match('/DELIVERY FAILURE/i', $tmpvar['subject']) ||
	preg_match('/Undelivered Mail Returned to Sender/i', $tmpvar['subject']) ||
	preg_match('/Delivery Status Notification \(Failure\)/i', $tmpvar['subject']) ||
	preg_match('/Returned mail\: see transcript for details/i', $tmpvar['subject']) ||
    preg_match('/^Undeliverable\:/i', $tmpvar['subject'])
	)
	{
		return true;
	}

	// Check Mail Delivery sender name
	if ( preg_match('/Mail[ \-_]?Delivery/i', $tmpvar['name']) )
	{
		return true;
	}

	// Check Delivery failed message
	if ( preg_match('/postmaster@/i', $tmpvar['email']) && preg_match('/Delivery has failed to these recipients/i', $tmpvar['message']) )
	{
		return true;
	}

	// No pattern detected, seems like this is not a returned email
	return false;

} // END hesk_isReturnedEmail()


function hesk_isEmailLoop($email, $message_hash)
{
	global $hesk_settings, $hesklang, $hesk_db_link;

	// If $hesk_settings['loop_hits'] is set to 0 this function is disabled
    if ( ! $hesk_settings['pipe_block_duplicate'] && ! $hesk_settings['loop_hits'])
    {
    	return false;
    }

	// Escape wildcards in email
	$email_like = hesk_dbEscape(hesk_dbLike($email));

	// Delete expired DB entries
	hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."pipe_loops` WHERE `dt` < (NOW() - INTERVAL ".intval($hesk_settings['loop_time'])." SECOND) ");

	// Check current entry
	$res = hesk_dbQuery("SELECT `hits`, `message_hash` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."pipe_loops` WHERE `email` LIKE '{$email_like}' LIMIT 1");

	// Any active entry*
	if (hesk_dbNumRows($res))
	{
		list($num, $md5) = hesk_dbFetchRow($res);

		$num++;

		// Number of emails in a time period reached?
		if ($hesk_settings['loop_hits'] && $num >= $hesk_settings['loop_hits'])
		{
            return "Email loop detected. Received {$num} emails from {$email} in {$hesk_settings['loop_time']} seconds.";
		}

        // Message exactly the same as in previous email?
        if ($hesk_settings['pipe_block_duplicate'] && $message_hash == $md5)
        {
            return "Duplicate email. An email with this exact message was already received from {$email} in the last {$hesk_settings['loop_time']} seconds.";
        }

		// Update DB entry
		hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."pipe_loops` SET `hits` = `hits` + 1, `message_hash` = '".hesk_dbEscape($message_hash)."' WHERE `email` LIKE '{$email_like}'");
	}
	else
	{
		// First instance, insert a new database row
		hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."pipe_loops` (`email`, `message_hash`) VALUES ('".hesk_dbEscape($email)."', '".hesk_dbEscape($message_hash)."')");
	}

	// No loop rule trigered
    return false;

} // END hesk_isEmailLoop()


function hesk_cleanExit($exit_reason = null)
{
    global $hesk_settings, $results;

    if ($hesk_settings['debug_mode']) {
        $hesk_settings['DEBUG_LOG']['PIPE'] = $exit_reason;
    }

	// Delete the temporary files
    if (is_array($results) && isset($results['tempdir']) && is_dir($results['tempdir'])) {
        deleteAll($results['tempdir']);
    }

	return NULL;
} // END hesk_cleanExit()

function hesk_notifyRejectedCustomer($email_info) {
    global $hesk_settings;

    $send_failure_email_rs = hesk_dbQuery("SELECT `id`, 
                    CASE
                        WHEN (`timestamp` + INTERVAL ".intval($hesk_settings['pipe_customer_rejection_email_cooldown_hours'])." HOUR) < NOW() THEN 1
                        ELSE 0 
                    END AS `should_send_email` 
                FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."pipe_rejections` 
                WHERE `email` = '".hesk_dbEscape($email_info['email'])."'");
    $send_failure_email = hesk_dbFetchAssoc($send_failure_email_rs);
    if ($send_failure_email === null || $send_failure_email['should_send_email']) {
        $possible_spam = false;
        if ($hesk_settings['notify_skip_spam']) {
            foreach ($hesk_settings['notify_spam_tags'] as $tag) {
                if ( strpos($email_info['subject'], $tag) !== false ) {
                    $possible_spam = true;
                    break;
                }
            }
        }

        if (!$possible_spam) {
            $rejection_template = $hesk_settings['customer_accounts_customer_self_register'] === 1 ?
                'email_rejected_can_self_register' :
                'email_rejected_cannot_self_register';
            hesk_sendCustomerTicket2EmailFailure($email_info, $rejection_template);
            if ($send_failure_email !== null) {
                hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."pipe_rejections` WHERE `id` = ".intval($send_failure_email['id']));
            }
            hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."pipe_rejections` (`timestamp`,`email`) VALUES (NOW(), '".hesk_dbEscape($email_info['email'])."')");
        }
    }
}
posting_functions.inc.php
wget 'https://lists2.roe3.org/hesk/inc/posting_functions.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

/*** FUNCTIONS ***/

function hesk_newTicket($ticket)
{
	global $hesk_settings, $hesklang, $hesk_db_link;
    require_once(HESK_PATH . 'inc/customer_accounts.inc.php');

    $primary_customer = hesk_get_customer_account_by_id($ticket['customer_id']);
    $name = $primary_customer['name'];
    $email = $primary_customer['email'];
    if ($primary_customer['verified'] !== 1) {
        if (isset($ticket['name'])) {
            $name = $ticket['name'];
        }
        if (isset($ticket['email'])) {
            $email = $ticket['email'];
        }
    }
    // Generate a subject if necessary
    if (hesk_mb_strlen($ticket['subject']) < 1)
    {
        $ticket['subject'] = sprintf($hesklang['default_subject'], $primary_customer['name']);
    }

	// If language is not set or default, set it to NULL
    if ( ! $hesk_settings['can_sel_lang'] || $hesklang['LANGUAGE'] == HESK_DEFAULT_LANGUAGE)
    {
        $ticket['language'] = HESK_DEFAULT_LANGUAGE;
        $language = "NULL";
    }
    else
    {
        $ticket['language'] = $hesklang['LANGUAGE'];
        $language = "'" . hesk_dbEscape($hesklang['LANGUAGE']) . "'";
    }

    $ticket['status'] = isset($ticket['status']) ? intval($ticket['status']) : 0;
    if ($ticket['status'] < 0) {
        $ticket['status'] = 0;
    }

    if ( ! isset($hesk_settings['priorities'])) {
        require_once(HESK_PATH . 'inc/priorities.inc.php');
    }

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

	// Prepare SQL for custom fields
	$custom_where = '';
	$custom_what  = '';

	for ($i=1; $i<=100; $i++)
	{
		$custom_where .= ", `custom{$i}`";
		$custom_what  .= ", '" . (isset($ticket['custom'.$i]) ? hesk_dbEscape($ticket['custom'.$i]) : '') . "'";
	}

    // Need to insert "addigned by" value?
    if (isset($ticket['assignedby']))
    {
        $ab_where = ', `assignedby` ';
        $ab_what  = ', ' . intval($ticket['assignedby']);
    }
    else
    {
        $ab_where = '';
        $ab_what  = '';
    }

    if (isset($ticket['due_date']) && $ticket['due_date'] != '') {
        $date = hesk_datepicker_get_date($ticket['due_date']);
        if ($date === false) {
            $due_date = 'NULL';
            $ticket['due_date'] = '';
        } else {
            $formatted_date = $date->format('Y-m-d');
            $due_date = "'" . hesk_dbEscape($formatted_date) . "'";
        }
    } else {
        $due_date = 'NULL';
        $ticket['due_date'] = '';
    }

    $locked = isset($ticket['locked']) ? 1 : 0;
    $closedat = isset($ticket['closedat']) ? 'NOW()' : 'NULL';
    $closedby = isset($ticket['closedby']) ? intval($ticket['closedby']) : 'NULL';

	// Insert ticket into database
	hesk_dbQuery("
	INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets`
	(
		`trackid`,
		`u_name`,
		`u_email`,
		`category`,
		`priority`,
		`subject`,
		`message`,
		`message_html`,
		`dt`,
		`lastchange`,
        `closedat`,
		`articles`,
		`ip`,
		`language`,
        `status`,
		`openedby`,
        `closedby`,
		`owner`,
        `locked`,
		`attachments`,
		`merged`,
		`history`,
		`due_date`
		{$custom_where}
        {$ab_where}
	)
	VALUES
	(
		'".hesk_dbEscape($ticket['trackid'])."',
		'".hesk_dbEscape( hesk_mb_substr($name, 0, 255) )."',
		'".hesk_dbEscape( hesk_mb_substr($email, 0, 1000) )."',
		'".intval($ticket['category'])."',
		'".intval($ticket['priority'])."',
		'".hesk_dbEscape( hesk_mb_substr($ticket['subject'], 0, 255) )."',
		'".hesk_dbEscape($ticket['message'])."',
		'".hesk_dbEscape($ticket['message_html'])."',
		NOW(),
		NOW(),
        {$closedat},
		".( isset($ticket['articles']) ? "'{$ticket['articles']}'" : 'NULL' ).",
		'".hesk_dbEscape(hesk_getClientIP())."',
		$language,
        '{$ticket['status']}',
		'".( isset($ticket['openedby']) ? intval($ticket['openedby']) : 0 )."',
        {$closedby},
		'".intval($ticket['owner'])."',
        '$locked',
		'".hesk_dbEscape($ticket['attachments'])."',
		'',
		'".hesk_dbEscape($ticket['history'])."',
		{$due_date}
		{$custom_what}
        {$ab_what}
	)
	");
    $ticket_id = hesk_dbInsertID();

    // Insert ticket/customer mapping(s)
    hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` (`ticket_id`, `customer_id`, `customer_type`)
        VALUES (".intval($ticket_id).", ".intval($ticket['customer_id']).", 'REQUESTER')");
    foreach ($ticket['follower_ids'] as $follower_id) {
        // Do we have the customer in the list of followers?  If so, skip it.
        if ($follower_id === $ticket['customer_id']) {
            continue;
        }

        hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` (`ticket_id`, `customer_id`, `customer_type`)
        VALUES (".intval($ticket_id).", ".intval($follower_id).", 'FOLLOWER')");
    }

    // Save the email ID map to ticket for later use
    if ( ! empty($ticket['email_id'])) {
        hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."email_id_to_ticket` (`email_id`, `ticket_id`, `from_hesk`) VALUES ('".hesk_dbEscape($ticket['email_id'])."', {$ticket_id}, 0)");
    }

	// Generate the array with ticket info that can be used in emails
	$info = array(
	'email'			=> $primary_customer['email'],
	'category'		=> $ticket['category'],
	'priority'		=> $ticket['priority'],
	'owner'			=> $ticket['owner'],
	'trackid'		=> $ticket['trackid'],
	'status'		=> $ticket['status'],
	'name'			=> addslashes($primary_customer['name']),
	'last_reply_by'	=> addslashes($primary_customer['name']),
	'subject'		=> $ticket['subject'],
	'message'		=> $ticket['message'],
    'message_html'  => stripslashes($ticket['message_html']),
	'attachments'	=> $ticket['attachments'],
	'dt'			=> hesk_date(),
	'lastchange'	=> hesk_date(),
    'due_date'      => hesk_format_due_date($ticket['due_date']),
	'id'			=> $ticket_id,
    'time_worked'   => '00:00:00',
    'language'      => $ticket['language'],
	);

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

    // Extra actions for achieving landmarks
    if (in_array($info['id'], array(100, 1000, 10000)))
    {
        hesk_PMtoMainAdmin($info['id']);
    }

    return hesk_ticketToPlain($info, 1);

} // END hesk_newTicket()


function hesk_cleanFileName($filename)
{
	$parts = pathinfo($filename);

    if ( ! isset($parts['extension']))
    {
        $parts['extension'] = 'unknown-file-type';
    }
    $parts['extension'] = preg_replace('/[^A-Za-z0-9\-_]/', '', $parts['extension']);

	if ( isset($parts['filename']) )
	{
		$filename = $parts['filename'];
	}
	// PHP < 5.2 needs special care
	elseif ( version_compare(PHP_VERSION, '5.2','<') )
	{
		$filename = rtrim( str_ireplace($parts['extension'], '', $filename), '.');
	}
	else
	{
		$filename = '';
	}

	$filename = str_replace( array( '%20', '+' ), '-', $filename );
	$filename = preg_replace('/[\s-]+/', '-', $filename);
	$filename = remove_accents($filename);
	$filename = preg_replace('/[^A-Za-z0-9\.\-_]/','', $filename);
	$filename = trim($filename, '-_');

	if ( strlen($filename) < 1 || strpos($filename, '.') === 0 )
	{
		$filename = mt_rand(10000,99999) . $filename;
	}

	return $filename . '.' . $parts['extension'];
} // END hesk_cleanFileName()


function hesk_getCategoryPriority($id)
{
	global $hesk_settings, $hesklang, $hesk_db_link;

	$priority = 3;

	// Does the category have a different default priority?
	$res = hesk_dbQuery("SELECT `priority` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` WHERE `id`=" . intval($id) . " LIMIT 1");
	if ( hesk_dbNumRows($res) == 1 )
	{
		$priority = hesk_dbResult($res);
	}

    if ( ! isset($hesk_settings['priorities'])) {
        require_once(HESK_PATH . 'inc/priorities.inc.php');
    }

    if ( ! hesk_is_valid_priority_id($priority)) {
        $priority = array_keys($hesk_settings['priorities'])[0];
    } else {
        $priority = intval($priority);
    }

	return $priority;

} // END hesk_getCategoryPriority()


function hesk_verifyCategory($any_type=0)
{
	global $hesk_settings, $hesklang, $hesk_db_link, $hesk_error_buffer, $tmpvar;

	// Verify just by public or any category type?
	$type = $any_type ? " 1 " : " `type`='0' ";

	// Does the category exist?
	$res = hesk_dbQuery("SELECT `name`, `autoassign` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` WHERE `id`='" . $tmpvar['category'] . "' AND {$type} LIMIT 1");
	if ( hesk_dbNumRows($res) == 1 )
	{
		$hesk_settings['category_data'][$tmpvar['category']] = hesk_dbFetchAssoc($res);
		return true;
	}

	// OK, something wrong with the category. Get a list of categories to check few things
	$res = hesk_dbQuery("SELECT `id`, `name`, `autoassign` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` WHERE {$type} ORDER BY `id` ASC");
	$num = hesk_dbNumRows($res);

	// If more than 1 choice is available, let the user choose
	if ($num > 1)
	{
		$hesk_error_buffer['category'] = $hesklang['sel_app_cat'];
		return false;
	}

	// Exactly one category is available, use it
	elseif ($num == 1)
	{
		$tmp = hesk_dbFetchAssoc($res);
		$tmpvar['category'] = $tmp['id'];
		$hesk_settings['category_data'][$tmpvar['category']] = $tmp;
		return true;
	}

	// No category is available, use the first one we find (should be ID 1)
	else
	{
		$res = hesk_dbQuery("SELECT `id`, `name`, `autoassign` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` ORDER BY `id` ASC LIMIT 1");

		if ( hesk_dbNumRows($res) == 1 )
		{
			$tmp = hesk_dbFetchAssoc($res);
			$tmpvar['category'] = $tmp['id'];
			$hesk_settings['category_data'][$tmpvar['category']] = $tmp;
		}
		else
		{
			// What the ...? No categories exist??? You know what, just error out...
			hesk_error($hesklang['int_error'] . ': ' . $hesklang['cat_not_found']);
		}
	}

} // END hesk_verifyCategory()


// The following code has been borrowed from Wordpress
// Credits: http://wordpress.org
function remove_accents($string) {
	if ( !preg_match('/[\x80-\xff]/', $string) )
		return $string;

	if (seems_utf8($string)) {
		$chars = array(
		// Decompositions for Latin-1 Supplement
		chr(194).chr(170) => 'a', chr(194).chr(186) => 'o',
		chr(195).chr(128) => 'A', chr(195).chr(129) => 'A',
		chr(195).chr(130) => 'A', chr(195).chr(131) => 'A',
		chr(195).chr(132) => 'A', chr(195).chr(133) => 'A',
		chr(195).chr(134) => 'AE',chr(195).chr(135) => 'C',
		chr(195).chr(136) => 'E', chr(195).chr(137) => 'E',
		chr(195).chr(138) => 'E', chr(195).chr(139) => 'E',
		chr(195).chr(140) => 'I', chr(195).chr(141) => 'I',
		chr(195).chr(142) => 'I', chr(195).chr(143) => 'I',
		chr(195).chr(144) => 'D', chr(195).chr(145) => 'N',
		chr(195).chr(146) => 'O', chr(195).chr(147) => 'O',
		chr(195).chr(148) => 'O', chr(195).chr(149) => 'O',
		chr(195).chr(150) => 'O', chr(195).chr(153) => 'U',
		chr(195).chr(154) => 'U', chr(195).chr(155) => 'U',
		chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y',
		chr(195).chr(158) => 'TH',chr(195).chr(159) => 's',
		chr(195).chr(160) => 'a', chr(195).chr(161) => 'a',
		chr(195).chr(162) => 'a', chr(195).chr(163) => 'a',
		chr(195).chr(164) => 'a', chr(195).chr(165) => 'a',
		chr(195).chr(166) => 'ae',chr(195).chr(167) => 'c',
		chr(195).chr(168) => 'e', chr(195).chr(169) => 'e',
		chr(195).chr(170) => 'e', chr(195).chr(171) => 'e',
		chr(195).chr(172) => 'i', chr(195).chr(173) => 'i',
		chr(195).chr(174) => 'i', chr(195).chr(175) => 'i',
		chr(195).chr(176) => 'd', chr(195).chr(177) => 'n',
		chr(195).chr(178) => 'o', chr(195).chr(179) => 'o',
		chr(195).chr(180) => 'o', chr(195).chr(181) => 'o',
		chr(195).chr(182) => 'o', chr(195).chr(184) => 'o',
		chr(195).chr(185) => 'u', chr(195).chr(186) => 'u',
		chr(195).chr(187) => 'u', chr(195).chr(188) => 'u',
		chr(195).chr(189) => 'y', chr(195).chr(190) => 'th',
		chr(195).chr(191) => 'y', chr(195).chr(152) => 'O',
		// Decompositions for Latin Extended-A
		chr(196).chr(128) => 'A', chr(196).chr(129) => 'a',
		chr(196).chr(130) => 'A', chr(196).chr(131) => 'a',
		chr(196).chr(132) => 'A', chr(196).chr(133) => 'a',
		chr(196).chr(134) => 'C', chr(196).chr(135) => 'c',
		chr(196).chr(136) => 'C', chr(196).chr(137) => 'c',
		chr(196).chr(138) => 'C', chr(196).chr(139) => 'c',
		chr(196).chr(140) => 'C', chr(196).chr(141) => 'c',
		chr(196).chr(142) => 'D', chr(196).chr(143) => 'd',
		chr(196).chr(144) => 'D', chr(196).chr(145) => 'd',
		chr(196).chr(146) => 'E', chr(196).chr(147) => 'e',
		chr(196).chr(148) => 'E', chr(196).chr(149) => 'e',
		chr(196).chr(150) => 'E', chr(196).chr(151) => 'e',
		chr(196).chr(152) => 'E', chr(196).chr(153) => 'e',
		chr(196).chr(154) => 'E', chr(196).chr(155) => 'e',
		chr(196).chr(156) => 'G', chr(196).chr(157) => 'g',
		chr(196).chr(158) => 'G', chr(196).chr(159) => 'g',
		chr(196).chr(160) => 'G', chr(196).chr(161) => 'g',
		chr(196).chr(162) => 'G', chr(196).chr(163) => 'g',
		chr(196).chr(164) => 'H', chr(196).chr(165) => 'h',
		chr(196).chr(166) => 'H', chr(196).chr(167) => 'h',
		chr(196).chr(168) => 'I', chr(196).chr(169) => 'i',
		chr(196).chr(170) => 'I', chr(196).chr(171) => 'i',
		chr(196).chr(172) => 'I', chr(196).chr(173) => 'i',
		chr(196).chr(174) => 'I', chr(196).chr(175) => 'i',
		chr(196).chr(176) => 'I', chr(196).chr(177) => 'i',
		chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij',
		chr(196).chr(180) => 'J', chr(196).chr(181) => 'j',
		chr(196).chr(182) => 'K', chr(196).chr(183) => 'k',
		chr(196).chr(184) => 'k', chr(196).chr(185) => 'L',
		chr(196).chr(186) => 'l', chr(196).chr(187) => 'L',
		chr(196).chr(188) => 'l', chr(196).chr(189) => 'L',
		chr(196).chr(190) => 'l', chr(196).chr(191) => 'L',
		chr(197).chr(128) => 'l', chr(197).chr(129) => 'L',
		chr(197).chr(130) => 'l', chr(197).chr(131) => 'N',
		chr(197).chr(132) => 'n', chr(197).chr(133) => 'N',
		chr(197).chr(134) => 'n', chr(197).chr(135) => 'N',
		chr(197).chr(136) => 'n', chr(197).chr(137) => 'N',
		chr(197).chr(138) => 'n', chr(197).chr(139) => 'N',
		chr(197).chr(140) => 'O', chr(197).chr(141) => 'o',
		chr(197).chr(142) => 'O', chr(197).chr(143) => 'o',
		chr(197).chr(144) => 'O', chr(197).chr(145) => 'o',
		chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe',
		chr(197).chr(148) => 'R',chr(197).chr(149) => 'r',
		chr(197).chr(150) => 'R',chr(197).chr(151) => 'r',
		chr(197).chr(152) => 'R',chr(197).chr(153) => 'r',
		chr(197).chr(154) => 'S',chr(197).chr(155) => 's',
		chr(197).chr(156) => 'S',chr(197).chr(157) => 's',
		chr(197).chr(158) => 'S',chr(197).chr(159) => 's',
		chr(197).chr(160) => 'S', chr(197).chr(161) => 's',
		chr(197).chr(162) => 'T', chr(197).chr(163) => 't',
		chr(197).chr(164) => 'T', chr(197).chr(165) => 't',
		chr(197).chr(166) => 'T', chr(197).chr(167) => 't',
		chr(197).chr(168) => 'U', chr(197).chr(169) => 'u',
		chr(197).chr(170) => 'U', chr(197).chr(171) => 'u',
		chr(197).chr(172) => 'U', chr(197).chr(173) => 'u',
		chr(197).chr(174) => 'U', chr(197).chr(175) => 'u',
		chr(197).chr(176) => 'U', chr(197).chr(177) => 'u',
		chr(197).chr(178) => 'U', chr(197).chr(179) => 'u',
		chr(197).chr(180) => 'W', chr(197).chr(181) => 'w',
		chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y',
		chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z',
		chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z',
		chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z',
		chr(197).chr(190) => 'z', chr(197).chr(191) => 's',
		// Decompositions for Latin Extended-B
		chr(200).chr(152) => 'S', chr(200).chr(153) => 's',
		chr(200).chr(154) => 'T', chr(200).chr(155) => 't',
		// Euro Sign
		chr(226).chr(130).chr(172) => 'E',
		// GBP (Pound) Sign
		chr(194).chr(163) => '',
		// Vowels with diacritic (Vietnamese)
		// unmarked
		chr(198).chr(160) => 'O', chr(198).chr(161) => 'o',
		chr(198).chr(175) => 'U', chr(198).chr(176) => 'u',
		// grave accent
		chr(225).chr(186).chr(166) => 'A', chr(225).chr(186).chr(167) => 'a',
		chr(225).chr(186).chr(176) => 'A', chr(225).chr(186).chr(177) => 'a',
		chr(225).chr(187).chr(128) => 'E', chr(225).chr(187).chr(129) => 'e',
		chr(225).chr(187).chr(146) => 'O', chr(225).chr(187).chr(147) => 'o',
		chr(225).chr(187).chr(156) => 'O', chr(225).chr(187).chr(157) => 'o',
		chr(225).chr(187).chr(170) => 'U', chr(225).chr(187).chr(171) => 'u',
		chr(225).chr(187).chr(178) => 'Y', chr(225).chr(187).chr(179) => 'y',
		// hook
		chr(225).chr(186).chr(162) => 'A', chr(225).chr(186).chr(163) => 'a',
		chr(225).chr(186).chr(168) => 'A', chr(225).chr(186).chr(169) => 'a',
		chr(225).chr(186).chr(178) => 'A', chr(225).chr(186).chr(179) => 'a',
		chr(225).chr(186).chr(186) => 'E', chr(225).chr(186).chr(187) => 'e',
		chr(225).chr(187).chr(130) => 'E', chr(225).chr(187).chr(131) => 'e',
		chr(225).chr(187).chr(136) => 'I', chr(225).chr(187).chr(137) => 'i',
		chr(225).chr(187).chr(142) => 'O', chr(225).chr(187).chr(143) => 'o',
		chr(225).chr(187).chr(148) => 'O', chr(225).chr(187).chr(149) => 'o',
		chr(225).chr(187).chr(158) => 'O', chr(225).chr(187).chr(159) => 'o',
		chr(225).chr(187).chr(166) => 'U', chr(225).chr(187).chr(167) => 'u',
		chr(225).chr(187).chr(172) => 'U', chr(225).chr(187).chr(173) => 'u',
		chr(225).chr(187).chr(182) => 'Y', chr(225).chr(187).chr(183) => 'y',
		// tilde
		chr(225).chr(186).chr(170) => 'A', chr(225).chr(186).chr(171) => 'a',
		chr(225).chr(186).chr(180) => 'A', chr(225).chr(186).chr(181) => 'a',
		chr(225).chr(186).chr(188) => 'E', chr(225).chr(186).chr(189) => 'e',
		chr(225).chr(187).chr(132) => 'E', chr(225).chr(187).chr(133) => 'e',
		chr(225).chr(187).chr(150) => 'O', chr(225).chr(187).chr(151) => 'o',
		chr(225).chr(187).chr(160) => 'O', chr(225).chr(187).chr(161) => 'o',
		chr(225).chr(187).chr(174) => 'U', chr(225).chr(187).chr(175) => 'u',
		chr(225).chr(187).chr(184) => 'Y', chr(225).chr(187).chr(185) => 'y',
		// acute accent
		chr(225).chr(186).chr(164) => 'A', chr(225).chr(186).chr(165) => 'a',
		chr(225).chr(186).chr(174) => 'A', chr(225).chr(186).chr(175) => 'a',
		chr(225).chr(186).chr(190) => 'E', chr(225).chr(186).chr(191) => 'e',
		chr(225).chr(187).chr(144) => 'O', chr(225).chr(187).chr(145) => 'o',
		chr(225).chr(187).chr(154) => 'O', chr(225).chr(187).chr(155) => 'o',
		chr(225).chr(187).chr(168) => 'U', chr(225).chr(187).chr(169) => 'u',
		// dot below
		chr(225).chr(186).chr(160) => 'A', chr(225).chr(186).chr(161) => 'a',
		chr(225).chr(186).chr(172) => 'A', chr(225).chr(186).chr(173) => 'a',
		chr(225).chr(186).chr(182) => 'A', chr(225).chr(186).chr(183) => 'a',
		chr(225).chr(186).chr(184) => 'E', chr(225).chr(186).chr(185) => 'e',
		chr(225).chr(187).chr(134) => 'E', chr(225).chr(187).chr(135) => 'e',
		chr(225).chr(187).chr(138) => 'I', chr(225).chr(187).chr(139) => 'i',
		chr(225).chr(187).chr(140) => 'O', chr(225).chr(187).chr(141) => 'o',
		chr(225).chr(187).chr(152) => 'O', chr(225).chr(187).chr(153) => 'o',
		chr(225).chr(187).chr(162) => 'O', chr(225).chr(187).chr(163) => 'o',
		chr(225).chr(187).chr(164) => 'U', chr(225).chr(187).chr(165) => 'u',
		chr(225).chr(187).chr(176) => 'U', chr(225).chr(187).chr(177) => 'u',
		chr(225).chr(187).chr(180) => 'Y', chr(225).chr(187).chr(181) => 'y',
		// Vowels with diacritic (Chinese, Hanyu Pinyin)
		chr(201).chr(145) => 'a',
		// macron
		chr(199).chr(149) => 'U', chr(199).chr(150) => 'u',
		// acute accent
		chr(199).chr(151) => 'U', chr(199).chr(152) => 'u',
		// caron
		chr(199).chr(141) => 'A', chr(199).chr(142) => 'a',
		chr(199).chr(143) => 'I', chr(199).chr(144) => 'i',
		chr(199).chr(145) => 'O', chr(199).chr(146) => 'o',
		chr(199).chr(147) => 'U', chr(199).chr(148) => 'u',
		chr(199).chr(153) => 'U', chr(199).chr(154) => 'u',
		// grave accent
		chr(199).chr(155) => 'U', chr(199).chr(156) => 'u',
		);

		$string = strtr($string, $chars);
	} else {
		// Assume ISO-8859-1 if not UTF-8
		$chars['in'] = chr(128).chr(131).chr(138).chr(142).chr(154).chr(158)
			.chr(159).chr(162).chr(165).chr(181).chr(192).chr(193).chr(194)
			.chr(195).chr(196).chr(197).chr(199).chr(200).chr(201).chr(202)
			.chr(203).chr(204).chr(205).chr(206).chr(207).chr(209).chr(210)
			.chr(211).chr(212).chr(213).chr(214).chr(216).chr(217).chr(218)
			.chr(219).chr(220).chr(221).chr(224).chr(225).chr(226).chr(227)
			.chr(228).chr(229).chr(231).chr(232).chr(233).chr(234).chr(235)
			.chr(236).chr(237).chr(238).chr(239).chr(241).chr(242).chr(243)
			.chr(244).chr(245).chr(246).chr(248).chr(249).chr(250).chr(251)
			.chr(252).chr(253).chr(255);

		$chars['out'] = "EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy";

		$string = strtr($string, $chars['in'], $chars['out']);
		$double_chars['in'] = array(chr(140), chr(156), chr(198), chr(208), chr(222), chr(223), chr(230), chr(240), chr(254));
		$double_chars['out'] = array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th');
		$string = str_replace($double_chars['in'], $double_chars['out'], $string);
	}

	return $string;
}


function seems_utf8($str) {
	$length = strlen($str);
	for ($i=0; $i < $length; $i++) {
		$c = ord($str[$i]);
		if ($c < 0x80) $n = 0; # 0bbbbbbb
		elseif (($c & 0xE0) == 0xC0) $n=1; # 110bbbbb
		elseif (($c & 0xF0) == 0xE0) $n=2; # 1110bbbb
		elseif (($c & 0xF8) == 0xF0) $n=3; # 11110bbb
		elseif (($c & 0xFC) == 0xF8) $n=4; # 111110bb
		elseif (($c & 0xFE) == 0xFC) $n=5; # 1111110b
		else return false; # Does not match any model
		for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ?
			if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80))
				return false;
		}
	}
	return true;
}


function utf8_uri_encode( $utf8_string, $length = 0 ) {
	$unicode = '';
	$values = array();
	$num_octets = 1;
	$unicode_length = 0;

	$string_length = strlen( $utf8_string );
	for ($i = 0; $i < $string_length; $i++ ) {

		$value = ord( $utf8_string[ $i ] );

		if ( $value < 128 ) {
			if ( $length && ( $unicode_length >= $length ) )
				break;
			$unicode .= chr($value);
			$unicode_length++;
		} else {
			if ( count( $values ) == 0 ) $num_octets = ( $value < 224 ) ? 2 : 3;

			$values[] = $value;

			if ( $length && ( $unicode_length + ($num_octets * 3) ) > $length )
				break;
			if ( count( $values ) == $num_octets ) {
				if ($num_octets == 3) {
					$unicode .= '%' . dechex($values[0]) . '%' . dechex($values[1]) . '%' . dechex($values[2]);
					$unicode_length += 9;
				} else {
					$unicode .= '%' . dechex($values[0]) . '%' . dechex($values[1]);
					$unicode_length += 6;
				}

				$values = array();
				$num_octets = 1;
			}
		}
	}

	return $unicode;
}

prepare_ticket_export.inc.php
wget 'https://lists2.roe3.org/hesk/inc/prepare_ticket_export.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

/* Acceptable $sort values and default asc(1)/desc(0) setting */
$sort_possible = array(
'trackid' 		=> 1,
'lastchange' 	=> 0,
'name' 			=> 1,
'subject' 		=> 1,
'status' 		=> 1,
'lastreplier' 	=> 1,
'priority' 		=> 1,
'category' 		=> 1,
'dt' 			=> 0,
'id' 			=> 1,
);

// These values should have collate appended in SQL
$sort_collation = array(
'name',
'subject',
);

// DATE
$sql .= " AND DATE(`dt`) BETWEEN '" . hesk_dbEscape($date_from) . "' AND '" . hesk_dbEscape($date_to) . "' ";


// Start the order by part of the SQL query
$sql .= " ORDER BY ";

/* Sort by which field? */
if (isset($_GET['sort']) && ! is_array($_GET['sort']) && isset($sort_possible[$_GET['sort']]))
{
	$sort = hesk_input($_GET['sort']);

    $sql .= ' `'.hesk_dbEscape($sort).'` ';

    // Need to set MySQL collation?
    if ( in_array($_GET['sort'], $sort_collation) )
    {
    	$sql .= " COLLATE '" . hesk_dbCollate() . "' ";
    }
}
else
{
	/* Default sorting by ticket status */
    $sql .= ' `id` ';
    $sort = 'id';
}

/* Ascending or Descending? */
if (isset($_GET['asc']) && intval($_GET['asc'])==0)
{
    $sql .= ' DESC ';
    $asc = 0;
    $asc_rev = 1;

    $sort_possible[$sort] = 1;
}
else
{
    $sql .= ' ASC ';
    $asc = 1;
    $asc_rev = 0;
    if (!isset($_GET['asc']))
    {
    	$is_default = 1;
    }

    $sort_possible[$sort] = 0;
}

// Options
$history = hesk_GET('history') ? 1 : 0;
$replies = hesk_GET('replies') ? 1 : 0;

# Uncomment for debugging purposes
# echo "SQL: $sql<br>";
prepare_ticket_search.inc.php
wget 'https://lists2.roe3.org/hesk/inc/prepare_ticket_search.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

$tmp = intval( hesk_GET('limit') );
$maxresults = ($tmp > 0) ? $tmp : $hesk_settings['max_listings'];

$tmp = intval( hesk_GET('page', 1) );
$page = ($tmp > 1) ? $tmp : 1;

/* Acceptable $sort values and default asc(1)/desc(0) setting */
$sort_possible = array();
foreach (array_keys($hesk_settings['possible_ticket_list']) as $key)
{
	$sort_possible[$key] = 1;
}
$sort_possible['priority'] = 1;
$sort_possible['dt'] = 0;
$sort_possible['lastchange'] = 0;

/* These values should have collate appended in SQL */
$sort_collation = array(
'name',
'subject',
);
for ($i=1;$i<=100;$i++) {
    $sort_collation[] = 'custom'.$i;
}

/* Acceptable $group values and default asc(1)/desc(0) setting */
$group_possible = array(
'owner' 		=> 1,
'priority' 		=> 1,
'category' 		=> 1,
);
/* Start the order by part of the SQL query */
$sql .= " ORDER BY ";

// Group parameter
$group = hesk_GET('g');
if ( ! isset($group_possible[$group]))
{
    $group = '';
}

// Sort parameter
$sort = hesk_GET('sort', 'status');
if ( ! isset($sort_possible[$sort]))
{
    $sort = 'status';
}

// Group tickets?
if ($group != '')
{
    if ($group == 'priority' && $sort == 'priority')
    {
		// No need to group by priority if we are already sorting by priority
    }
    elseif ($group == 'owner')
    {
		// If group by owner place own tickets on top
		$sql .= " CASE WHEN `owner` = '".intval($_SESSION['id'])."' THEN 1 ELSE 0 END DESC, `owner` ASC, ";
    }
    elseif ($group == 'category' && $sort == 'category')
    {
        // No need to group by category if we are already sorting by category
    }
    elseif ($group == 'category')
    {
        // Get list of categories
        $hesk_settings['categories'] = array();
        $res2 = hesk_dbQuery('SELECT `id`, `name` FROM `'.hesk_dbEscape($hesk_settings['db_pfix']).'categories` WHERE ' . hesk_myCategories('id') . ' ORDER BY `cat_order` ASC');
        while ($row=hesk_dbFetchAssoc($res2))
        {
            $hesk_settings['categories'][$row['id']] = $row['name'];
        }

        // Make sure categories are in correct order
        $sql .= ' FIELD(`category`, ' . preg_replace('/[^0-9,]/', '', implode(',' , array_keys($hesk_settings['categories']))) . '), ';
    }
    else
    {
	    $sql .= ' `'.hesk_dbEscape($group).'` ';
	    $sql .= $group_possible[$group] ? 'ASC, ' : 'DESC, ';
    }
}

// Show critical tickets always on top? Default: yes
$cot = hesk_GET('cot') == 1 ? 1 : 0;
if (!$cot)
{
	$sql .= " CASE WHEN `priority` = '0' THEN 1 ELSE 0 END DESC , ";
}

// Prepare sorting
if ($sort == 'category')
{
    // Get list of categories
    $hesk_settings['categories'] = array();
    $res2 = hesk_dbQuery('SELECT `id`, `name` FROM `'.hesk_dbEscape($hesk_settings['db_pfix']).'categories` WHERE ' . hesk_myCategories('id') . ' ORDER BY `cat_order` ASC');
    while ($row=hesk_dbFetchAssoc($res2))
    {
        $hesk_settings['categories'][$row['id']] = $row['name'];
    }

    // Make sure categories are in correct order
    $sql .= ' FIELD(`category`, ' . preg_replace('/[^0-9,]/', '', implode(',' , array_keys($hesk_settings['categories']))) . ') ';
}
else
{
    if ($sort === 'lastreplier') {
        $sql .= " CASE WHEN `lastreplier` = '0' THEN COALESCE(`lastreplier_customer`.`name`, `customer`.`name`) ELSE `lastreplier_staff`.`name` END ";
    } elseif ($sort === 'name') {
        $sql .= " COALESCE(`customer`.`name`, '".hesk_dbEscape($hesklang['anon_name'])."') ";
    } elseif ($sort === 'priority') {
        $sql .= ' `priority_order` ';
    } else {
        $sql .= ' `'.hesk_dbEscape($sort).'` ';
    }


    // Need to set MySQL collation?
    if ( in_array($sort, $sort_collation) )
    {
    	$sql .= " COLLATE '" . hesk_dbCollate() . "' ";
    }
}

/* Ascending or Descending? */
if (isset($_GET['asc']) && intval($_GET['asc'])==0)
{
    $sql .= ' DESC ';
    $asc = 0;
    $asc_rev = 1;

    $sort_possible[$sort] = 1;
}
else
{
    $sql .= ' ASC ';
    $asc = 1;
    $asc_rev = 0;
    if (!isset($_GET['asc']))
    {
    	$is_default = 1;
    }

    $sort_possible[$sort] = 0;
}

/* In the end same results should always be sorted by priority */
if ($sort != 'priority')
{
	$sql .= ' , `priority_order` DESC ';
}

// Always sort by ID to make sure chached results don't cause different results between pages
if ($sort != 'id') {
    $sql .= ' , `id` ASC ';
}

# Uncomment for debugging purposes
# echo "SQL: $sql<br>";
print_group.inc.php
wget 'https://lists2.roe3.org/hesk/inc/print_group.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

if ( ! function_exists('hesk_get_priority_name'))
{
	require_once(HESK_PATH . 'inc/priorities.inc.php');
}

/* Group tickets into tables */
if ($group == 'owner')
{
	if ($ticket['owner'] != $group_tmp)
	{
		$group_tmp = $ticket['owner'];

		if ($is_table)
		{
			echo '</tbody></table></div>';
		}

		if ($space)
		{
			echo '&nbsp;<br />';
		}

		if (empty($group_tmp) || ! isset($admins[$group_tmp]))
		{
			echo '<p>'.$hesklang['gbou'].'</p>';
			$space++;
		}
		else
		{
			if ($group_tmp == $_SESSION['id'])
			{
				echo '<p>'.$hesklang['gbom'].'</p>';
				$space++;
			}
			else
			{
				echo '<p>'.sprintf($hesklang['gboo'],$admins[$group_tmp]).'</p>';
				$space++;
			}
		}

		hesk_print_list_head();
		$is_table = 1;
	}
} // END if 'owner'

elseif ($group == 'priority')
{
	$tmp = hesk_get_priority_name($ticket['priority']);

	if ($ticket['priority'] != $group_tmp)
	{
		$group_tmp = $ticket['priority'];

		if ($is_table)
		{
			echo '</tbody></table></div>';
		}

		if ($space)
		{
			echo '&nbsp;<br />';
		}

		echo '<p>'.$hesklang['priority'].': <b>'.$tmp.'</b></p>';
		$space++;

		hesk_print_list_head();
		$is_table = 1;
	}
} // END elseif 'priority'

else
{
	if ($ticket['category'] != $group_tmp)
	{
		$group_tmp = $ticket['category'];

		if ($is_table)
		{
			echo '</tbody></table></div>';
		}

		if ($space)
		{
			echo '&nbsp;<br />';
		}


        $tmp = isset($hesk_settings['categories'][$group_tmp]) ? $hesk_settings['categories'][$group_tmp] : '('.$hesklang['unknown'].')';

		echo '<p>'.$hesklang['category'].': <b>'.$tmp.'</b></p>';
		$space++;

		hesk_print_list_head();
		$is_table = 1;
	}
} // END else ('category')
print_template.inc.php
wget 'https://lists2.roe3.org/hesk/inc/print_template.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

if ( ! function_exists('hesk_get_priority_name'))
{
    require_once(HESK_PATH . 'inc/priorities.inc.php');
}

$formatted_tickets = array();
foreach ($tickets as $ticket) {
    // Ticket priority
    $priority_name = hesk_get_priority_name($ticket['priority']);
    $ticket['priority'] = ($ticket['priority'] == 0 || $ticket['priority'] == 1) ? '<b>'.$priority_name.'</b>' : $priority_name;

    // Replies
    $replies = array();
    foreach ($ticket['replies'] as $reply) {
        $reply['dt'] = hesk_date($reply['dt'], true);
        $reply['message'] = hesk_unhortenUrl($reply['message']);
        if ($reply['name'] === null) {
            if (intval($reply['staffid']) > 0) {
                $reply['name'] = $hesklang['staff_deleted'];
            } else {
                $reply['name'] = $hesklang['anon_name'];
            }
        }

        $replies[] = $reply;
    }
    $ticket['replies'] = $replies;

    // Set last replier name
    if ($ticket['lastreplier']) {
        if (empty($ticket['repliername'])) {
            $ticket['repliername'] = $hesklang['staff'];
        }
    } else {
        if (!function_exists('hesk_get_customers_for_ticket')) {
            require_once(HESK_PATH . 'inc/customer_accounts.inc.php');
        }

        // Get the last reply and pull its name
        $reply_count = count($ticket['replies']);
        if ($reply_count > 0) {
            $last_reply = $ticket['replies'][$reply_count - 1];

            $customers = hesk_get_customers_for_ticket($ticket['id']);
            $customer_names = array_map(function($customer) { return $customer['name']; }, $customers);
            $ticket['repliername'] = (strlen($last_reply['name']) ? $last_reply['name'] : ( ! empty($last_reply['email']) ? $last_reply['email'] : $hesklang['pde'] ));
        } else {
            $ticket['repliername'] = '';
        }
    }

    // Other variables that need processing
    $ticket['dt'] = hesk_date($ticket['dt'], true);
    $ticket['lastchange'] = hesk_date($ticket['lastchange'], true);
    $ticket['due_date'] = hesk_format_due_date($ticket['due_date']);
    $random=mt_rand(10000,99999);

    $ticket['status'] = hesk_get_status_name($ticket['status']);

    if ($ticket['owner'] && ! empty($_SESSION['id'])) {
        $owner_array = hesk_getStaffNameArray($ticket['owner']);
        $ticket['owner'] = $owner_array['name'];
    } else {
        $ticket['owner'] = '';
    }

    // Custom fields
    $custom_fields = array();
    foreach ($hesk_settings['custom_fields'] as $k=>$v) {
        if (($v['use'] == 1 || (!empty($_SESSION['id']) && $v['use'] == 2)) && (strlen($ticket[$k]) || hesk_is_custom_field_in_category($k, $ticket['category']))) {
            if ($v['type'] == 'date') {
                $ticket[$k] = hesk_custom_date_display_format($ticket[$k], $v['value']['date_format']);
            }

            $custom_fields[] = array(
                'name' => $v['name:'],
                'value' => hesk_unhortenUrl($ticket[$k])
            );
        }
    }
    $ticket['custom_fields'] = $custom_fields;

    // Initial ticket message
    if ($ticket['message'] != '')
    {
        $ticket['message'] = hesk_unhortenUrl($ticket['message']);
    }

    $formatted_tickets[] = $ticket;
}

$hesk_settings['render_template'](TEMPLATE_PATH . 'print-ticket.php', array(
    'tickets' => $formatted_tickets,
    'showStaffOnlyFields' => !empty($_SESSION['id'])
), true, true);
print_tickets.inc.php
wget 'https://lists2.roe3.org/hesk/inc/print_tickets.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');} 

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

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

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

// Prepare total counts that we will use later
$totals = array(
    'all' => 0,
    'open' => 0,
    'resolved' => 0,
    'filtered' => array(
        'all' => 0,
        'open' => 0,
        'assigned_to_me' => 0,
        'assigned_to_others' => 0,
        'assigned_to_others_by_me' => 0,
        'unassigned' => 0,
        'bookmarks' => 0,
        'due_soon' => 0,
        'overdue' => 0,
        'by_status' => array(),
        'collaborator' => 0,
        'collaborator_assigned_to_me' => 0,
        'collaborator_assigned_to_others' => 0,
        'collaborator_assigned_to_others_by_me' => 0,
        'collaborator_unassigned' => 0,
    ),
);

// Let's check some permissions
$can_view_unassigned = hesk_checkPermission('can_view_unassigned',0);
$can_view_ass_others = hesk_checkPermission('can_view_ass_others',0);
$can_view_ass_by = hesk_checkPermission('can_view_ass_by',0);

// Is this a quick link?
$is_quick_link = hesk_GET('ql', false);

// This will get number of ALL tickets this user has access to
$sql = "SELECT COUNT(DISTINCT `ticket`.`id`) AS `cnt`, IF (`status` = 3, 1, 0) AS `is_resolved`
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` AS `ticket`
        LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_collaborator` AS `w` ON (`ticket`.`id` = `w`.`ticket_id` AND `w`.`user_id` = ".intval($_SESSION['id']).")
        WHERE
        (
            `w`.`user_id`=".intval($_SESSION['id'])."
            OR
            (".hesk_myOwnership().")
        )
        AND ".hesk_myCategories()."
        GROUP BY `is_resolved`";
$res = hesk_dbQuery($sql);

while ($row = hesk_dbFetchAssoc($res))
{
    // Total tickets found
    $totals['all'] += $row['cnt'];

    // Total by status
    if ($row['is_resolved'])
    {
        $totals['resolved'] += $row['cnt'];
    }
    else
    {
        $totals['open'] = $row['cnt'];
    }
}

$sql_final = ''; // SQL that fetches ticket data from the database
$sql_count = ''; // SQL that runs a quick count of tickets by status, due date and ownership
$sql_collaborator = ''; // SQL that runs a quick count of collaborated tickets

$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` = `ticket`.`id`";
$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` = `ticket`.`id`
        AND COALESCE(`customer_emails`.`email`, '') <> ''";

// This SQL code will be used to retrieve results
$sql_final = "SELECT
`ticket`.`id` AS `id`,
`trackid`,
COALESCE(`customer`.`name`, '".hesk_dbEscape($hesklang['anon_name'])."') AS `name`,
COALESCE(`customer`.`email`, '".hesk_dbEscape($hesklang['anon_email'])."') AS `email`,
({$sql_customer_count}) AS `customer_count`,
({$sql_email_count}) AS `email_count`,
`category`,
`priority`,
`priority_order` AS `vv`,
`subject`,
LEFT(`message`, 400) AS `message`,
`dt`,
`lastchange`,
`firstreply`,
`closedat`,
`status`,
`openedby`,
`firstreplyby`,
`closedby`,
`ticket`.`replies`,
`staffreplies`,
`owner`,
`time_worked`,
`due_date`,
`lastreplier`,
`lastreplier_customer`.`name` AS `lastreplier_customername`,
`replierid`,
`archive`,
`locked`,
CASE WHEN `bookmarks`.`ticket_id` IS NOT NULL THEN 1 ELSE 0 END AS `is_bookmark`
";

foreach ($hesk_settings['custom_fields'] as $k=>$v)
{
	if ($v['use'])
	{
		$sql_final .= ", `".$k."`";
	}
}

$sql_final.= " FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` AS `ticket`
LEFT JOIN (
    SELECT DISTINCT `ticket_id`, `user_id`
    FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_collaborator`
    WHERE `user_id` = ".intval($_SESSION['id'])."
) AS `w` ON (`ticket`.`id` = `w`.`ticket_id`) ";

$sql_final.= "
LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` AS `customer`
    ON `customer`.`id` = (
        SELECT MIN(`customer_id`)
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` AS `ticket_to_customer`
        WHERE `ticket_id` = `ticket`.`id`
            AND `customer_type` = 'REQUESTER'
        LIMIT 1
    )
LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."customers` AS `lastreplier_customer`
    ON `ticket`.`lastreplier` = '0'
    AND `lastreplier_customer`.`id` = (
        SELECT `customer_id`
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."replies`
        WHERE `replyto` = `ticket`.`id`
            AND `customer_id` IS NOT NULL
        ORDER BY `id` DESC
        LIMIT 1)
LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."users` AS `lastreplier_staff`
    ON `ticket`.`lastreplier` <> '0'
    AND `ticket`.`replierid` = `lastreplier_staff`.`id`
LEFT JOIN (
    SELECT DISTINCT `ticket_id`, `user_id`
    FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."bookmarks`
    WHERE `user_id` = ".intval($_SESSION['id'])."
) AS `bookmarks` ON (`ticket`.`id` = `bookmarks`.`ticket_id`)
LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."custom_priorities` AS `custom_priorities` ON `ticket`.`priority` = `custom_priorities`.`id`
WHERE
";

if ($is_quick_link == 'cbm') {
    $sql_final.= " `w`.`user_id`=".intval($_SESSION['id'])." AND ".hesk_myCategories()." ";
} else {
    $sql_final .= " ".hesk_myCategories()." AND ".hesk_myOwnership(1);
}

// This code will be used to count number of results for this specific search
$sql_count = " SELECT COUNT(DISTINCT `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets`.`id`) AS `cnt`, `status`,
                      IF (`owner` = " . intval($_SESSION['id']) . ", 1, IF (`owner` = 0, 0, IF (`assignedby` = " . intval($_SESSION['id']) . ", 3, 2) ) ) AS `assigned_to`,
                      IF (`due_date` < NOW(), 2, IF (`due_date` BETWEEN NOW() AND (NOW() + INTERVAL ".intval($hesk_settings['due_soon'])." DAY), 1, 0) ) AS `due`,
                      CASE WHEN `bookmarks`.`ticket_id` IS NOT NULL THEN 1 ELSE 0 END AS `is_bookmark`,
                      CASE WHEN `w`.`user_id` IS NOT NULL THEN 1 ELSE 0 END AS `is_collaborator`
                FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets`
                LEFT JOIN (
                    SELECT DISTINCT `ticket_id`, `user_id`
                    FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_collaborator`
                    WHERE `user_id` = ".intval($_SESSION['id'])."
                ) AS `w` ON (`".hesk_dbEscape($hesk_settings['db_pfix'])."tickets`.`id` = `w`.`ticket_id`)
                LEFT JOIN (
                    SELECT DISTINCT `ticket_id`, `user_id`
                    FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."bookmarks`
                    WHERE `user_id` = ".intval($_SESSION['id'])."
                ) AS `bookmarks` ON (`".hesk_dbEscape($hesk_settings['db_pfix'])."tickets`.`id` = `bookmarks`.`ticket_id`)
                WHERE ".hesk_myCategories()." AND ".hesk_myOwnership(1);

// This code will be used to count collaborated tickets for this specific search
$sql_collaborator = " SELECT COUNT(DISTINCT `ticket`.`id`) AS `cnt`
                FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` AS `ticket` LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_collaborator` AS `w` ON (`ticket`.`id` = `w`.`ticket_id` AND `w`.`user_id` = ".intval($_SESSION['id']).")
                WHERE `w`.`user_id`=".intval($_SESSION['id'])." AND ".hesk_myCategories();

// This is common SQL for all queries
$sql = "";

// Some default settings
$archive = array(1=>0,2=>0);
$s_my = array(1=>1,2=>1);
$s_ot = array(1=>1,2=>1);
$s_un = array(1=>1,2=>1);

// For some specific quick links we will ignore some filters
$ignore_category = false;
$ignore_status = false;
$ignore_owner = false;
$ignore_archive = false;
$ignore_category = false;

// -> All tickets
if ($is_quick_link == 'all')
{
    $ignore_category = true;
    $ignore_status = true;
    $ignore_owner = true;
    $ignore_archive = true;
    $ignore_category = true;
}
// -> All open tickets
elseif ($is_quick_link == 'alo')
{
    $ignore_category = true;
    $ignore_status = true;
    $ignore_owner = true;
    $ignore_archive = true;
    $ignore_category = true;
}
// -> Collaborated tickets
elseif ($is_quick_link == 'cbm')
{
    $ignore_owner = true;
}

// --> TICKET CATEGORY
if (isset($_GET['c']) && is_array($_GET['c'])) {
    $categories = [];
    foreach ($_GET['c'] as $category) {
        $category = intval($category);
        if ($category && hesk_okCategory($category, 0) ) {
            $categories[] = $category;
        }
    }
    if ( ! $ignore_category && count($categories)) {
        $sql .= " AND `category` IN (".implode(',', $categories).") ";
    }
} elseif (isset($_GET['category'])) {
    // Legacy, select a single category
    $category = intval( hesk_GET('category', 0) );
    $categories = array($category);
    if ( ! $ignore_category && $category && hesk_okCategory($category, 0) ) {
        $sql .= " AND `category`='{$category}' ";
    }
} else {
    $category = 0;
    $categories = [0];
}

// Show only tagged tickets?
if ( ! $ignore_archive && ! empty($_GET['archive']) )
{
	$archive[1]=1;
	$sql .= " AND `archive`='1' ";
}

$sql_count .= $sql;
$sql_collaborator .= $sql;

// Ticket owner preferences
$fid = 1;
require(HESK_PATH . 'inc/assignment_search.inc.php');

// --> TICKET STATUS
$status = $hesk_settings['statuses'];

// Process statuses unless overridden with "s_all" variable
if ( ! $ignore_status && ! hesk_GET('s_all') )
{
	foreach ($status as $k => $v)
	{
		if (empty($_GET['s'.$k]))
		{
			unset($status[$k]);
	    }
	}
}

// How many statuses are we pulling out of the database?
$tmp = count($status);

// Do we need to search by status?
if ( $tmp < count($hesk_settings['statuses']) )
{
	// If no statuses selected, show default (all except RESOLVED)
	if ($tmp == 0)
	{
		$status = $hesk_settings['statuses'];
		unset($status[3]);
	}

	// Add to the SQL
	$sql .= " AND `status` IN ('" . implode("','", array_keys($status) ) . "') ";
    $sql_count .= " AND `status` IN ('" . implode("','", array_keys($status) ) . "') ";
    $sql_collaborator .= " AND `status` IN ('" . implode("','", array_keys($status) ) . "') ";
}

// --> TICKET PRIORITY

$possible_priority = hesk_possible_priorities();
$priority = $possible_priority;

foreach ($priority as $k => $v)
{
	if (empty($_GET['p'.$k]))
    {
    	unset($priority[$k]);
    }
}

// How many priorities are we pulling out of the database?
$tmp = count($priority);

// Create the SQL based on the number of priorities we need
if ($tmp == 0 || $tmp == count($possible_priority))
{
	// Nothing or all selected, no need to modify the SQL code
    $priority = $possible_priority;
}
else
{
	// A custom selection of priorities
	$sql .= " AND `priority` IN ('" . implode("','", array_keys($priority) ) . "') ";
    $sql_count .= " AND `priority` IN ('" . implode("','", array_keys($priority) ) . "') ";
    $sql_collaborator .= " AND `priority` IN ('" . implode("','", array_keys($priority) ) . "') ";
}

// Due date
if ($is_quick_link == 'due')
{
    $sql .= " AND `status` != 3 AND `due_date` BETWEEN NOW() AND (NOW() + INTERVAL ".intval($hesk_settings['due_soon'])." DAY) ";
}
elseif ($is_quick_link == 'ovr')
{
    $sql .= " AND `status` != 3 AND `due_date` < NOW() ";
}
elseif ($is_quick_link == 'alo')
{
    $sql .= " AND `status` != 3 ";
    $sql_count .= " AND `status` != 3 ";
    $sql_collaborator .= " AND `status` != 3 ";
}
elseif ($is_quick_link == 'bm')
{
    $sql .= " AND `bookmarks`.`ticket_id` IS NOT NULL";
}

// Did the user specify a due date (either specific or a range)?
$duedate_search_type = hesk_restricted_GET('duedate_option', ['specific', 'range'], 'specific');
$duedate_input = hesk_GET('duedate_specific_date');
$duedate_amount_value = intval(hesk_GET('duedate_amount_value'));
$duedate_amount_unit = hesk_restricted_GET('duedate_amount_unit', ['day', 'week'], 'day');
if ($duedate_search_type === 'specific' && $duedate_input && hesk_datepicker_get_date($duedate_input) !== false) {
    $formatted_due_date = hesk_datepicker_get_date($duedate_input);
    $hesk_settings['datepicker'] = array();
    $hesk_settings['datepicker']['#duedate_specific_date']['timestamp'] = $formatted_due_date->getTimestamp();;
    $formatted_due_date = $formatted_due_date->format('Y-m-d');
    $sql .= " AND DATE(`due_date`) = '".hesk_dbEscape($formatted_due_date)."' ";
    $sql_count .= " AND DATE(`due_date`) = '".hesk_dbEscape($formatted_due_date)."' ";
    $sql_collaborator .= " AND DATE(`due_date`) = '".hesk_dbEscape($formatted_due_date)."' ";
} elseif ($duedate_search_type === 'range' && $duedate_amount_value && $duedate_amount_unit) {
    $unit = $duedate_amount_unit === 'day' ? 'DAY' : 'WEEK';
    $sql .= " AND `due_date` BETWEEN NOW() AND (NOW() + INTERVAL {$duedate_amount_value} {$unit}) ";
    $sql_count .= " AND `due_date` BETWEEN NOW() AND (NOW() + INTERVAL {$duedate_amount_value} {$unit}) ";
    $sql_collaborator .= " AND `due_date` BETWEEN NOW() AND (NOW() + INTERVAL {$duedate_amount_value} {$unit}) ";
}

// That's all the SQL we need for count
$sql = $sql_final . $sql;

// Prepare variables used in search and forms
require(HESK_PATH . 'inc/prepare_ticket_search.inc.php');

// We need to group the count SQL by parameters to be able to extract different totals
$sql_count .= " GROUP BY `assigned_to`, `due`, `status`, `is_bookmark`, `is_collaborator` ";

// List tickets?
if (!isset($_SESSION['hide']['ticket_list']))
{
	require(HESK_PATH . 'inc/ticket_list.inc.php');
}
priorities.inc.php
wget 'https://lists2.roe3.org/hesk/inc/priorities.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

// Load available priorities
hesk_load_priorities();

/*** FUNCTIONS ***/

function hesk_load_priorities($use_cache=1)
{
    global $hesk_settings, $hesklang;

    // Do we have a cached version available
    $cache_dir = dirname(dirname(__FILE__)).'/'.$hesk_settings['cache_dir'].'/';
    $cache_file = $cache_dir . 'priority_' . sha1($hesk_settings['language']).'.cache.php';

    if ($use_cache && file_exists($cache_file))
    {
        require($cache_file);
        return true;
    }

    // Define priorities array
    $hesk_settings['priorities'] = array();
    hesk_load_database_functions();
    hesk_dbConnect();

    $res = hesk_dbQuery("SELECT `id`, `name`, `color`, `can_customers_select`,`priority_order` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."custom_priorities` ORDER BY `priority_order` ASC");
    while ($row = hesk_dbFetchAssoc($res))
    {
        // Let's set priority name for current language (or the first one we find)
        $names = json_decode($row['name'], true);
        //print_r($names);
        $p_names = "";
        if((!isset($names[$hesk_settings['language']]) && $row['id'] < 4) || (isset($names[$hesk_settings['language']]) && strtolower($names[$hesk_settings['language']]) == "null"  && $row['id'] < 4)){
            //Check for default priority name is NULL
            switch ($row['id']) {
                case 0:
                    $p_names = $hesklang['critical'];
                    break;
                case 1:
                    $p_names = $hesklang['high'];
                    break;
                case 2:
                    $p_names = $hesklang['medium'];
                    break;
                default:
                    $p_names = $hesklang['low'];
            }
        }else{
            $row['name'] = (isset($names[$hesk_settings['language']])) ? $names[$hesk_settings['language']] : reset($names);
            $p_names = $row['name'];
        }


        // Add to priorities array
        $hesk_settings['priorities'][$row['id']] = array(
            'id' => $row['id'],
            'name'  => $p_names,
            'color' => '#'.$row['color'],
            'can_customers_select' => $row['can_customers_select'],
            'priority_order' => $row['priority_order'],
        );
    }
    // Try to cache results
    if ($use_cache && (is_dir($cache_dir) || ( @mkdir($cache_dir, 0777) && is_writable($cache_dir) ) ) )
    {
        // Is there an index.htm file?
        if ( ! file_exists($cache_dir.'index.htm'))
        {
            @file_put_contents($cache_dir.'index.htm', '');
        }

        // Write data
        @file_put_contents($cache_file, '<?php if (!defined(\'IN_SCRIPT\')) {die();} $hesk_settings[\'priorities\']=' . var_export($hesk_settings['priorities'], true) . ';' );
    }

    return true;
} // END hesk_load_priorities()


function hesk_is_valid_priority_id($priority)
{
    global $hesk_settings;

    if (is_int($priority))
    {
        return isset($hesk_settings['priorities'][$priority]);
    }

    if ( ! is_string($priority) || ! preg_match('/^(0|[1-9][0-9]*)$/', $priority))
    {
        return false;
    }

    return isset($hesk_settings['priorities'][$priority]);
} // END hesk_is_valid_priority_id()


function hesk_get_valid_priority_id($priority, $default = null)
{
    global $hesk_settings;

    if (hesk_is_valid_priority_id($priority))
    {
        return intval($priority);
    }

    if ($default !== null && hesk_is_valid_priority_id($default))
    {
        return intval($default);
    }

    $priority_ids = array_keys($hesk_settings['priorities']);
    return intval($priority_ids[0]);
} // END hesk_get_valid_priority_id()


function hesk_get_priority_select($ignore_priority = '', $can_select = true, $select_category = '', $select_category_multiple = array() )
{
    global $hesk_settings;

    $options = '';

    foreach ($hesk_settings['priorities'] as $k => $v)
    {
        $data_style = "";
        if ($k === $ignore_priority) {
            continue;
        }
        if ($can_select == true || $v['can_customers_select'] == "1") {
            $data_style ='border-top-color:'.$v['color'].';border-left-color:'.$v['color'].';border-bottom-color:'.$v['color'].';';
            $options .= '<option value="'.$v['id'].'" '.( ($v['id'] == $select_category || in_array($v['id'], $select_category_multiple) ) ? 'selected' : '').' data-class="priority_img priority_dwn" data-style="'.$data_style.'" >'.$v['name'].'</option>';
        }
    }

    return $options;

} // END hesk_get_priority_select()


function hesk_get_priority_checkboxes($selected = array())
{
    global $hesk_settings;

    $i = 0;

    echo '<div class="checkbox-group list">';

    $has_row = false;
    foreach ($hesk_settings['priorities'] as $k => $v) {

        if ($i % 3 === 0) {
            echo '<div class="row">';
            $has_row = true;
        }
        $data_style ='border-top-color:'.$v['color'].';border-left-color:'.$v['color'].';border-bottom-color:'.$v['color'].';';
        echo '
        <div class="checkbox-custom">
            <input type="checkbox" id="priority_'.$k.'" name="p'.$k.'" value="1" '.(isset($selected[$k]) ? 'checked' : '').'>
            <label for="priority_'.$k.'" class="td-flex"> <div class="priority_img" style='.$data_style.'></div> '.hesk_get_admin_ticket_priority($k).'</label>
        </div>';

        if ($i % 3 === 2) {
            echo '</div>';
            $has_row = false;
        }

        $i++;
    }
    if ($has_row) echo '</div>';
    echo '</div>';
} // END hesk_get_priority_checkboxes()


function hesk_get_priority_name($priority)
{
    global $hesk_settings, $hesklang;
    return isset($hesk_settings['priorities'][$priority]['name']) ? $hesk_settings['priorities'][$priority]['name'] : $hesklang['unknown'];
} // END hesk_get_priority_name()


function hesk_get_admin_ticket_priority($priority, $append = '')
{
    return hesk_get_ticket_priority($priority, $append, 0);
} // END hesk_get_admin_ticket_priority()


function hesk_get_admin_ticket_priority_for_list($priority, $append = '')
{
    global $hesk_settings;

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

    $color = $hesk_settings['priorities'][$priority]['color'];
    $data_style ='border-top-color:'.$color.';border-left-color:'.$color.';border-bottom-color:'.$color.';';

    return '<div class="priority_img" style='.$data_style.'></div> '.hesk_get_ticket_priority($priority, $append, 0);
} // END hesk_get_admin_ticket_priority_for_list()


function hesk_get_ticket_priority($priority, $append = '', $check_change = 1)
{
    global $hesk_settings, $hesklang;

    // Is this a valid priority?
    if ( ! isset($hesk_settings['priorities'][$priority]['name']))
    {
        return $hesklang['unknown'];
    }

    // In the customer side check if this priority can be changed
    if ($check_change && ! hesk_can_customer_select_priority($priority))
    {
        if (isset($hesk_settings['priorities'][$priority]['color']))
        {
            return '<div class="ml5">'.$hesk_settings['priorities'][$priority]['name'].'</div>';
        }

        return $hesk_settings['priorities'][$priority]['name'];
    }

    // Does this priority have a color code?
    if (isset($hesk_settings['priorities'][$priority]['color']))
    {
        return '<div class="ml5">'.$hesk_settings['priorities'][$priority]['name'].'</div>' . $append;
    }

    // Just return the name if nothing matches
    return $hesk_settings['priorities'][$priority]['name'] . $append;

} // END hesk_get_ticket_priority()


function hesk_get_ticket_priority_from_DB($trackingID)
{
    global $hesk_settings, $hesklang;

    if (empty($trackingID)) {
        hesk_error($hesklang['no_trackID']);
    }

    $result = hesk_dbQuery("SELECT `priority` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `trackid`='".hesk_dbEscape($trackingID)."' LIMIT 1");
    if (hesk_dbNumRows($result) != 1) {
        hesk_error($hesklang['ticket_not_found']);
    }

    return hesk_dbResult($result);
} // END hesk_get_ticket_priority_from_DB()


function hesk_can_customer_select_priority($priority)
{
    global $hesk_settings;
    return ( ! isset($hesk_settings['priorities'][$priority]['can_customers_select']) || $hesk_settings['priorities'][$priority]['can_customers_select'] == '1') ? true : false;
} // END hesk_can_customer_select_priority()


function hesk_print_priority_select_box_jquery()
{
    global $hesk_settings;
    ?>
    <script>
    $(document).ready(function() {
        <?php
        $data_options = array();
        foreach ($hesk_settings['priorities'] as $id => $data)
        {
            // Is this a default priority? Use style class to add color
            if (isset($data['class']))
            {
                $data_options[$id] = array('class' => $data['class']);
                echo '$("#ticket-status-div > div.dropdown-select > ul.dropdown-list > li[data-option=\''.$id.'\']").addClass("'.$data['class'].'");'."\n";
                echo '
                    $("#ticket-status-div > div.dropdown-select > div.label > span").filter(function () {
                        return $(this).text() == "'.addslashes($data['name']).'";
                    }).addClass("'.$data['class'].'");'."\n";
                echo '$("#submit-as-div > ul.dropdown-list > li[data-option=\'submit_as-'.$id.'\']").addClass("'.$data['class'].'");'."\n";
                continue;
            }

            // Does this priority have a color code?
            if (isset($data['color']))
            {
                $data_options[$id] = array('color' => $data['color']);
                echo '$("#ticket-status-div > div.dropdown-select > ul.dropdown-list > li[data-option=\''.$id.'\']").css("color", "'.$data['color'].'");'."\n";
                echo '
                    $("#ticket-status-div > div.dropdown-select > div.label > span").filter(function () {
                        return $(this).text() == "'.addslashes($data['name']).'";
                    }).css("color", "'.$data['color'].'");'."\n";
                echo '$("#submit-as-div > ul.dropdown-list > li[data-option=\'submit_as-'.$id.'\']").css("color", "'.$data['color'].'");'."\n";
            }
        }
        ?>
    });

    function hesk_update_priority_color(this_id)
    {
        $("#ticket-status-div > div.dropdown-select > div.label > span").removeClass();
        $("#ticket-status-div > div.dropdown-select > div.label > span").removeAttr('style');
        <?php
        foreach($data_options as $id => $data) {
            echo 'if (this_id == '.$id.') {';
            if (isset($data['class'])) {
                echo '$("#ticket-status-div > div.dropdown-select > div.label > span").addClass("'.$data['class'].'");';
            } else {
                echo '$("#ticket-status-div > div.dropdown-select > div.label > span").css("color", "'.$data['color'].'");';
            }
            echo 'return;}';
        }
        ?>
    }
    </script>
    <?php
} // END hesk_print_priority_select_box_jquery()


function hesk_possible_priorities(){
    global $hesk_settings;
    $possible_priority = [];
    foreach ($hesk_settings['priorities'] as $k => $v) {
        $possible_priority[$k] = $v['name'];
    }
    return $possible_priority;
}// END hesk_possible_priorities()


function hesk_possible_priorities_order(){
    global $hesk_settings;
    $res = hesk_dbQuery("SELECT `id`, `name`, `color`, `can_customers_select`,`priority_order` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."custom_priorities` ORDER BY `priority_order` ASC");
    $possible_priority_order = [];
    while ($row = hesk_dbFetchAssoc($res))
    {
        // Let's set status name for current language (or the first one we find)
        $names = json_decode($row['name'], true);
        $row['name'] = (isset($names[$hesk_settings['language']])) ? $names[$hesk_settings['language']] : reset($names);
        $possible_priority_order[$row['priority_order']] = $row['id'];
    }    
    
    return $possible_priority_order;
}// END hesk_possible_priorities_order()
privacy_functions.inc.php
wget 'https://lists2.roe3.org/hesk/inc/privacy_functions.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}


/*** FUNCTIONS ***/
function hesk_anonymizeTicketsForCustomer($customer_id) {
    global $hesk_settings;

    $tickets = hesk_dbQuery("SELECT `ticket_id` 
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer`
        WHERE `customer_id` = ".intval($customer_id)."
        AND `customer_type` = 'REQUESTER'");

    while ($row = hesk_dbFetchAssoc($tickets)) {
        hesk_anonymizeTicket(intval($row['ticket_id']));
    }
}

function hesk_anonymizeTicket($id, $trackingID = null, $have_ticket = false)
{
	global $hesk_settings, $hesklang;

    // Do we already have ticket info?
    if ($have_ticket)
    {
        global $ticket;
    }
    else
    {
        // Get ticket info by tracking or numerical ID
        if ($trackingID !== null)
        {
            $res = hesk_dbQuery("SELECT `id`, `trackid` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `trackid`='".hesk_dbEscape($trackingID)."' AND ".hesk_myOwnership());
        }
        else
        {
    	    $res = hesk_dbQuery("SELECT `id`, `trackid` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `id`=".intval($id)." AND ".hesk_myOwnership());
        }
        if ( ! hesk_dbNumRows($res))
        {
            return false;
        }
        $ticket = hesk_dbFetchAssoc($res);
    }

    if (!function_exists('hesk_get_customers_for_ticket')) {
        require_once(HESK_PATH . 'inc/customer_accounts.inc.php');
    }
    $customers = hesk_get_customers_for_ticket($ticket['id']);

    // Delete attachment files
	$res = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."attachments` WHERE `ticket_id`='".hesk_dbEscape($ticket['trackid'])."'");
    if (hesk_dbNumRows($res))
    {
    	$hesk_settings['server_path'] = dirname(dirname(__FILE__));

    	while ($file = hesk_dbFetchAssoc($res))
        {
        	hesk_unlink($hesk_settings['server_path'].'/'.$hesk_settings['attach_dir'].'/'.$file['saved_name']);
        }
    }

    // Delete attachments info from the database
	hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."attachments` WHERE `ticket_id`='".hesk_dbEscape($ticket['trackid'])."'");

    // Anonymize customer names on ticket history
    foreach ($customers as $customer) {
        hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets`
            SET `history`=REPLACE(`history`, ' ".hesk_dbEscape(addslashes($customer['name']))."</li>', ' ".hesk_dbEscape($hesklang['anon_name'])."</li>')
            WHERE `id` = ".intval($ticket['id']));
    }

    // Anonymize ticket
    $sql = "UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` SET
    `u_name`  = '".hesk_dbEscape($hesklang['anon_name'])."',
    `u_email` = '".hesk_dbEscape($hesklang['anon_email'])."',
    `subject` = '".hesk_dbEscape($hesklang['anon_subject'])."',
    `message` = '".hesk_dbEscape($hesklang['anon_message'])."',
    `message_html` = '".hesk_dbEscape($hesklang['anon_message'])."',
    `ip`      = '".hesk_dbEscape($hesklang['anon_IP'])."',
    ";
    for($i=1; $i<=50; $i++)
    {
        $sql .= "`custom{$i}` = '',";
    }
    $sql .= "
    attachments='',
    `history`=CONCAT(`history`,'".hesk_dbEscape(sprintf($hesklang['thist18'],hesk_date(),addslashes($_SESSION['name']).' ('.$_SESSION['user'].')'))."')
    WHERE `id`='".intval($ticket['id'])."'";
	hesk_dbQuery($sql);

    // Delete customer relationships
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."ticket_to_customer` WHERE `ticket_id` = ".intval($ticket['id']));

    // Anonymize replies
	hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` SET `customer_id` = NULL, `message` = '".hesk_dbEscape($hesklang['anon_message'])."', `message_html` = '".hesk_dbEscape($hesklang['anon_message'])."', attachments='' WHERE `replyto`='".intval($ticket['id'])."'");

    // Delete ticket notes
	hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."notes` WHERE `ticket`='".intval($ticket['id'])."'");

	// Delete ticket reply drafts
	hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."reply_drafts` WHERE `ticket`=".intval($ticket['id']));

    // Delete linked ticket associations
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."linked_tickets` WHERE `ticket_id1`='".intval($ticket['id'])."' OR `ticket_id2`='".intval($ticket['id'])."'");

    return true;
} // END hesk_anonymizeTicket()
profile_functions.inc.php
wget 'https://lists2.roe3.org/hesk/inc/profile_functions.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}


function hesk_getPermissionGroups($group_ids = array(), $assignable_only = false)
{
    global $hesk_settings;

    $where = '';
    if (is_array($group_ids) && count($group_ids)) {
        $group_ids = array_unique(array_map('intval', $group_ids));
        $where = " WHERE `group`.`id` IN (" . implode(',', $group_ids) . ")";
    }

    $permission_groups_rs = hesk_dbQuery("SELECT `group`.`id` AS `group_id`, `group`.`name` AS `group_name`, `category`.`category_id` AS `category_feature_value`, 'CATEGORY' AS `category_feature_type`
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_groups` AS `group`
        INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_group_categories` AS `category`
            ON `group`.`id` = `category`.`group_id`
        " . $where . "
        UNION ALL
        SELECT `group`.`id` AS `group_id`, `group`.`name` AS `group_name`, `feature`.`feature` AS `category_feature_value`, 'FEATURE' AS `category_feature_type`
        FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_groups` AS `group`
        INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."permission_group_features` AS `feature`
            ON `group`.`id` = `feature`.`group_id`
        " . $where);

    $permission_groups = array();
    while ($row = hesk_dbFetchAssoc($permission_groups_rs)) {
        if (!key_exists($row['group_id'], $permission_groups)) {
            $permission_groups[$row['group_id']] = array(
                'id' => $row['group_id'],
                'name' => $row['group_name'],
                'categories' => array(),
                'features' => array()
            );
        }
        if ($row['category_feature_type'] === 'CATEGORY') {
            $permission_groups[$row['group_id']]['categories'][] = intval($row['category_feature_value']);
        } else {
            $permission_groups[$row['group_id']]['features'][] = $row['category_feature_value'];
        }
    }

    if ($assignable_only && ! $_SESSION['isadmin']) {
        foreach ($permission_groups as $group_id => $permission_group) {
            if ( ! hesk_isPermissionGroupAssignable($permission_group)) {
                unset($permission_groups[$group_id]);
            }
        }
    }

    return $permission_groups;

} // End hesk_getPermissionGroups()


function hesk_isPermissionGroupAssignable($permission_group)
{
    global $hesk_settings;

    if ($_SESSION['isadmin']) {
        return true;
    }

    foreach ($permission_group['categories'] as $category_id) {
        if ( ! array_key_exists($category_id, $hesk_settings['categories'])) {
            return false;
        }
    }

    foreach ($permission_group['features'] as $feature) {
        if ( ! in_array($feature, $hesk_settings['features'])) {
            return false;
        }
    }

    return true;

} // End hesk_isPermissionGroupAssignable()


function hesk_validatePermissionGroupsForCurrentUser($permission_group_ids)
{
    if ($_SESSION['isadmin'] || ! is_array($permission_group_ids) || count($permission_group_ids) == 0) {
        return true;
    }

    $permission_groups = hesk_getPermissionGroups($permission_group_ids);

    foreach ($permission_groups as $permission_group) {
        if ( ! hesk_isPermissionGroupAssignable($permission_group)) {
            return false;
        }
    }

    return true;

} // End hesk_validatePermissionGroupsForCurrentUser()


function hesk_profile_tab($session_array='new',$is_profile_page=true)
{
	global $hesk_settings, $hesklang, $can_reply_tickets, $can_view_tickets, $can_view_unassigned, $can_man_customers;

	$show_permissions = false;
	$show_preferences = false;

	$steps = array($hesklang['pinfo']);
	if (!$is_profile_page) {
	    $steps[] = $hesklang['permissions'];
	    $show_permissions = true;
    }
	$steps[] = $hesklang['sig'];
	if (!$is_profile_page || $can_reply_tickets) {
	    $steps[] = $hesklang['pref'];
	    $show_preferences = true;
    }
	$steps[] = $hesklang['notn'];

	$errors = hesk_SESSION(array($session_array, 'errors'));
	$errors = is_array($errors) ? $errors : array();


    $permission_groups = $show_permissions ? hesk_getPermissionGroups(array(), true) : array();

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

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

    if ( ! isset($_SESSION[$session_array]['pg_features'])) {
        $_SESSION[$session_array]['pg_features'] = array();
    }
    ?>
	<!-- TABS -->
    <ul class="step-bar">
        <?php
        $i = 1;
        foreach ($steps as $step_name): ?>
            <li data-link="<?php echo $i++; ?>" data-all="<?php echo count($steps); ?>">
                <?php echo $step_name; ?>
            </li>
        <?php endforeach; ?>
    </ul>
    <?php
    $current_step = 1;
    ?>
    <div class="step-slider">
        <div class="step-item step-<?php echo $current_step++; ?>">
            <div class="form-group">
                <label for="prof_name"><?php echo $hesklang['real_name']; ?></label>
                <input type="text" class="form-control <?php echo in_array('name', $errors) ? 'isError' : ''; ?>" id="prof_name" name="name" maxlength="50"
                       value="<?php echo $_SESSION[$session_array]['name']; ?>">
            </div>
            <div class="form-group">
                <label for="prof_email"><?php echo $hesklang['email']; ?></label>
                <input type="text" class="form-control <?php echo in_array('email', $errors) ? 'isError' : ''; ?>" name="email" maxlength="255" id="prof_email"
                       value="<?php echo $_SESSION[$session_array]['email']; ?>">
            </div>
            <?php if ($hesk_settings['staff_nicknames']): ?>
            <div class="form-group">
                <label for="prof_nickname">
                    <?php echo $hesklang['nickname']; ?>
                    <div class="tooltype right out-close" style="vertical-align: text-bottom;">
                        <svg class="icon icon-info">
                            <use xlink:href="../img/sprite.svg#icon-info"></use>
                        </svg>
                        <div class="tooltype__content">
                            <div class="tooltype__wrapper"><?php echo $hesklang['what_cust_see']; ?></div>
                        </div>
                    </div>
                </label>
                <input type="text" class="form-control <?php echo in_array('nickname', $errors) ? 'isError' : ''; ?>" name="nickname" maxlength="255" id="prof_nickname"
                       value="<?php echo isset($_SESSION[$session_array]['nickname']) ? $_SESSION[$session_array]['nickname'] : ''; ?>">
            </div>
            <?php endif; ?>
            <?php
            if ( ! $is_profile_page || $_SESSION['isadmin'])
            {
                ?>
                <section class="item--section">
                <h4>
                    <?php echo $hesklang['login_credentials']; ?>
                </h4>
                <div class="form-group">
                    <label for="prof_user"><?php echo $hesklang['username']; ?></label>
                    <input type="text" class="form-control <?php echo in_array('user', $errors) ? 'isError' : ''; ?>" name="user" autocomplete="off" id="prof_user" maxlength="20"
                           value="<?php echo $_SESSION[$session_array]['user']; ?>">
                </div>
                </section>
                <?php
            }

            if ( ! $is_profile_page || $hesk_settings['autoassign']):
            ?>
            <section class="item--section">
                <?php if ( ! $is_profile_page): ?>
                <div class="form-group">
                    <label for="prof_newpass"><?php echo (empty($_SESSION[$session_array]['id']) ? $hesklang['pass'] : $hesklang['new_pass']); ?></label>
                    <input type="password" id="prof_newpass" name="newpass" autocomplete="off" class="form-control <?php echo in_array('passwords', $errors) ? 'isError' : ''; ?>"
                           value="<?php echo isset($_SESSION[$session_array]['cleanpass']) ? $_SESSION[$session_array]['cleanpass'] : ''; ?>"
                           onkeyup="hesk_checkPassword(this.value)">
                </div>
                <div class="form-group">
                    <label for="prof_newpass2"><?php echo (empty($_SESSION[$session_array]['id']) ? $hesklang['confirm_pass'] : $hesklang['confirm_new_pass']); ?></label>
                    <input type="password" class="form-control <?php echo in_array('passwords', $errors) ? 'isError' : ''; ?>" id="prof_newpass2" name="newpass2" autocomplete="off"
                           value="<?php echo isset($_SESSION[$session_array]['cleanpass']) ? $_SESSION[$session_array]['cleanpass'] : ''; ?>">
                </div>
                <div class="form-group">
                    <label><?php echo $hesklang['pwdst']; ?></label>
                    <div style="border: 1px solid #d4d6e3; width: 100%; height: 14px">
                        <div id="progressBar" style="font-size: 1px; height: 12px; width: 0px; border: none;">
                        </div>
                    </div>
                </div>
                <?php else: ?>
                <?php hesk_show_info($hesklang['change_pass_info']); ?>
                <?php endif; ?>
                <?php if (!$is_profile_page && $hesk_settings['autoassign']): ?>
                    <div class="form-switcher">
                        <label class="switch-checkbox">
                            <input type="checkbox" name="autoassign" value="Y"
                                <?php if (isset($_SESSION[$session_array]['autoassign']) && !empty($_SESSION[$session_array]['autoassign'])) {echo 'checked';} ?>>
                            <div class="switch-checkbox__bullet">
                                <i>
                                    <svg class="icon icon-close">
                                        <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-close"></use>
                                    </svg>
                                    <svg class="icon icon-tick">
                                        <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-tick"></use>
                                    </svg>
                                </i>
                            </div>
                            <span><?php echo $hesklang['user_aa']; ?></span>
                        </label>
                    </div>
                <?php endif; ?>
            </section>
            <?php endif; ?>
        </div>
        <?php if ($show_permissions): ?>
        <div class="step-item step-<?php echo $current_step++; ?>">
            <div class="form-group">
                <label><?php echo $hesklang['atype']; ?></label>
                <?php
                /* Only administrators can create new administrator accounts */
                if ($_SESSION['isadmin']) {
                    ?>
                    <div class="radio-list">
                        <div class="radio-custom">
                            <input type="radio" id="prof_isadmin1" name="isadmin" value="1" onchange="hesk_toggleLayerDisplay('options')"
                                <?php if ($_SESSION[$session_array]['isadmin']) echo 'checked'; ?>>
                            <label for="prof_isadmin1">
                                <?php echo $hesklang['administrator']; ?>
                                <?php echo $hesklang['admin_can']; ?>
                            </label>
                        </div>
                        <div class="radio-custom">
                            <input type="radio" id="prof_isadmin0" name="isadmin" value="0" onchange="hesk_toggleLayerDisplay('options')"
                                <?php if (!$_SESSION[$session_array]['isadmin']) echo 'checked'; ?>>
                            <label for="prof_isadmin0">
                                <?php echo $hesklang['astaff']; ?>
                                <?php echo $hesklang['staff_can']; ?>
                            </label>
                        </div>
                    </div>
                    <?php
                } else {
                    echo '<label>'.$hesklang['astaff'].' '.$hesklang['staff_can'] . '</label>';
                }
                ?>
            </div>
            <div id="options" style="display: <?php echo ($_SESSION['isadmin'] && $_SESSION[$session_array]['isadmin']) ? 'none' : 'block'; ?>;">
                <h4><?php echo $hesklang['permission_groups_title']; ?></h4>
                <section class="item--section">
                    <?php foreach ($permission_groups as $permission_group): ?>
                    <div class="checkbox-custom <?php echo in_array('permission_groups', $errors) ? 'isError' : ''; ?>">
                        <input type="checkbox" id="prof_permission_group_<?php echo $permission_group['id'] ?>"
                               data-categories="<?php echo implode(',', $permission_group['categories']); ?>"
                               data-features="<?php echo implode(',', $permission_group['features']); ?>"
                               name="permissionGroups[]"
                               value="<?php echo $permission_group['id']; ?>"
                            <?php if (in_array($permission_group['id'],$_SESSION[$session_array]['permission_groups'])) { echo 'checked'; } ?>>
                        <label for="prof_permission_group_<?php echo $permission_group['id'] ?>"><?php echo $permission_group['name']; ?></label>
                    </div>
                    <?php endforeach; ?>
                </section>
                <h4><?php echo $hesklang['allowed_cat']; ?></h4>
                <section class="item--section">
                    <?php foreach ($hesk_settings['categories'] as $catid => $catname): ?>
                    <div class="checkbox-custom <?php echo in_array('categories', $errors) ? 'isError' : ''; ?>">
                        <input type="checkbox" id="prof_category_<?php echo $catid; ?>" name="temp_categories[]" value="<?php echo $catid; ?>"
                            <?php if (in_array($catid,$_SESSION[$session_array]['categories']) || in_array($catid,$_SESSION[$session_array]['pg_categories'])) { echo 'checked'; } ?>
                            <?php if (in_array($catid,$_SESSION[$session_array]['pg_categories'])) { echo ' disabled'; } ?>>
                        <label for="prof_category_<?php echo $catid; ?>"><?php echo $catname; ?></label>
                    </div>
                    <?php endforeach; ?>
                </section>
                <h4><?php echo $hesklang['allow_feat']; ?></h4>
                <section class="item--section">
                    <?php foreach ($hesk_settings['features'] as $k): ?>
                        <div class="checkbox-custom <?php echo in_array('features', $errors) ? 'isError' : ''; ?>">
                            <input type="checkbox" id="<?php echo $k; ?>" name="temp_features[]" value="<?php echo $k; ?>"
                                <?php if (in_array($k,$_SESSION[$session_array]['features']) || in_array($k,$_SESSION[$session_array]['pg_features'])) { echo 'checked'; } ?>
                                <?php if (in_array($k,$_SESSION[$session_array]['pg_features'])) { echo ' disabled'; } ?>>
                            <label for="<?php echo $k; ?>"><?php echo $hesklang[$k]; ?></label>
                        </div>
                    <?php endforeach; ?>
                </section>
                <div id="selected-user-permissions" style="display: none">
                    <?php foreach ($_SESSION[$session_array]['categories'] as $catid): ?>
                    <input type="hidden" name="categories[]" value="<?php echo $catid; ?>">
                    <?php endforeach; ?>
                    <?php foreach ($_SESSION[$session_array]['features'] as $k): ?>
                        <input type="hidden" name="features[]" value="<?php echo $k; ?>">
                    <?php endforeach; ?>
                </div>
            </div>
        </div>
        <?php endif; ?>
        <div class="step-item step-<?php echo $current_step++; ?>">
            <div class="form-group">
                <label for="prof_signature"><?php echo $hesklang['signature_max']; ?></label>
                <textarea class="form-control <?php echo in_array('signature', $errors) ? 'isError' : ''; ?>"
                          name="signature" rows="10" cols="60" aria-label="<?php echo $hesklang['sig']; ?>"><?php echo $_SESSION[$session_array]['signature']; ?></textarea>
                <?php echo $hesklang['sign_extra']; ?>
            </div>
        </div>
        <?php if ($show_preferences): ?>
        <div class="step-item step-<?php echo $current_step++; ?>">
            <section class="item--section">
                <div class="form-group">
                    <label><?php echo $hesklang['aftrep']; ?></label>
                    <div class="radio-list">
                        <div class="radio-custom">
                            <input type="radio" id="prof_afterreply0" name="afterreply" value="0" <?php if (!$_SESSION[$session_array]['afterreply']) {echo 'checked';} ?>>
                            <label for="prof_afterreply0">
                                <?php echo $hesklang['showtic']; ?>
                            </label>
                        </div>
                        <div class="radio-custom">
                            <input type="radio" id="prof_afterreply1" name="afterreply" value="1" <?php if ($_SESSION[$session_array]['afterreply'] == 1) {echo 'checked';} ?>>
                            <label for="prof_afterreply1">
                                <?php echo $hesklang['gomain']; ?>
                            </label>
                        </div>
                        <div class="radio-custom">
                            <input type="radio" id="prof_afterreply2" name="afterreply" value="2" <?php if ($_SESSION[$session_array]['afterreply'] == 2) {echo 'checked';} ?>>
                            <label for="prof_afterreply2">
                                <?php echo $hesklang['shownext']; ?>
                            </label>
                        </div>
                    </div>
                </div>
            </section>
            <section class="item--section defaults-section">
                <h4><?php echo $hesklang['defaults']; ?></h4>
                <?php if ($hesk_settings['time_worked']): ?>
                    <div class="checkbox-custom">
                        <input type="checkbox" id="prof_autostart" name="autostart" value="1" <?php if (!empty($_SESSION[$session_array]['autostart'])) {echo 'checked';}?>>
                        <label for="prof_autostart"><?php echo $hesklang['autoss']; ?></label>
                    </div>
                <?php
                endif;

                if (empty($_SESSION[$session_array]['autoreload'])) {
                    $reload_time = 5;
                    $sec = '';
                    $min = 'selected="selected"';
                } else {
                    $reload_time = intval($_SESSION[$session_array]['autoreload']);

                    if ($reload_time >= 60 && $reload_time % 60 == 0) {
                        $reload_time = $reload_time / 60;
                        $sec = '';
                        $min = 'selected="selected"';
                    } else {
                        $sec = 'selected="selected"';
                        $min = '';
                    }
                }
                ?>
                <div class="checkbox-custom">
                    <input type="checkbox" id="prof_notify_customer_new" name="notify_customer_new" value="1" <?php if (!empty($_SESSION[$session_array]['notify_customer_new'])) {echo 'checked';}?>>
                    <label for="prof_notify_customer_new"><?php echo $hesklang['pncn']; ?></label>
                </div>
                <div class="checkbox-custom">
                    <input type="checkbox" id="prof_notify_customer_reply" name="notify_customer_reply" value="1" <?php if (!empty($_SESSION[$session_array]['notify_customer_reply'])) {echo 'checked';}?>>
                    <label for="prof_notify_customer_reply"><?php echo $hesklang['pncr']; ?></label>
                </div>
                <div class="checkbox-custom">
                    <input type="checkbox" id="prof_show_suggested" name="show_suggested" value="1" <?php if (!empty($_SESSION[$session_array]['show_suggested'])) {echo 'checked';}?>>
                    <label for="prof_show_suggested"><?php echo $hesklang['pssy']; ?></label>
                </div>
                <div class="check-plus-input">
                    <div class="checkbox-custom">
                        <input type="checkbox" id="prof_autoreload" name="autoreload" value="1" <?php if (!empty($_SESSION[$session_array]['autoreload'])) {echo 'checked';}?>>
                        <label for="prof_autoreload"><?php echo $hesklang['arpp']; ?></label>
                    </div>
                    <div class="form-group">
                        <input type="text" class="form-control" name="reload_time" value="<?php echo $reload_time; ?>" aria-label="Reload time"
                               maxlength="5" onkeyup="this.value=this.value.replace(/[^\d]+/,'')">
                    </div>
                    <div class="form-group">
                        <div class="dropdown-select center out-close">
                            <select name="secmin">
                                <option value="sec" <?php echo $sec; ?>><?php echo $hesklang['seconds']; ?></option>
                                <option value="min" <?php echo $min; ?>><?php echo $hesklang['minutes']; ?></option>
                            </select>
                        </div>
                    </div>
                </div>
            </section>
        </div>
        <?php endif; ?>
        <div class="step-item step-<?php echo $current_step; ?>">
            <h5><?php echo $hesklang['nomw']; ?></h5>
            <?php if (!$is_profile_page || $can_view_tickets) {
                echo '<section class="item--section">';
                if (!$is_profile_page || $can_view_unassigned) { ?>
                    <div class="checkbox-custom">
                        <input type="checkbox" id="prof_notify_new_unassigned" name="notify_new_unassigned" value="1"
                            <?php if (!empty($_SESSION[$session_array]['notify_new_unassigned'])) {echo 'checked';}?>>
                        <label for="prof_notify_new_unassigned"><?php echo $hesklang['nwts']; ?> <?php echo $hesklang['unas']; ?></label>
                    </div>
            <?php
                }
                ?>
                <div class="checkbox-custom">
                    <input type="checkbox" id="prof_notify_new_my" name="notify_new_my" value="1"
                        <?php if (!empty($_SESSION[$session_array]['notify_new_my'])) {echo 'checked';}?>>
                    <label for="prof_notify_new_my"><?php echo $hesklang['nwts']; ?> <?php echo $hesklang['s_my']; ?></label>
                </div>
            <?php
                echo '</section>';
                echo '<section class="item--section">';

                if (!$is_profile_page || $can_view_unassigned) { ?>
                    <div class="checkbox-custom">
                        <input type="checkbox" id="prof_notify_reply_unassigned" name="notify_reply_unassigned" value="1"
                            <?php if (!empty($_SESSION[$session_array]['notify_reply_unassigned'])) {echo 'checked';}?>>
                        <label for="prof_notify_reply_unassigned"><?php echo $hesklang['ncrt']; ?> <?php echo $hesklang['unas']; ?></label>
                    </div>
                <?php
                } ?>
                <div class="checkbox-custom">
                    <input type="checkbox" id="prof_notify_reply_my" name="notify_reply_my" value="1"
                        <?php if (!empty($_SESSION[$session_array]['notify_reply_my'])) {echo 'checked';}?>>
                    <label for="prof_notify_reply_my"><?php echo $hesklang['ncrt']; ?> <?php echo $hesklang['s_my']; ?></label>
                </div>
            <?php
                echo '</section>';
                echo '<section class="item--section">';
                if (!$is_profile_page || $can_view_unassigned) { ?>
                    <div class="checkbox-custom">
                        <input type="checkbox" id="prof_notify_overdue_unassigned" name="notify_overdue_unassigned" value="1"
                            <?php if (!empty($_SESSION[$session_array]['notify_overdue_unassigned'])) {echo 'checked';}?>>
                        <label for="prof_notify_overdue_unassigned"><?php echo $hesklang['ntoverdue']; ?> <?php echo $hesklang['unas']; ?>*</label>
                    </div>
            <?php
                }
                ?>
                <div class="checkbox-custom">
                    <input type="checkbox" id="prof_notify_overdue_my" name="notify_overdue_my" value="1"
                        <?php if (!empty($_SESSION[$session_array]['notify_overdue_my'])) {echo 'checked';}?>>
                    <label for="prof_notify_overdue_my"><?php echo $hesklang['ntoverdue']; ?> <?php echo $hesklang['s_my']; ?>*</label>
                </div>
            <?php
                echo '</section>';
            ?>
            <section class="item--section">
                <div class="checkbox-custom">
                    <input type="checkbox" id="prof_notify_assigned" name="notify_assigned" value="1"
                        <?php if (!empty($_SESSION[$session_array]['notify_assigned'])) {echo 'checked';}?>>
                    <label for="prof_notify_assigned"><?php echo $hesklang['ntam']; ?></label>
                </div>
                <div class="checkbox-custom">
                    <input type="checkbox" id="prof_notify_note" name="notify_note" value="1"
                        <?php if (!empty($_SESSION[$session_array]['notify_note'])) {echo 'checked';}?>>
                    <label for="prof_notify_note"><?php echo $hesklang['ntnote']; ?></label>
                </div>
            <?php
            } ?>
                <div class="checkbox-custom">
                    <input type="checkbox" id="prof_notify_pm" name="notify_pm" value="1"
                        <?php if (!empty($_SESSION[$session_array]['notify_pm'])) {echo 'checked';}?>>
                    <label for="prof_notify_pm"><?php echo $hesklang['npms']; ?></label>
                </div>
            </section>
            <?php if (!$is_profile_page || $can_man_customers): ?>
            <section class="item--section">
                <div class="checkbox-custom">
                    <input type="checkbox" id="prof_notify_customer_approval" name="notify_customer_approval" value="1"
                        <?php if (!empty($_SESSION[$session_array]['notify_customer_approval'])) {echo 'checked';}?>>
                    <label for="prof_notify_customer_approval"><?php echo $hesklang['n_cust_app']; ?></label>
                </div>
            </section>
            <?php endif; ?>
            <?php if (!$is_profile_page || $can_view_tickets): ?>
            <section class="item--section">
                <div class="checkbox-custom">
                    <input type="checkbox" id="prof_notify_collaborator_added" name="notify_collaborator_added" value="1"
                        <?php if (!empty($_SESSION[$session_array]['notify_collaborator_added'])) {echo 'checked';}?>>
                    <label for="prof_notify_collaborator_added"><?php echo $hesklang['notify_collaborator_added']; ?></label>
                </div>
                <div class="checkbox-custom">
                    <input type="checkbox" id="prof_notify_collaborator_customer_reply" name="notify_collaborator_customer_reply" value="1"
                        <?php if (!empty($_SESSION[$session_array]['notify_collaborator_customer_reply'])) {echo 'checked';}?>>
                    <label for="prof_notify_collaborator_customer_reply"><?php echo $hesklang['notify_collaborator_customer_reply']; ?></label>
                </div>
                <div class="checkbox-custom">
                    <input type="checkbox" id="prof_notify_collaborator_staff_reply" name="notify_collaborator_staff_reply" value="1"
                        <?php if (!empty($_SESSION[$session_array]['notify_collaborator_staff_reply'])) {echo 'checked';}?>>
                    <label for="prof_notify_collaborator_staff_reply"><?php echo $hesklang['notify_collaborator_staff_reply']; ?></label>
                </div>
                <div class="checkbox-custom">
                    <input type="checkbox" id="prof_notify_collaborator_note" name="notify_collaborator_note" value="1"
                        <?php if (!empty($_SESSION[$session_array]['notify_collaborator_note'])) {echo 'checked';}?>>
                    <label for="prof_notify_collaborator_note"><?php echo $hesklang['notify_collaborator_note']; ?></label>
                </div>
                <div class="checkbox-custom">
                    <input type="checkbox" id="prof_notify_collaborator_resolved" name="notify_collaborator_resolved" value="1"
                        <?php if (!empty($_SESSION[$session_array]['notify_collaborator_resolved'])) {echo 'checked';}?>>
                    <label for="prof_notify_collaborator_resolved"><?php echo $hesklang['notify_collaborator_resolved']; ?></label>
                </div>
                <div class="checkbox-custom">
                    <input type="checkbox" id="prof_notify_collaborator_overdue" name="notify_collaborator_overdue" value="1"
                        <?php if (!empty($_SESSION[$session_array]['notify_collaborator_overdue'])) {echo 'checked';}?>>
                    <label for="prof_notify_collaborator_overdue"><?php echo $hesklang['notify_collaborator_overdue']; ?></label>
                </div>
            </section>
            <?php endif; ?>
            <?php
            if ($can_view_tickets) {
                hesk_show_notice($hesklang['ovdcron'] . ' <a href="https://www.hesk.com/knowledgebase/?article=103" target="_blank">'.$hesklang['instructions'].'</a>', '*', false);
            }?>
        </div>
    </div>

    <script>
    hesk_checkPassword(document.form1.newpass.value);

    $(document).ready(function() {
        $('#can_reply_tickets').click(function() {
            if($(this).prop("checked") == true) {
                $('#can_view_tickets').prop("checked", true);
            }
        });
    });

    const permissionGroups = [];
    const userCategories = [];
    const userFeatures = [];
    <?php
    foreach ($permission_groups as $permission_group):
        $categories = count($permission_group['categories']) === 0 ? '[]' : "[".implode(",", $permission_group['categories'])."]";
        $features = count($permission_group['features']) === 0 ? '[]' : "['".implode("','", $permission_group['features'])."']";

    ?>
    permissionGroups.push({
        id: <?php echo $permission_group['id']; ?>,
        categories: <?php echo $categories; ?>,
        features: <?php echo $features; ?>,
        selected: <?php echo in_array($permission_group['id'],$_SESSION[$session_array]['permission_groups']) ? 'true' : 'false'; ?>
    });
    <?php endforeach; ?>
    <?php foreach ($_SESSION[$session_array]['categories'] as $catid): ?>
    userCategories.push(<?php echo $catid; ?>);
    <?php endforeach; ?>
    <?php foreach ($_SESSION[$session_array]['features'] as $feature): ?>
    userFeatures.push('<?php echo hesk_makeJsString($feature); ?>');
    <?php endforeach; ?>

    $('input[name="permissionGroups[]"]').change(function() {
        const permissionGroup = permissionGroups.find(x => x.id === parseInt(this.value));
        permissionGroup.selected = this.checked;
        updateCheckboxState();
    });
    const $categoryCheckboxes = $('input[type="checkbox"][name="temp_categories[]"]');
    const $featureCheckboxes = $('input[type="checkbox"][name="temp_features[]"]');
    $categoryCheckboxes.change(function() {
        const categoryId = parseInt(this.value);
        if (this.checked) {
            userCategories.push(categoryId);
        } else {
            userCategories.splice(userCategories.indexOf(categoryId), 1);
        }
        updateCheckboxState();
    });
    $featureCheckboxes.change(function() {
        const feature = this.value;
        if (this.checked) {
            userFeatures.push(feature);
        } else {
            userFeatures.splice(userFeatures.indexOf(feature), 1);
        }
        updateCheckboxState();
    });


    function updateCheckboxState() {
        $categoryCheckboxes.each((_, element) => {
            const categoryId = parseInt(element.value);
            element.disabled = permissionGroups.some(group => group.selected && group.categories.includes(categoryId));
            if (element.disabled) {
                element.checked = true;
            } else {
                element.checked = userCategories.includes(categoryId);
            }
        });
        $featureCheckboxes.each((_, element) => {
            const feature = element.value;
            element.disabled = permissionGroups.some(group => group.selected && group.features.includes(feature));
            if (element.disabled) {
                element.checked = true;
            } else {
                element.checked = userFeatures.includes(feature);
            }
        });

        const selectedUserPermissions = document.getElementById('selected-user-permissions');
        selectedUserPermissions.innerHTML = '';
        userCategories.forEach(category => {
            const input = document.createElement('input');
            input.setAttribute('type', 'hidden');
            input.setAttribute('name', 'categories[]');
            input.setAttribute('value', category);
            selectedUserPermissions.appendChild(input);
        });
        userFeatures.forEach(feature => {
            const input = document.createElement('input');
            input.setAttribute('type', 'hidden');
            input.setAttribute('name', 'features[]');
            input.setAttribute('value', feature);
            selectedUserPermissions.appendChild(input);
        })
    }

    </script>
	<?php
} // END hesk_profile_tab()
reporting_functions.inc.php
wget 'https://lists2.roe3.org/hesk/inc/reporting_functions.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');} 

/*** FUNCTIONS ***/

function hesk_SecondsToHHMMSS($in)
{
	// Default values for hours, minutes and seconds
    $h = 0;
    $m = 0;
    $s = intval(trim($in === null ? '' : $in));

	// If time is 0 seconds just return an empty string
	if ($s == 0)
	{
		return '';
	}

	// Convert seconds to minutes if 60 or more seconds
    if ($s > 59)
    {
    	$m = floor($s / 60) + $m;
        $s = intval($s % 60);
    }

	// Convert minutes to hours if 60 or more minutes
    if ($m > 59)
    {
    	$h = floor($m / 60) + $h;
        $m = intval($m % 60);
    }

	// That's it, let's send out formatted time string
    return str_pad($h, 2, "0", STR_PAD_LEFT) . ':' . str_pad($m, 2, "0", STR_PAD_LEFT) . ':' . str_pad($s, 2, "0", STR_PAD_LEFT);

} // END hesk_SecondsToHHMMSS()


function hesk_parseXML($msg)
{
	$from = array('/\<a href="mailto\:([^"]*)"\>([^\<]*)\<\/a\>/i', '/\<a href="([^"]*)" target="_blank"\>([^\<]*)\<\/a\>/i');
	$to   = array("$1", "$1");
	$msg = preg_replace($from,$to,$msg);
	$msg = preg_replace('/<br \/>\s*/',"\n",$msg);
	$msg = trim($msg);
	return $msg;
} // END hesk_parseXML()


function dateweek($weeknumber,$business=0)
{
    $today = time();
    $day_of_week = date('N', $today);

    // Start from Monday of the current week, then offset by requested week number
    $monday = mktime(0, 0, 0, date('m', $today), date('d', $today) - $day_of_week + 1 + (7 * $weeknumber), date('Y', $today));

	// DATE BEGINNING OF THE WEEK (Monday)
	$dt[0] = date('Y-m-d', $monday);

	if ($business)
    {
		// DATE END OF BUSINESS WEEK (Friday)
		$dt[1] = date('Y-m-d', strtotime('+4 days', $monday));
	}
    else
    {
		// DATE END OF THE WEEK (Sunday)
		$dt[1] = date('Y-m-d', strtotime('+6 days', $monday));
	}

	return $dt;
} // END dateweek()


function DateArray($s,$e)
{
	$start = strtotime($s);
	$end = strtotime($e);
	$da = array();
	$loop = 0;
	while ($loop < 10000 && $start <= $end)
	{
		$loop++;
		$da[] = date('Y-m-d', $start);
		$start = strtotime('+1 day', $start);
	}
	return $da;
} // END DateArray()


function MonthsArray($s,$e)
{
	$start = date('Y-m-01', strtotime($s));
	$end = date('Y-m-01', strtotime($e));
    $mt = array();
	while ($start <= $end)
	{
		$mt[] = $start;
		$start = date('Y-m-01',strtotime("+1 month", strtotime($start)));
	}
    return $mt;
} // END MonthsArray()


function hesk_getOldestDate()
{
	global $hesk_settings, $hesklang, $date_from, $date_to;

	$res = hesk_dbQuery("SELECT `dt` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` ORDER BY `dt` ASC LIMIT 1");

    if (hesk_dbNumRows($res) == 1)
    {
		$row = hesk_dbFetchAssoc($res);
        return date('Y-m-d', strtotime($row['dt']) );
    }
    else
    {
    	return date('Y-m-d');
    }

} // END hesk_getOldestDate()
secimg.inc.php
wget 'https://lists2.roe3.org/hesk/inc/secimg.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}
#[AllowDynamicProperties]
class PJ_SecurityImage
{

        function __construct($key)
        {
                $this->code = '';
                $this->key = $key;
        } // End PJ_SecurityImage

        function encrypt($plain_text)
        {
            $this->code = trim(sha1($plain_text.$this->key));
        } // End encrypt

        function checkCode($mystring,$checksum)
        {
            $this->encrypt($mystring);
            if ($this->code == $checksum)
                return true;
            else
                return false;
        } // End checkCode

        function printImage($random_number)
        {
            $im = @imagecreate(150, 40) or die("Cannot Initialize new GD image stream");
            $background_color = imagecolorallocate($im, mt_rand(0,100), mt_rand(0,100), mt_rand(0,100));

			for ($i=0;$i<strlen($random_number);$i++)
			{
            	$text_color = imagecolorallocate($im, mt_rand(180,255), mt_rand(180,255), mt_rand(100,255));
				$display = substr($random_number,$i,1);
				$x = ($i*30) + mt_rand(3,16);
				$y = mt_rand(3,26);
				imagestring($im, 5, $x, $y, $display, $text_color);
			}

			if ( function_exists('imagejpeg') )
			{
				header("Content-type: image/jpeg");
				imagejpeg($im);
			}
			elseif ( function_exists('imagepng') )
			{
				header("Content-type: image/png");
				imagepng($im);
			}
			elseif ( function_exists('imagegif') )
			{
				header("Content-type: image/gif");
				imagegif($im);
			}
			else
			{
				die("GD was not compiled with JPEG or PNG support");
			}

            if (PHP_VERSION_ID < 80500) {
                imagedestroy($im);
            }
        } // End printImage

        function get()
        {
            return $this->code;
        } // End get

} // End class PJ_SecurityImage
setup_functions.inc.php
wget 'https://lists2.roe3.org/hesk/inc/setup_functions.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');} 

use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;

$hesk_settings['barcode_types'] = array(
    'C128A'      => 'CODE 128 A',
    'C128B'      => 'CODE 128 B',
    'C128'       => 'CODE 128',
    'C39E+'      => 'CODE 39 EXTENDED + CHECKSUM',
    'C39E'       => 'CODE 39 EXTENDED',
    'C39+'       => 'CODE 39 + CHECKSUM',
    'C39'        => 'CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.',
    'C93'        => 'CODE 93 - USS-93',
    'DATAMATRIX' => 'DATAMATRIX (ISO/IEC 16022)',
    'PDF417'     => 'PDF417 (ISO/IEC 15438:2006)',
    'QRCODE'     => 'QR-CODE',
);

$hesk_settings['barcode_formats'] = array(
    'svg' => 'SVG image',
    'png' => 'PNG image',
);

/*** FUNCTIONS ***/


function hesk_map_datepicker_date_format_to_php($format)
{
    $js_to_php_date_format_map = array(
        'dd'  => 'zzzz',
        'd'   => 'j',
        'DD'  => 'l',
        'D'   => 'D',
        'mm'  => 'wwww',
        'm'   => 'n',
        'MM'  => 'F',
        'M'   => 'M',
        'yyyy' => 'Y',
        'yy'   => 'y',

        // Trick to not overwrite d and m after matching dd and mm
        'zzzz' => 'd',
        'wwww' => 'm',
    );

    foreach ($js_to_php_date_format_map as $js_format => $php_format) {
        $format = str_replace($js_format, $php_format, $format);
    }

    return $format;
} // END hesk_map_datepicker_date_format_to_php()


function hesk_translate_timezone_list($timezone_list)
{
    global $hesklang;

    $translate_months_short = array(
        'Jan' => $hesklang['ms01'],
        'Feb' => $hesklang['ms02'],
        'Mar' => $hesklang['ms03'],
        'Apr' => $hesklang['ms04'],
        'May' => $hesklang['ms05'],
        'Jun' => $hesklang['ms06'],
        'Jul' => $hesklang['ms07'],
        'Aug' => $hesklang['ms08'],
        'Sep' => $hesklang['ms09'],
        'Oct' => $hesklang['ms10'],
        'Nov' => $hesklang['ms11'],
        'Dec' => $hesklang['ms12']
    );

    return str_replace(array_keys($translate_months_short), array_values($translate_months_short), $timezone_list);
} // END hesk_translate_timezone_list()


function hesk_generate_timezone_list()
{
    static $regions = array(
        DateTimeZone::AFRICA,
        DateTimeZone::AMERICA,
        DateTimeZone::ANTARCTICA,
        DateTimeZone::ASIA,
        DateTimeZone::ATLANTIC,
        DateTimeZone::AUSTRALIA,
        DateTimeZone::EUROPE,
        DateTimeZone::INDIAN,
        DateTimeZone::PACIFIC,
    );

    $timezones = array();
    foreach( $regions as $region )
    {
        $timezones = array_merge( $timezones, DateTimeZone::listIdentifiers( $region ) );
    }

    $timezone_offsets = array();
    foreach( $timezones as $timezone )
    {
        $tz = new DateTimeZone($timezone);
        $timezone_offsets[$timezone] = $tz->getOffset(new DateTime);
    }

    // sort timezone by timezone name
    ksort($timezone_offsets);
    //asort($timezone_offsets); // <-- use this to sort by time offset from UTC instead

    // Add UTC as the first element
    $timezone_offsets = array('UTC' => 0) + $timezone_offsets;

    $timezone_list = array();
    foreach( $timezone_offsets as $timezone => $offset )
    {
        $offset_prefix = $offset < 0 ? '-' : '+';
        $offset_formatted = gmdate( 'H:i', abs($offset) );

        $pretty_offset = "UTC{$offset_prefix}{$offset_formatted}";

        $t = new DateTimeZone($timezone);
        $c = new DateTime("now", $t);
        $current_time = $c->format('d M Y, H:i');

        $timezone_list[$timezone] = "{$timezone} - {$current_time}";
    }

    return $timezone_list;
} // END hesk_generate_timezone_list()


function hesk_testMySQL()
{
	global $hesk_settings, $hesklang, $set, $mysql_error, $mysql_log;

	define('REQUIRE_MYSQL_VERSION','5.0.7');

	// Use MySQLi extension to connect?
	$use_mysqli = function_exists('mysqli_connect') ? true : false;

	// Get variables
	$set['db_host'] = hesk_input( hesk_POST('s_db_host'), $hesklang['err_dbhost']);
	$set['db_name'] = hesk_input( hesk_POST('s_db_name'), $hesklang['err_dbname']);
	$set['db_user'] = hesk_input( hesk_POST('s_db_user'), $hesklang['err_dbuser']);
	$set['db_pass'] = hesk_input( hesk_POST('s_db_pass') );
	$set['db_pfix'] = preg_replace('/[^0-9a-zA-Z_]/', '', hesk_POST('s_db_pfix', 'hesk_') );

	// Allow some special chars in password and username
    $set['db_user'] = str_replace('&amp;', '&', $set['db_user']);
    $set['db_pass'] = str_replace(array('&amp;', '&gt;', '&lt;'), array('&', '>', '<'), $set['db_pass']);

	// MySQL tables used by HESK
	$tables = array(
        $set['db_pfix'].'attachments',
        $set['db_pfix'].'auth_tokens',
        $set['db_pfix'].'banned_emails',
        $set['db_pfix'].'banned_ips',
        $set['db_pfix'].'categories',
        $set['db_pfix'].'custom_fields',
        $set['db_pfix'].'custom_statuses',
        $set['db_pfix'].'kb_articles',
        $set['db_pfix'].'kb_attachments',
        $set['db_pfix'].'kb_categories',
        $set['db_pfix'].'logins',
        $set['db_pfix'].'log_overdue',
        $set['db_pfix'].'mail',
        $set['db_pfix'].'mfa_backup_codes',
        $set['db_pfix'].'mfa_verification_tokens',
        $set['db_pfix'].'notes',
        $set['db_pfix'].'online',
        $set['db_pfix'].'pipe_loops',
        $set['db_pfix'].'replies',
        $set['db_pfix'].'reply_drafts',
        $set['db_pfix'].'reset_password',
        $set['db_pfix'].'service_messages',
        $set['db_pfix'].'std_replies',
        $set['db_pfix'].'tickets',
        $set['db_pfix'].'ticket_templates',
        $set['db_pfix'].'users',
	);

	$connection_OK = false;
    $mysql_error = '';

	ob_start();

	// Connect to MySQL
	if ($use_mysqli)
	{
        mysqli_report(MYSQLI_REPORT_OFF);

		// Do we need a special port? Check and connect to the database
		if ( strpos($set['db_host'], ':') )
		{
			list($set['db_host_no_port'], $set['db_port']) = explode(':', $set['db_host']);
            try {
                $set_link = mysqli_connect($set['db_host_no_port'], $set['db_user'], $set['db_pass'], $set['db_name'], intval($set['db_port']) );
            } catch (Exception $e) {
                $set_link = false;
            }
		}
		else
		{
            try {
                $set_link = mysqli_connect($set['db_host'], $set['db_user'], $set['db_pass'], $set['db_name']);
            } catch (Exception $e) {
                $set_link = false;
            }
		}

		if (empty($set_link))
		{
			ob_end_clean();
			$mysql_error = $hesklang['err_dbconn'];
			$mysql_log = "(".mysqli_connect_errno().") ".mysqli_connect_error();
			return false;
		}

		$res = mysqli_query($set_link, 'SHOW TABLES FROM `'.mysqli_real_escape_string($set_link, $set['db_name']).'`');
		while ($row = mysqli_fetch_row($res))
		{
			foreach($tables as $k => $v)
			{
				if ($v == $row[0])
				{
					unset($tables[$k]);
					break;
				}
			}
		}

		// Get MySQL version
		$mysql_version = mysqli_fetch_assoc( mysqli_query($set_link, 'SELECT VERSION() AS version') );

		// Close connections
		mysqli_close($set_link);
	}
	else
	{
		$set_link = mysql_connect($set['db_host'], $set['db_user'], $set['db_pass']);

		if ( ! $set_link)
		{
			ob_end_clean();
			$mysql_error = $hesklang['err_dbconn'];
			$mysql_log = mysql_error();
			return false;
		}

		// Select database
		if ( ! mysql_select_db($set['db_name'], $set_link) )
		{
			ob_end_clean();
			$mysql_error = $hesklang['err_dbsele'];
			$mysql_log = mysql_error();
			return false;
		}

		$res = mysql_query('SHOW TABLES FROM `'.mysql_real_escape_string($set['db_name']).'`', $set_link);
		while ($row = mysql_fetch_row($res))
		{
			foreach($tables as $k => $v)
			{
				if ($v == $row[0])
				{
					unset($tables[$k]);
					break;
				}
			}
		}

		// Get MySQL version
		$mysql_version = mysql_fetch_assoc( mysql_query('SELECT VERSION() AS version') );

		// Close connections
		mysql_close($set_link);
	}

	// Check MySQL version
	if ( version_compare($mysql_version['version'], REQUIRE_MYSQL_VERSION, '<') )
	{
		ob_end_clean();
		$mysql_error = $hesklang['err_dbversion'] . ' ' . $mysql_version['version'];
		$mysql_log = '';
		return false;
	}

	// Some tables weren't found, show an error
	if (count($tables) > 0)
	{
    	ob_end_clean();
		$mysql_error = $hesklang['err_dpi2'].'<br /><br />'.implode(',<br />', $tables);
		$mysql_log = '';
		return false;
	}
    else
    {
    	$connection_OK = true;
    }

    ob_end_clean();

    return $connection_OK;
} // END hesk_testMySQL()


function hesk_testPOP3($check_old_settings=false)
{
	global $hesk_settings, $hesklang, $set;

	$set['pop3_host_name']	= hesk_input( hesk_POST('s_pop3_host_name', 'mail.example.com') );
	$set['pop3_host_port']	= intval( hesk_POST('s_pop3_host_port', 110) );
	$set['pop3_tls']		= empty($_POST['s_pop3_tls']) ? 0 : 1;
    $set['pop3_keep']		= empty($_POST['s_pop3_keep']) ? 0 : 1;
	$set['pop3_user']		= hesk_input( hesk_POST('s_pop3_user') );
	$set['pop3_password']	= hesk_input( hesk_POST('s_pop3_password') );
    $set['pop3_conn_type']  = hesk_input(hesk_POST('s_pop3_conn_type'));
    $set['pop3_oauth_provider'] = $set['pop3_conn_type'] === 'basic' ? 0 : intval(hesk_POST('s_pop3_oauth_provider'));

    // For compatibility with PHP 5.3 magic quotes...
    if (HESK_SLASH === false)
    {
        $set['pop3_password'] = str_replace('\\&quot;', '&quot;', $set['pop3_password']);
    }

	// Are new settings the same as old? If yes, skip testing connection, assume it works
	if ($check_old_settings)
	{
		$set['tmp_pop3_host_name']	= hesk_input( hesk_POST('tmp_pop3_host_name', 'mail.example.com') );
		$set['tmp_pop3_host_port']	= intval( hesk_POST('tmp_pop3_host_port', 110) );
		$set['tmp_pop3_tls']		= empty($_POST['tmp_pop3_tls']) ? 0 : 1;
		$set['tmp_pop3_keep']		= empty($_POST['tmp_pop3_keep']) ? 0 : 1;
		$set['tmp_pop3_user']		= hesk_input( hesk_POST('tmp_pop3_user') );
		$set['tmp_pop3_password']	= hesk_input( hesk_POST('tmp_pop3_password') );
        $set['tmp_pop3_conn_type']  = hesk_input(hesk_POST('tmp_pop3_conn_type'));
        $set['tmp_pop3_oauth_provider']  = $set['tmp_pop3_conn_type'] === 'basic' ? 0 : intval(hesk_POST('tmp_pop3_oauth_provider'));

        // For compatibility with PHP 5.3 magic quotes...
        if (HESK_SLASH === false)
        {
            $set['tmp_pop3_password'] = str_replace('\\&quot;', '&quot;', $set['tmp_pop3_password']);
        }

		if (
			$set['tmp_pop3_host_name'] != 'mail.example.com'      && // Default setting
			$set['tmp_pop3_host_name'] == $set['pop3_host_name'] &&
			$set['tmp_pop3_host_port'] == $set['pop3_host_port'] &&
			$set['tmp_pop3_tls']       == $set['pop3_tls']       &&
			$set['tmp_pop3_keep']      == $set['pop3_keep']      &&
			$set['tmp_pop3_user']      == $set['pop3_user']      &&
			$set['tmp_pop3_password']  == $set['pop3_password']  &&
            $set['tmp_pop3_conn_type'] == $set['pop3_conn_type'] &&
            $set['tmp_pop3_oauth_provider'] == $set['pop3_oauth_provider']
		)
		{
			return true;
		}
	}

	// Initiate POP3 class and set parameters
	require_once(HESK_PATH . 'inc/mail/pop3.php');
	$pop3 = new pop3_class;
	$pop3->hostname	= $set['pop3_host_name'];
	$pop3->port		= $set['pop3_host_port'];
	$pop3->tls		= $set['pop3_tls'];
	$pop3->debug	= 1;

    if ($set['pop3_conn_type']=='oauth') {
        require_once(HESK_PATH . 'inc/oauth_functions.inc.php');
        $pop3->authentication_mechanism = 'XOAUTH2';
        hesk_dbConnect();
        $access_token = hesk_fetch_access_token($set['pop3_oauth_provider']);
        if (!$access_token) {
            global $pop3_error, $pop3_log;
            $pop3_error = $hesklang['oauth_error_retrieve'];
            $pop3_log = $hesklang['oauth_error_retrieve'];
            return false;
        }
    }

	$connection_OK = false;

	ob_start();

	// Connect to POP3
	if(($error=$pop3->Open())=="")
	{
		// Authenticate
		if(($error=$pop3->Login($set['pop3_user'], ($set['pop3_conn_type']=='oauth' ? $access_token : hesk_htmlspecialchars_decode(stripslashes($set['pop3_password'])))))=="")
		{
            // Get number of messages and total size
            if(($error=$pop3->Statistics($messages,$size))=="")
            {
                global $emails_found;
                $emails_found = $messages;

                if(($error=$pop3->Close()) == "")
                {
                    // Connection OK
                    $connection_OK = true;
                }
            }
		}
	}

	if($error != '')
	{
    	global $pop3_error, $pop3_log;
        $pop3_error = $error;
		$pop3_log   = ob_get_contents();
	}

	ob_end_clean();

    return $connection_OK;
} // END hesk_testPOP3()


function hesk_testSMTP($check_old_settings=false)
{
	global $hesk_settings, $hesklang, $set;

	// Get variables
	$set['smtp_host_name']	= hesk_input( hesk_POST('s_smtp_host_name', 'localhost') );
	$set['smtp_host_port']	= intval( hesk_POST('s_smtp_host_port', 25) );
	$set['smtp_timeout']	= intval( hesk_POST('s_smtp_timeout', 10) );
    $set['smtp_enc']        = hesk_POST('s_smtp_enc');
    $set['smtp_enc']        = ($set['smtp_enc'] == 'ssl' || $set['smtp_enc'] == 'tls') ? $set['smtp_enc'] : '';
    $set['smtp_noval_cert'] = empty($_POST['s_smtp_noval_cert']) ? 0 : 1;
	$set['smtp_user']		= hesk_input( hesk_POST('s_smtp_user') );
	$set['smtp_password']	= hesk_input( hesk_POST('s_smtp_password') );
    $set['smtp_conn_type']  = hesk_input(hesk_POST('s_smtp_conn_type'));
    $set['smtp_oauth_provider']  = $set['smtp_conn_type'] === 'basic' ? 0 : intval(hesk_POST('s_smtp_oauth_provider'));

    // For compatibility with PHP 5.3 magic quotes...
    if (HESK_SLASH === false)
    {
        $set['smtp_password'] = str_replace('\\&quot;', '&quot;', $set['smtp_password']);
    }

	// Are new settings the same as old? If yes, skip testing connection, assume it works
	if ($check_old_settings)
	{
		$set['tmp_smtp_host_name']	= hesk_input( hesk_POST('tmp_smtp_host_name', 'localhost') );
		$set['tmp_smtp_host_port']	= intval( hesk_POST('tmp_smtp_host_port', 25) );
		$set['tmp_smtp_timeout']	= intval( hesk_POST('tmp_smtp_timeout', 10) );
        $set['tmp_smtp_enc']        = hesk_POST('tmp_smtp_enc');
        $set['tmp_smtp_enc']        = ($set['tmp_smtp_enc'] == 'ssl' || $set['tmp_smtp_enc'] == 'tls') ? $set['tmp_smtp_enc'] : '';
        $set['tmp_smtp_noval_cert'] = empty($_POST['tmp_smtp_noval_cert']) ? 0 : 1;
		$set['tmp_smtp_user']		= hesk_input( hesk_POST('tmp_smtp_user') );
		$set['tmp_smtp_password']	= hesk_input( hesk_POST('tmp_smtp_password') );
        $set['tmp_smtp_conn_type']  = hesk_input(hesk_POST('tmp_smtp_conn_type'));
        $set['tmp_smtp_oauth_provider']  = $set['tmp_smtp_conn_type'] === 'basic' ? 0 : intval(hesk_POST('tmp_smtp_oauth_provider'));

        // For compatibility with PHP 5.3 magic quotes...
        if (HESK_SLASH === false)
        {
            $set['tmp_smtp_password'] = str_replace('\\&quot;', '&quot;', $set['tmp_smtp_password']);
        }

		if (
			$set['tmp_smtp_host_name'] != 'mail.example.com'      && // Default setting
			$set['tmp_smtp_host_name'] == $set['smtp_host_name'] &&
			$set['tmp_smtp_host_port'] == $set['smtp_host_port'] &&
			$set['tmp_smtp_timeout']   == $set['smtp_timeout']   &&
			$set['tmp_smtp_enc']       == $set['smtp_enc']       &&
			$set['tmp_smtp_noval_cert'] == $set['smtp_noval_cert'] &&
			$set['tmp_smtp_user']      == $set['smtp_user']      &&
			$set['tmp_smtp_password']  == $set['smtp_password'] &&
            $set['tmp_smtp_conn_type'] == $set['smtp_conn_type'] &&
            $set['tmp_smtp_oauth_provider'] == $set['smtp_oauth_provider']
		)
		{
			return true;
		}
	}

    ob_start();

    //Create a new SMTP instance
    $smtp = new SMTP();

    //Enable connection-level debug output
    if ($hesk_settings['debug_mode']) {
        $smtp->do_debug = SMTP::DEBUG_CONNECTION;
        // $smtp->do_debug = SMTP::DEBUG_LOWLEVEL;
    } else {
        $smtp->do_debug = SMTP::DEBUG_SERVER;
    }
    $smtp->Timeout = $set['smtp_timeout'];
    $smtp->Timelimit = $set['smtp_timeout'];

    if ($set['smtp_noval_cert']) {
        $options = array(
          'ssl' => array(
              'verify_peer' => false,
              'verify_peer_name' => false,
              'allow_self_signed' => true
          )
        );
    } else {
        $options = array();
    }

    if (stripos($set['smtp_host_name'], 'ssl://') === 0) {
        $set['smtp_host_name'] = substr($set['smtp_host_name'], 6);
    }

    $set['smtp_host_name_full'] = ($set['smtp_enc'] == 'ssl') ? 'ssl://' . $set['smtp_host_name'] : $set['smtp_host_name'];

    try {
        //Connect to an SMTP server
        if (!$smtp->connect($set['smtp_host_name_full'], $set['smtp_host_port'], $set['smtp_timeout'], $options)) {
            throw new Exception('Connect failed');
        }
        //Say hello
        if (!$smtp->hello(gethostname())) {
            throw new Exception('EHLO failed: ' . $smtp->getError()['error']);
        }
        //Get the list of ESMTP services the server offers
        $e = $smtp->getServerExtList();

        if ($set['smtp_enc'] == 'tls' && is_array($e)) {
            if ( ! array_key_exists('STARTTLS', $e)) {
                throw new Exception('Server does not support STARTTLS');
            }
            $tlsok = $smtp->startTLS();
            if (!$tlsok) {
                throw new Exception('Failed to start encryption: ' . $smtp->getError()['error']);
            }
            //Repeat EHLO after STARTTLS
            if (!$smtp->hello(gethostname())) {
                throw new Exception('EHLO (2) failed: ' . $smtp->getError()['error']);
            }
            //Get new capabilities list, which will usually now include AUTH if it didn't before
            $e = $smtp->getServerExtList();
        }

        //If server supports authentication, do it (even if no encryption)
        if (is_array($e) && array_key_exists('AUTH', $e)) {
            if ($set['smtp_conn_type']=='oauth') {
                require_once(HESK_PATH . 'inc/oauth_functions.inc.php');
                require_once(HESK_PATH . 'inc/mail/HeskOAuthTokenProvider.php');

                $oauthTokenProvider = new \PHPMailer\PHPMailer\HeskOAuthTokenProvider();
                $oauthTokenProvider->username = $set['smtp_user'];
                $oauthTokenProvider->provider = $set['smtp_oauth_provider'];

                if ($smtp->authenticate($set['smtp_user'], null, 'XOAUTH2', $oauthTokenProvider)) {
                    echo 'Connected ok (OAuth)!';
                } else {
                    throw new Exception('Authentication failed: ' . $smtp->getError()['error']);
                }

            } elseif ($smtp->authenticate($set['smtp_user'], hesk_htmlspecialchars_decode(stripslashes($set['smtp_password'])))) {
                echo 'Connected ok!';
            } else {
                throw new Exception('Authentication failed: ' . $smtp->getError()['error']);
            }
        }
    } catch (Exception $e) {
        global $smtp_error, $smtp_log;
        $smtp_error = $e->getMessage();
        $smtp_log = ob_get_contents();
        $smtp->quit();
        ob_end_clean();
        return false;
    }

    $smtp->quit();
    ob_end_clean();
    return true;
} // END hesk_testSMTP()


function hesk_testIMAP($check_old_settings=false)
{
	global $hesk_settings, $hesklang, $set;

	$set['imap_host_name']	= hesk_input( hesk_POST('s_imap_host_name', 'mail.example.com') );
	$set['imap_host_port']	= intval( hesk_POST('s_imap_host_port', 993) );
	$set['imap_enc']		= hesk_POST('s_imap_enc');
	$set['imap_enc']        = ($set['imap_enc'] == 'ssl' || $set['imap_enc'] == 'tls') ? $set['imap_enc'] : '';
	$set['imap_noval_cert'] = empty($_POST['s_imap_noval_cert']) ? 0 : 1;
    $set['imap_disable_GSSAPI'] = empty($_POST['s_imap_disable_GSSAPI']) ? 0 : 1;
	$set['imap_keep']		= empty($_POST['s_imap_keep']) ? 0 : 1;
	$set['imap_user']		= hesk_input( hesk_POST('s_imap_user') );
	$set['imap_password']	= hesk_input( hesk_POST('s_imap_password') );
    $set['imap_conn_type']  = hesk_input(hesk_POST('s_imap_conn_type'));
    $set['imap_oauth_provider']  = $set['imap_conn_type'] === 'basic' ? 0 : intval(hesk_POST('s_imap_oauth_provider'));
    $set['imap_mailbox']	= hesk_input( hesk_POST('s_imap_mailbox', 'INBOX')); // Added for IMAP Mailbox
    // For compatibility with PHP 5.3 magic quotes...
    if (HESK_SLASH === false)
    {
        $set['imap_password'] = str_replace('\\&quot;', '&quot;', $set['imap_password']);
    }

	// Are new settings the same as old? If yes, skip testing connection, assume it works
	if ($check_old_settings)
	{
		$set['tmp_imap_host_name']	= hesk_input( hesk_POST('tmp_imap_host_name', 'mail.example.com') );
		$set['tmp_imap_host_port']	= intval( hesk_POST('tmp_imap_host_port', 993) );
		$set['tmp_imap_enc']		= hesk_POST('s_imap_enc');
		$set['tmp_imap_enc']        = ($set['tmp_imap_enc'] == 'ssl' || $set['tmp_imap_enc'] == 'tls') ? $set['tmp_imap_enc'] : '';
        $set['tmp_imap_noval_cert'] = empty($_POST['tmp_imap_noval_cert']) ? 0 : 1;
        $set['tmp_imap_disable_GSSAPI'] = empty($_POST['tmp_imap_disable_GSSAPI']) ? 0 : 1;
		$set['tmp_imap_keep']		= empty($_POST['tmp_imap_keep']) ? 0 : 1;
		$set['tmp_imap_user']		= hesk_input( hesk_POST('tmp_imap_user') );
		$set['tmp_imap_password']	= hesk_input( hesk_POST('tmp_imap_password') );
        $set['tmp_imap_conn_type']  = hesk_input(hesk_POST('tmp_imap_conn_type'));
        $set['tmp_imap_oauth_provider']  = $set['tmp_imap_conn_type'] === 'basic' ? 0 : intval(hesk_POST('tmp_imap_oauth_provider'));
        $set['tmp_imap_mailbox']	= hesk_input( hesk_POST('s_imap_mailbox', 'INBOX')); // Added for IMAP Mailbox

        // For compatibility with PHP 5.3 magic quotes...
        if (HESK_SLASH === false)
        {
            $set['tmp_imap_password'] = str_replace('\\&quot;', '&quot;', $set['tmp_imap_password']);
        }

		if (
			$set['tmp_imap_host_name'] != 'mail.example.com'      && // Default setting
			$set['tmp_imap_host_name'] == $set['imap_host_name'] &&
			$set['tmp_imap_host_port'] == $set['imap_host_port'] &&
			$set['tmp_imap_enc']       == $set['imap_enc']       &&
			$set['tmp_imap_noval_cert'] == $set['imap_noval_cert'] &&
            $set['tmp_imap_disable_GSSAPI'] == $set['imap_disable_GSSAPI'] &&
			$set['tmp_imap_keep']      == $set['imap_keep']      &&
			$set['tmp_imap_user']      == $set['imap_user']      &&
			$set['tmp_imap_password']  == $set['imap_password']  &&
            $set['tmp_imap_conn_type'] == $set['imap_conn_type'] &&
            $set['tmp_imap_oauth_provider'] == $set['imap_oauth_provider'] &&
            $set['tmp_imap_mailbox'] == $set['imap_mailbox'] // Added for IMAP Mailbox
		)
		{
			return true;
		}
	}

    $connection_OK = false;

    ob_start();

    // IMAP mailbox based on required encryption
    require_once(HESK_PATH . 'inc/mail/imap/HeskIMAP.php');
    $imap = new HeskIMAP();

    $imap->host = $set['imap_host_name'];
    $imap->port = $set['imap_host_port'];
    $imap->username = $set['imap_user'];
    if ($set['imap_conn_type'] === 'basic') {
        $imap->password = hesk_htmlspecialchars_decode(stripslashes($set['imap_password']));
        $imap->useOAuth = false;
    } elseif ($set['imap_conn_type'] === 'oauth') {
        require_once(HESK_PATH . 'inc/oauth_functions.inc.php');
        $access_token = hesk_fetch_access_token($set['imap_oauth_provider']);
        if (!$access_token) {
            global $imap_error, $imap_log;
            $imap_error = $hesklang['oauth_error_retrieve'];
            $imap_log = $hesklang['oauth_error_retrieve'];
            return false;
        }

        $imap->accessToken = $access_token;
        $imap->useOAuth = true;
        $imap->password = null;
    }

    $imap->readOnly = false;
    $imap->ignoreCertificateErrors = $set['imap_noval_cert'];
    $imap->disableGSSAPI = $set['imap_disable_GSSAPI'];
    $imap->connectTimeout = 15;
    $imap->responseTimeout = 15;
    $imap->imap_mailbox = $set['imap_mailbox'];// Added for IMAP Mailbox
    $imap->folder = $set['imap_mailbox']; //Change for IMAP Mailbox;

    if ($set['imap_enc'] === 'ssl')
    {
        $imap->ssl = true;
        $imap->tls = false;
    }
    elseif ($set['imap_enc'] === 'tls')
    {
        $imap->ssl = false;
        $imap->tls = true;
    }
    else
    {
        $imap->ssl = false;
        $imap->tls = false;
    }

    if ($imap->login())
    {
        global $emails_found;
        $emails_found = 0;
        echo $hesk_settings['debug_mode'] ? "<pre>Connected to the IMAP server &quot;" . $imap->host . ":" . $imap->port . "&quot;.</pre>\n" : '';

        $has_unseen_message = $imap->hasUnseenMessages();
        if (is_int($has_unseen_message)) {
            $emails = $imap->getUnseenMessageIDs();
            $emails_found = count($emails);
        }

        $imap->logout();
    }

    // Any error messages?
    if($errors = $imap->getErrors())
    {
        global $imap_error, $imap_log;

        $imap_error = end($errors);
        reset($errors);

        $imap_log = '';

        foreach ($errors as $error)
        {
            $imap_log .= hesk_htmlspecialchars($error) . "\n";
        }
    }
    else
    {
        $connection_OK = true;
    }

    ob_end_clean();

    return $connection_OK;
} // END hesk_testIMAP()


function hesk_generate_SPAM_question()
{
	$useChars = 'AEUYBDGHJLMNPRSTVWXZ23456789';
	$ac = $useChars[mt_rand(0,27)];
	for($i=1;$i<5;$i++)
	{
	    $ac .= $useChars[mt_rand(0,27)];
	}

    $animals = array('dog','cat','cow','pig','elephant','tiger','chicken','bird','fish','alligator','monkey','mouse','lion','turtle','crocodile','duck','gorilla','horse','penguin','dolphin','rabbit','sheep','snake','spider');
    $not_animals = array('ball','window','house','tree','earth','money','rocket','sun','star','shirt','snow','rain','air','candle','computer','desk','coin','TV','paper','bell','car','baloon','airplane','phone','water','space');

    $keys = array_rand($animals,2);
    $my_animals[] = $animals[$keys[0]];
    $my_animals[] = $animals[$keys[1]];

    $keys = array_rand($not_animals,2);
    $my_not_animals[] = $not_animals[$keys[0]];
    $my_not_animals[] = $not_animals[$keys[1]];

	$my_animals[] = $my_not_animals[0];
    $my_not_animals[] = $my_animals[0];

    $e = mt_rand(1,9);
    $f = $e + 1;
    $d = mt_rand(1,9);
    $s = intval($e + $d);

    if ($e == $d)
    {
    	$d ++;
    	$h = $d;
        $l = $e;
    }
    elseif ($e < $d)
    {
    	$h = $d;
        $l = $e;
    }
    else
    {
    	$h = $e;
        $l = $d;
    }

    $spam_questions = array(
    	$f => 'What is the next number after '.$e.'? (Use only digits to answer)',
    	'white' => 'What color is snow? (give a 1 word answer to show you are a human)',
    	'green' => 'What color is grass? (give a 1 word answer to show you are a human)',
    	'blue' => 'What color is water? (give a 1 word answer to show you are a human)',
    	$ac => 'Access code (type <b>'.$ac.'</b> here):',
    	$ac => 'Type <i>'.$ac.'</i> here to fight SPAM:',
    	$s => 'Solve this equation to show you are human: '.$e.' + '.$d.' = ',
    	$my_animals[2] => 'Which of these is not an animal: ' . implode(', ',hesk_randomize_array($my_animals)),
    	$my_not_animals[2] => 'Which of these is an animal: ' . implode(', ',hesk_randomize_array($my_not_animals)),
    	$h => 'Which number is higher <b>'.$e.'</b> or <b>'.$d.'</b>:',
    	$l => 'Which number is lower <b>'.$e.'</b> or <b>'.$d.'</b>:',
        'no' => 'Are you a robot? (yes or no)',
        'yes' => 'Are you a human? (yes or no)'
    );

    $r = array_rand($spam_questions);
	$ask = $spam_questions[$r];
    $ans = $r;

    return array($ask,$ans);
} // END hesk_generate_SPAM_question()


function hesk_randomize_array($array)
{
	$rand_items = array_rand($array, count($array));
	$new_array = array();
	foreach($rand_items as $value)
	{
	    $new_array[$value] = $array[$value];
	}

	return $new_array;
} // END hesk_randomize_array()


function hesk_checkMinMax($myint,$min,$max,$defval)
{
	if ($myint > $max || $myint < $min)
	{
		return $defval;
	}
	return $myint;
} // END hesk_checkMinMax()
show_admin_nav.inc.php
wget 'https://lists2.roe3.org/hesk/inc/show_admin_nav.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');} 

$num_mail = hesk_checkNewMail();

// Name of the page that is being requested, without '.php' at the end
$calling_script = basename($_SERVER['PHP_SELF'], '.php');
?>
<!-- NEW DESIGN -->
<aside class="main-menu">
    <nav class="navbar">
        <div class="navbar__header">
            <button class="btn navbar__toggler" id="navbarToggler" type="button" aria-label="<?php echo $hesklang['toggle_navigation']; ?>">
                <svg class="icon icon-menu">
                    <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-menu"></use>
                </svg>
            </button>
            <a class="navbar__logo" href="admin_main.php">
                <?php echo $hesklang['help_desk']; ?>
            </a>
        </div>
        <div class="navbar__menu-wrap">
            <ul class="navbar__list">
                <?php
                    $pages = array('admin_main', 'show_tickets', 'find_tickets');
                    $open_menu = in_array($calling_script, $pages) ? 'current' : '';
                ?>
                <li class="listitem <?php echo $open_menu; ?>">
                    <div class="listitem__icon">
                        <a href="admin_main.php" aria-label="<?php echo $hesklang['tickets']; ?>">
                            <svg class="icon icon-tickets">
                                <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-tickets"></use>
                            </svg>
                        </a>
                    </div>
                    <div class="listitem__menu">
                        <a href="admin_main.php" class="listitem__caption">
                            <?php echo $hesklang['tickets']; ?>
                        </a>
                    </div>
                </li>
                <?php if (hesk_checkPermission('can_man_canned',0) &&
                          hesk_checkPermission('can_man_ticket_tpl',0)) {
                    $pages = array('manage_canned', 'manage_ticket_templates');
                    $open_menu = in_array($calling_script, $pages) ? 'current submenu-is-opened' : '';
                    ?>
                <li class="listitem submenu <?php echo $open_menu; ?>">
                    <div class="listitem__icon">
                        <a href="#" aria-label="<?php echo $hesklang['responses']; ?>">
                            <svg class="icon icon-templates">
                                <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-templates"></use>
                            </svg>
                        </a>
                    </div>
                    <div class="listitem__menu">
                        <a href="#" class="listitem__caption"><?php echo $hesklang['nav_templates']; ?></a>
                        <ul class="submenu__list">
                            <li class="submenu__listitem <?php if ($calling_script === 'manage_canned') { ?>current<?php } ?>">
                                <a href="manage_canned.php">
                                    <?php echo $hesklang['responses']; ?>
                                </a>
                            </li>
                            <li class="submenu__listitem <?php if ($calling_script === 'manage_ticket_templates') { ?>current<?php } ?>">
                                <a href="manage_ticket_templates.php">
                                    <?php echo $hesklang['tickets']; ?>
                                </a>
                            </li>
                        </ul>
                    </div>
                </li>
                <?php } elseif (hesk_checkPermission('can_man_canned',0)) { ?>
                    <li class="listitem <?php if ($calling_script === 'manage_canned') { ?>current<?php } ?>">
                        <div class="listitem__icon">
                            <a href="manage_canned.php" aria-label="<?php echo $hesklang['responses']; ?>">
                                <svg class="icon icon-tickets">
                                    <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-templates"></use>
                                </svg>
                            </a>
                        </div>
                        <div class="listitem__menu">
                            <a href="manage_canned.php" class="listitem__caption">
                                <?php echo $hesklang['responses']; ?>
                            </a>
                        </div>
                    </li>
                <?php } elseif (hesk_checkPermission('can_man_ticket_tpl',0)) { ?>
                    <li class="listitem <?php if ($calling_script === 'manage_ticket_templates') { ?>current<?php } ?>">
                        <div class="listitem__icon">
                            <a href="manage_ticket_templates.php" aria-label="<?php echo $hesklang['nav_templates']; ?>">
                                <svg class="icon icon-tickets">
                                    <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-templates"></use>
                                </svg>
                            </a>
                        </div>
                        <div class="listitem__menu">
                            <a href="manage_ticket_templates.php" class="listitem__caption">
                                <?php echo $hesklang['nav_templates']; ?>
                            </a>
                        </div>
                    </li>
                <?php
                }

                if ($hesk_settings['kb_enable'] && hesk_checkPermission('can_man_kb',0)) {
                    $pages = array('manage_knowledgebase', 'knowledgebase_private');
                    $open_menu = in_array($calling_script, $pages) ? 'current submenu-is-opened' : '';
                    ?>
                    <li class="listitem submenu <?php echo $open_menu; ?>">
                        <div class="listitem__icon">
                            <a href="manage_knowledgebase.php" aria-label="<?php echo $hesklang['menu_kb_manage']; ?>">
                                <svg class="icon icon-knowledge">
                                    <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-knowledge"></use>
                                </svg>
                            </a>
                        </div>
                        <div class="listitem__menu">
                            <a href="#" class="listitem__caption"><?php echo $hesklang['menu_kb']; ?></a>
                            <ul class="submenu__list">
                                <li class="submenu__listitem <?php if ($calling_script === 'manage_knowledgebase') { ?>current<?php } ?>">
                                    <a href="manage_knowledgebase.php">
                                        <?php echo $hesklang['menu_kb_manage']; ?>
                                    </a>
                                </li>
                                <li class="submenu__listitem <?php if ($calling_script === 'knowledgebase_private') { ?>current<?php } ?>">
                                    <a href="knowledgebase_private.php">
                                        <?php echo $hesklang['menu_kb_view']; ?>
                                    </a>
                                </li>
                            </ul>
                        </div>
                    </li>
                    <?php
                } elseif ($hesk_settings['kb_enable']) {
                    ?>
                    <li class="listitem <?php if ($calling_script === 'knowledgebase_private') { ?>current<?php } ?>">
                        <div class="listitem__icon">
                            <a href="knowledgebase_private.php" aria-label="<?php echo $hesklang['menu_kb']; ?>">
                                <svg class="icon icon-knowledge">
                                    <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-knowledge"></use>
                                </svg>
                            </a>
                        </div>
                        <div class="listitem__menu">
                            <a href="knowledgebase_private.php" class="listitem__caption">
                                <?php echo $hesklang['menu_kb']; ?>
                            </a>
                        </div>
                    </li>
                    <?php
                }
                ?>
                <li class="listitem <?php if ($calling_script === 'manage_categories') { ?>current<?php } ?>">
                    <div class="listitem__icon">
                        <a href="manage_categories.php" aria-label="<?php echo $hesklang['menu_cat']; ?>">
                            <svg class="icon icon-categories">
                                <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-categories"></use>
                            </svg>
                        </a>
                    </div>
                    <div class="listitem__menu">
                        <a href="manage_categories.php" class="listitem__caption">
                            <?php echo $hesklang['menu_cat']; ?>
                        </a>
                    </div>
                </li>
                <li class="separator"></li>
                <?php
                $number_of_accessible_pages = 0;
                if (hesk_checkPermission('can_man_users',0) || hesk_checkPermission('can_view_users',0)) {
                    $number_of_accessible_pages++;
                }
                if (hesk_checkPermission('can_man_customers',0) || hesk_checkPermission('can_view_customers',0)) {
                    $number_of_accessible_pages++;
                }
                if (hesk_checkPermission('can_man_permission_groups',0)) {
                    $number_of_accessible_pages++;
                }

                if ($number_of_accessible_pages > 1):
                    $pages = array('manage_users', 'manage_customers', 'import_customers', 'manage_permission_groups');
                    $open_menu = in_array($calling_script, $pages) ? 'current submenu-is-opened' : '';
                ?>
                    <li class="listitem submenu <?php echo $open_menu; ?>">
                        <div class="listitem__icon">
                            <a href="#" aria-label="<?php echo $hesklang['team']; ?>">
                                <svg class="icon icon-team">
                                    <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-team"></use>
                                </svg>
                            </a>
                        </div>
                        <div class="separator"></div>
                        <div class="listitem__menu">
                            <?php
                            $show_badge = 0;
                            if ((hesk_checkPermission('can_man_customers', 0) || hesk_checkPermission('can_man_customers', 0)) && $hesk_settings['customer_accounts_admin_approvals']) {
                                $pending_customers = hesk_dbQuery("SELECT 1 FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."customers`
                                        WHERE `verified` = 2");
                                $show_badge = hesk_dbNumRows($pending_customers);
                            }
                            ?>
                            <a href="#" class="listitem__caption">
                                <?php
                                echo $hesklang['menu_users'];
                                if ($show_badge): ?>
                                    <span class="admin-nav-badge"><?php echo $show_badge; ?></span>
                                <?php endif; ?>
                            </a>
                            <ul class="submenu__list">
                                <?php if (hesk_checkPermission('can_man_users',0) || hesk_checkPermission('can_view_users',0)): ?>
                                <li class="submenu__listitem <?php if ($calling_script === 'manage_users') { ?>current<?php } ?>">
                                    <a href="manage_users.php">
                                        <?php echo $hesklang['team']; ?>
                                    </a>
                                </li>
                                <?php
                                endif;
                                if (hesk_checkPermission('can_man_customers',0) || hesk_checkPermission('can_view_customers',0)):
                                ?>
                                <li class="submenu__listitem <?php if (in_array($calling_script, ['manage_customers','import_customers'])) { ?>current<?php } ?>">
                                    <a href="manage_customers.php">
                                        <?php
                                        echo $hesklang['customers'];

                                        if ($show_badge > 0):
                                        ?>
                                        <span class="admin-nav-badge"><?php echo $show_badge; ?></span>
                                        <?php endif; ?>
                                    </a>
                                </li>
                                <?php
                                endif;
                                if (hesk_checkPermission('can_man_permission_groups',0)):
                                ?>
                                <li class="submenu__listitem <?php if ($calling_script === 'manage_permission_groups') { ?>current<?php } ?>">
                                    <a href="manage_permission_groups.php">
                                        <?php echo $hesklang['permission_groups_title']; ?>
                                    </a>
                                </li>
                                <?php endif; ?>
                            </ul>
                        </div>
                    </li>
                <?php else: ?>
                    <?php if (hesk_checkPermission('can_man_users',0) || hesk_checkPermission('can_view_users',0)) { ?>
                    <li class="listitem <?php if (in_array($calling_script, ['manage_users'])) { ?>current<?php } ?>">
                        <div class="listitem__icon">
                            <a href="manage_users.php" aria-label="<?php echo $hesklang['team']; ?>">
                                <svg class="icon icon-team">
                                    <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-team"></use>
                                </svg>
                            </a>
                        </div>
                        <div class="listitem__menu">
                            <a href="manage_users.php" class="listitem__caption">
                                <?php echo $hesklang['team']; ?>
                            </a>
                        </div>
                    </li>
                    <?php
                    }
                    if (hesk_checkPermission('can_man_customers',0) || hesk_checkPermission('can_view_customers',0)) { ?>
                        <li class="listitem <?php if (in_array($calling_script, ['manage_customers','import_customers'])) { ?>current<?php } ?>">
                            <div class="listitem__icon">
                                <a href="manage_customers.php" aria-label="<?php echo $hesklang['customers']; ?>">
                                    <svg class="icon icon-team">
                                        <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-team"></use>
                                    </svg>
                                </a>
                            </div>
                            <div class="listitem__menu">
                                <a href="manage_customers.php" class="listitem__caption">
                                    <?php echo $hesklang['customers']; ?>
                                </a>
                            </div>
                        </li>
                        <?php
                    }
                    if (hesk_checkPermission('can_man_permission_groups',0)) { ?>
                        <li class="listitem <?php if ($calling_script === 'manage_permission_groups') { ?>current<?php } ?>">
                            <div class="listitem__icon">
                                <a href="manage_permission_groups.php" aria-label="<?php echo $hesklang['permission_groups_title']; ?>">
                                    <svg class="icon icon-team">
                                        <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-team"></use>
                                    </svg>
                                </a>
                            </div>
                            <div class="listitem__menu">
                                <a href="manage_permission_groups.php" class="listitem__caption">
                                    <?php echo $hesklang['permission_groups_title']; ?>
                                </a>
                            </div>
                        </li>
                        <?php
                    }
                endif;

                //Reports
                if (hesk_checkPermission('can_run_reports',0)) {
                    $pages = array('reports', 'export');
                    $open_menu = in_array($calling_script, $pages) ? 'current submenu-is-opened' : '';
                ?>
                <li class="listitem submenu <?php echo $open_menu; ?>">
                    <div class="listitem__icon">
                        <a href="#" aria-label="<?php echo $hesklang['reports_tab']; ?>">
                            <svg class="icon icon-reports">
                                <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-reports"></use>
                            </svg>
                        </a>
                    </div>
                    <div class="separator"></div>
                    <div class="listitem__menu">
                        <a href="#" class="listitem__caption"><?php echo $hesklang['reports']; ?></a>
                        <ul class="submenu__list">
                            <li class="submenu__listitem <?php if ($calling_script === 'reports') { ?>current<?php } ?>">
                                <a href="reports.php">
                                    <?php echo $hesklang['reports_tab']; ?>
                                </a>
                            </li>
                            <?php
                            if (hesk_checkPermission('can_export',0)) {
                                ?>
                                <li class="submenu__listitem <?php if ($calling_script === 'export') { ?>current<?php } ?>">
                                    <a href="export.php">
                                        <?php echo $hesklang['export']; ?>
                                    </a>
                                </li>
                                <?php
                            }
                            ?>
                        </ul>
                    </div>
                </li>
                <?php
                } elseif (hesk_checkPermission('can_export',0)) {
                    ?>
                    <li class="listitem <?php if ($calling_script === 'export') { ?>current<?php } ?>">
                        <div class="listitem__icon">
                            <a href="export.php" aria-label="<?php echo $hesklang['export']; ?>">
                                <svg class="icon icon-team">
                                    <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-team"></use>
                                </svg>
                            </a>
                        </div>
                        <div class="listitem__menu">
                            <a href="export.php" class="listitem__caption">
                                <?php echo $hesklang['export']; ?>
                            </a>
                        </div>
                    </li>
                    <?php
                }

                // Modules
                if (hesk_checkPermission('can_run_reports',0) ||
                    hesk_checkPermission('can_man_settings',0)) {
                    $pages = array('module_statistics', 'module_escalate', 'module_satisfaction', 'module_satisfaction_optout', 'module_recurring_tickets');
                    $open_menu = in_array($calling_script, $pages) ? 'current submenu-is-opened' : '';
                ?>
                <li class="listitem submenu <?php echo $open_menu; ?>">
                    <div class="listitem__icon">
                        <a href="#" aria-label="<?php echo $hesklang['modules']; ?>">
                            <svg class="icon icon-modules">
                                <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-modules"></use>
                            </svg>
                        </a>
                    </div>
                    <div class="listitem__menu">
                        <a href="#" class="listitem__caption">
                            <?php echo $hesklang['modules']; ?>
                        </a>
                        <ul class="submenu__list">
                            <?php if (hesk_checkPermission('can_run_reports',0)) {
                                ?>
                                <li class="submenu__listitem <?php if ($calling_script === 'module_statistics') { ?>current<?php } ?>">
                                    <a href="module_statistics.php">
                                        <?php echo $hesklang['statistics']['tab']; ?>
                                    </a>
                                </li>
                                <?php
                            }

                            if (hesk_checkPermission('can_man_settings',0)) {
                                ?>
                                <li class="submenu__listitem <?php if ($calling_script === 'module_escalate') { ?>current<?php } ?>">
                                    <a href="module_escalate.php">
                                        <?php echo $hesklang['escalate']['tab']; ?>
                                    </a>
                                </li>
                                <li class="submenu__listitem <?php if ($calling_script === 'module_satisfaction' || $calling_script === 'module_satisfaction_optout') { ?>current<?php } ?>">
                                    <a href="module_satisfaction.php">
                                        <?php echo $hesklang['satisfaction']['tab']; ?>
                                    </a>
                                </li>
                                <li class="submenu__listitem <?php if ($calling_script === 'module_recurring_tickets') { ?>current<?php } ?>">
                                    <a href="module_recurring_tickets.php">
                                        <?php echo $hesklang['recurring_tickets']['tab']; ?>
                                    </a>
                                </li>
                                <?php
                            }
                            ?>
                        </ul>
                    </div>
                </li>
                <?php
                }

                // Tools
                if (hesk_checkPermission('can_ban_emails',0) ||
                    hesk_checkPermission('can_mute_emails',0) ||
                    hesk_checkPermission('can_ban_ips',0) ||
                    hesk_checkPermission('can_service_msg',0) ||
                    hesk_checkPermission('can_email_tpl',0) ||
                    hesk_checkPermission('can_man_settings',0)) {
                    $pages = array('banned_emails', 'muted_emails', 'banned_ips', 'service_messages', 'email_templates', 'custom_fields', 'custom_statuses', 'oauth_providers', 'custom_priorities');
                    $open_menu = in_array($calling_script, $pages) ? 'current submenu-is-opened' : '';
                ?>
                <li class="separator"></li>
                <li class="listitem submenu <?php echo $open_menu; ?>">
                    <div class="listitem__icon">
                        <a href="#" aria-label="<?php echo $hesklang['tools']; ?>">
                            <svg class="icon icon-tools">
                                <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-tools"></use>
                            </svg>
                        </a>
                    </div>
                    <div class="listitem__menu">
                        <a href="#" class="listitem__caption">
                            <?php echo $hesklang['tools']; ?>
                        </a>
                        <ul class="submenu__list">
                            <?php if (hesk_checkPermission('can_ban_emails',0)) {
                                ?>
                                <li class="submenu__listitem <?php if ($calling_script === 'banned_emails') { ?>current<?php } ?>">
                                    <a href="banned_emails.php">
                                        <?php echo $hesklang['banemail']; ?>
                                    </a>
                                </li>
                                <?php
                            }

                            if (hesk_checkPermission('can_mute_emails',0)) {
                                ?>
                                <li class="submenu__listitem <?php if ($calling_script === 'muted_emails') { ?>current<?php } ?>">
                                    <a href="muted_emails.php">
                                        <?php echo $hesklang['mute_emails']; ?>
                                    </a>
                                </li>
                                <?php
                            }

                            if (hesk_checkPermission('can_ban_ips',0)) {
                                ?>
                                <li class="submenu__listitem <?php if ($calling_script === 'banned_ips') { ?>current<?php } ?>">
                                    <a href="banned_ips.php">
                                        <?php echo $hesklang['banip']; ?>
                                    </a>
                                </li>
                                <?php
                            }

                            if (hesk_checkPermission('can_service_msg',0)) {
                                ?>
                                <li class="submenu__listitem <?php if ($calling_script === 'service_messages') { ?>current<?php } ?>">
                                    <a href="service_messages.php">
                                        <?php echo $hesklang['sm_title']; ?>
                                    </a>
                                </li>
                                <?php
                            }

                            if (hesk_checkPermission('can_email_tpl',0)) {
                                ?>
                                <li class="submenu__listitem <?php if ($calling_script === 'email_templates') { ?>current<?php } ?>">
                                    <a href="email_templates.php">
                                        <?php echo $hesklang['et_title']; ?>
                                    </a>
                                </li>
                                <?php
                            }

                            if (hesk_checkPermission('can_man_settings',0)) {
                                ?>
                                <li class="submenu__listitem <?php if ($calling_script === 'custom_fields') { ?>current<?php } ?>">
                                    <a href="custom_fields.php">
                                        <?php echo $hesklang['tab_4']; ?>
                                    </a>
                                </li>
                                <li class="submenu__listitem <?php if ($calling_script === 'custom_statuses') { ?>current<?php } ?>">
                                    <a href="custom_statuses.php">
                                        <?php echo $hesklang['statuses']; ?>
                                    </a>
                                </li>
                                <li class="submenu__listitem <?php if ($calling_script === 'custom_priorities') { ?>current<?php } ?>">
                                    <a href="custom_priorities.php">
                                        <?php echo $hesklang['priorities']; ?>
                                    </a>
                                </li>
                                <li class="submenu__listitem <?php if ($calling_script === 'oauth_providers') { ?>current<?php } ?>">
                                    <a href="oauth_providers.php">
                                        <?php echo $hesklang['email_oauth_providers']; ?>
                                    </a>
                                </li>
                                <?php
                            }
                            ?>
                        </ul>
                    </div>
                </li>
                <?php
                }

                if (hesk_checkPermission('can_man_settings',0)) {
                    $pages = array('admin_settings_custom_html', 'admin_settings_general', 'admin_settings_help_desk',  'admin_settings_theme', 'admin_settings_knowledgebase',
                        'admin_settings_email', 'admin_settings_ticket_list', 'admin_settings_misc');
                    $open_menu = in_array($calling_script, $pages) ? 'current submenu-is-opened' : '';
                    ?>
                    <li class="listitem submenu <?php echo $open_menu; ?>">
                        <div class="listitem__icon">
                            <a href="#" aria-label="<?php echo $hesklang['settings']; ?>">
                                <svg class="icon icon-settings">
                                    <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-settings"></use>
                                </svg>
                            </a>
                        </div>
                        <div class="listitem__menu">
                            <a href="#" class="listitem__caption">
                                <?php echo $hesklang['settings']; ?>
                            </a>
                            <ul class="submenu__list">
                                <li class="submenu__listitem <?php if ($calling_script === 'admin_settings_general') { ?>current<?php } ?>">
                                    <a href="admin_settings_general.php">
                                        <?php echo $hesklang['tab_1']; ?>
                                    </a>
                                </li>
                                <li class="submenu__listitem <?php if ($calling_script === 'admin_settings_help_desk') { ?>current<?php } ?>">
                                    <a href="admin_settings_help_desk.php">
                                        <?php echo $hesklang['tab_2']; ?>
                                    </a>
                                </li>
                                <li class="submenu__listitem <?php if ($calling_script === 'admin_settings_theme' || $calling_script === 'admin_settings_custom_html') { ?>current<?php } ?>">
                                    <a href="admin_settings_theme.php">
                                        <?php echo $hesklang['tab_8']; ?>
                                    </a>
                                </li>
                                <li class="submenu__listitem <?php if ($calling_script === 'admin_settings_knowledgebase') { ?>current<?php } ?>">
                                    <a href="admin_settings_knowledgebase.php">
                                        <?php echo $hesklang['tab_3']; ?>
                                    </a>
                                </li>
                                <li class="submenu__listitem <?php if ($calling_script === 'admin_settings_email') { ?>current<?php } ?>">
                                    <a href="admin_settings_email.php">
                                        <?php echo $hesklang['tab_6']; ?>
                                    </a>
                                </li>
                                <li class="submenu__listitem <?php if ($calling_script === 'admin_settings_ticket_list') { ?>current<?php } ?>">
                                    <a href="admin_settings_ticket_list.php">
                                        <?php echo $hesklang['tab_7']; ?>
                                    </a>
                                </li>
                                <li class="submenu__listitem <?php if ($calling_script === 'admin_settings_misc') { ?>current<?php } ?>">
                                    <a href="admin_settings_misc.php">
                                        <?php echo $hesklang['tab_5']; ?>
                                    </a>
                                </li>
                            </ul>
                        </div>
                    </li>
                    <?php
                }
                ?>
                <li class="separator mobile"></li>
                <li class="listitem mobile <?php if ($calling_script === 'mail') { ?>current<?php } ?>">
                    <div class="listitem__icon">
                        <a href="mail.php" aria-label="<?php echo $hesklang['menu_msg']; ?>">
                            <svg class="icon icon-mail">
                                <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-mail"></use>
                            </svg>
                        </a>
                    </div>
                    <div class="listitem__menu">
                        <a href="mail.php" class="listitem__caption">
                            <?php echo $hesklang['menu_msg']; ?>
                        </a>
                        <?php if ($num_mail > 0): ?>
                        <span class="badge listitem__notification">
                            <?php echo ($num_mail > 99) ? '99+' : $num_mail; ?>
                        </span>
                        <?php endif; ?>
                    </div>
                </li>
                <li class="listitem mobile <?php if ($calling_script === 'profile') { ?>current<?php } ?>">
                    <div class="listitem__icon">
                        <a href="profile.php" class="mobile_ava" aria-label="<?php echo $hesklang['profile']; ?>">
                            <?php
                            $letter = hesk_mb_substr($_SESSION['name'], 0, 1);
                            echo hesk_mb_strtoupper($letter);
                            ?>
                        </a>
                    </div>
                    <div class="listitem__menu">
                        <a href="profile.php" class="listitem__caption">
                            <?php echo $hesklang['profile']; ?>
                        </a>
                    </div>
                </li>
            </ul>
        </div>
    </nav>
</aside>
<main class="main" id="maincontent">
    <!-- begin header -->
    <header class="header">
        <div class="header__left">
        </div>
        <div class="header__right" style="border-left: none">
            <a href="new_ticket.php" class="btn btn-full" ripple="ripple" aria-label="<?php echo $hesklang['create_new_ticket']; ?>">
                <?php echo $hesklang['create_new_ticket']; ?>
            </a>
            <div class="profile">
                <div class="profile__item profile__item--mail">
                    <a href="mail.php" class="btn btn-empty tooltip" title="<?php echo $hesklang['m_h']; ?>">
                        <div class="profile__item_rel">
                            <svg class="icon icon-mail">
                                <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-mail"></use>
                            </svg>
                            <?php if ($num_mail > 0): ?>
                            <div class="badge"><?php echo ($num_mail > 99) ? '99+' : $num_mail; ?></div>
                            <?php
                            endif;
                            unset($num_mail);
                            ?>
                        </div>
                    </a>
                </div>
                <div class="profile__item profile__user out-close">
                    <div class="user__ava" data-action="show-profile">
                        <?php
                        $letter = hesk_mb_substr($_SESSION['name'], 0, 1);
                        echo hesk_mb_strtoupper($letter);
                        ?>
                    </div>
                    <div class="user__name" data-action="show-profile">
                        <p>
                            <span><?php echo $_SESSION['name']; ?></span>
                            <svg class="icon icon-chevron-down">
                                <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-chevron-down"></use>
                            </svg>
                        </p>
                    </div>
                    <section class="profile__menu">
                        <div class="profile--view">
                            <a href="profile.php" class="btn btn-border" ripple="ripple"><?php echo $hesklang['view_profile']; ?></a>
                        </div>
                        <div class="profile--logout">
                            <a href="index.php?a=logout&token=<?php hesk_token_echo(); ?>">
                                <svg class="icon icon-log-out">
                                    <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-log-out"></use>
                                </svg>
                                <span><?php echo $hesklang['logout']; ?></span>
                            </a>
                        </div>
                    </section>
                </div>
            </div>
        </div>
        <div class="header__mobile">
            <button class="btn btn-empty header__menu" data-action="toggle-menu" aria-label="<?php echo $hesklang['toggle_navigation']; ?>">
                <svg class="icon icon-menu-mobile">
                    <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-menu-mobile"></use>
                </svg>
                <svg class="icon icon-close-mobile">
                    <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-close-mobile"></use>
                </svg>
            </button>
            <a class="navbar__logo" href="admin_main.php"><?php echo $hesklang['help_desk']; ?></a>
            <div class="header__mobile_actions">
                <a href="new_ticket.php" class="btn btn-empty" data-action="create-ticket" aria-label="<?php echo $hesklang['create_new_ticket']; ?>">
                    <svg class="icon icon-add">
                        <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-add"></use>
                    </svg>
                </a>
            </div>
        </div>
    </header>
<?php
// Show a notice if we are in maintenance mode
if ( hesk_check_maintenance(false) )
{
	echo '<br />';
	hesk_show_notice($hesklang['mma2'], $hesklang['mma1'], false);
}

// Show a notice if we are in "Knowledgebase only" mode
if ( hesk_check_kb_only(false) )
{
	echo '<br />';
	hesk_show_notice($hesklang['kbo2'], $hesklang['kbo1'], false);
}
?>
show_search_form.inc.php
wget 'https://lists2.roe3.org/hesk/inc/show_search_form.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

if ( ! isset($status) )
{
	$status = $hesk_settings['statuses'];
    unset($status[3]);
}

if ( ! isset($priority) )
{
    $priority = hesk_possible_priorities();
}

if ( ! isset($priority_order) )
{
    $priority_order = hesk_possible_priorities_order();
}

if ( ! isset($what) )
{
	$what = 'trackid';
}

if ( ! isset($owner_input) )
{
	$owner_input = 0;
}

if ( ! isset($date_input) )
{
	$date_input = '';
}

if (hesk_GET('duedate_option') === '') {
    $duedate_search_type = 'specific';
}

$duedate_input = hesk_GET('duedate_specific_date');
$duedate_amount_value = intval(hesk_GET('duedate_amount_value'));
$duedate_amount_unit = hesk_restricted_GET('duedate_amount_unit', ['day', 'week'], 'day');

/* Can view tickets that are unassigned or assigned to others? */
$can_view_ass_others = hesk_checkPermission('can_view_ass_others',0);
$can_view_unassigned = hesk_checkPermission('can_view_unassigned',0);
$can_view_ass_by     = hesk_checkPermission('can_view_ass_by', 0);

/* Category options */
$category_options = '';
if ( isset($hesk_settings['categories']) && count($hesk_settings['categories']) )
{
	foreach ($hesk_settings['categories'] as $row['id'] => $row['name'])
	{
		$row['name'] = (hesk_mb_strlen($row['name']) > 30) ? hesk_mb_substr($row['name'],0,30) . '...' : $row['name'];
		$selected = (in_array($row['id'], $categories)) ? 'selected="selected"' : '';
		$category_options .= '<option value="'.$row['id'].'" '.$selected.'>'.$row['name'].'</option>';
	}
}
else
{
	$res2 = hesk_dbQuery('SELECT `id`, `name` FROM `'.hesk_dbEscape($hesk_settings['db_pfix']).'categories` WHERE ' . hesk_myCategories('id') . ' ORDER BY `cat_order` ASC');
	while ($row=hesk_dbFetchAssoc($res2))
	{
		$row['name'] = (hesk_mb_strlen($row['name']) > 30) ? hesk_mb_substr($row['name'],0,30) . '...' : $row['name'];
		$selected = (in_array($row['id'], $categories)) ? 'selected="selected"' : '';
		$category_options .= '<option value="'.$row['id'].'" '.$selected.'>'.$row['name'].'</option>';
	}
}

/* List of staff */
if (($can_view_ass_others || $can_view_ass_by) && ! isset($admins))
{
	$admins = array();
	$res2 = hesk_dbQuery("SELECT `id`,`name` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."users` ORDER BY `name` ASC");
	while ($row=hesk_dbFetchAssoc($res2))
	{
		$admins[$row['id']]=$row['name'];
	}
}

$more = empty($_GET['more']) ? 0 : 1;
$more2 = empty($_GET['more2']) ? 0 : 1;

#echo "SQL: $sql";
?>

<!-- ** START SHOW TICKET FORM ** -->
<h2 style="font-size: 18px; font-weight: bold"><?php echo $hesklang['show_tickets']; ?></h2>
<div class="table-wrap">
    <form name="showt" action="show_tickets.php" method="get" class="show_tickets form">
        <div class="search-option">
            <div class="search-name">
                <?php echo $hesklang['status']; ?>
                <br><a href="" id="selectAllStatus" class="plain_link"><?php echo $hesklang['a_select']; ?></a>
                <br><a href="" id="deselectAllStatus" class="plain_link"><?php echo $hesklang['a_deselect']; ?></a>
                <br><a href="" id="toggleAllStatus" class="plain_link"><?php echo $hesklang['a_toggle']; ?></a>
            </div>
            <div class="search-options">
                <div class="checkbox-list">
                <?php
                hesk_get_status_checkboxes($status);
                ?>
                </div>
            </div>
        </div>
        <div id="topSubmit" style="display:<?php echo $more ? 'none' : 'block' ; ?>">
            <div style="display: flex">
                <button type="submit" class="btn btn-full"><?php echo $hesklang['show_tickets']; ?></button>
                <a class="btn btn--blue-border" href="javascript:void(0)" onclick="Javascript:hesk_toggleLayerDisplay('divShow');Javascript:hesk_toggleLayerDisplay('topSubmit');document.showt.more.value='1';"><?php echo $hesklang['mopt']; ?></a>
            </div>
        </div>
        <div id="divShow" style="display:<?php echo $more ? 'block' : 'none' ; ?>">
            <div class="search-option">
                <div class="search-name">
                    <?php echo $hesklang['priority']; ?>
                    <br><a href="" id="selectAllPriority" class="plain_link"><?php echo $hesklang['a_select']; ?></a>
                    <br><a href="" id="deselectAllPriority" class="plain_link"><?php echo $hesklang['a_deselect']; ?></a>
                    <br><a href="" id="toggleAllPriority" class="plain_link"><?php echo $hesklang['a_toggle']; ?></a>
                </div>
                <div class="search-options">
                    <div class="checkbox-list">
                        <?php 
                            hesk_get_priority_checkboxes($priority);
                        ?>
                    </div>
                </div>
            </div>
            <div class="search-option">
                <div class="search-name">
                    <?php echo $hesklang['show']; ?>
                    <br><a href="" id="selectAllShow" class="plain_link"><?php echo $hesklang['a_select']; ?></a>
                    <br><a href="" id="deselectAllShow" class="plain_link"><?php echo $hesklang['a_deselect']; ?></a>
                    <br><a href="" id="toggleAllShow" class="plain_link"><?php echo $hesklang['a_toggle']; ?></a>
                </div>
                <div class="search-options">
                    <div class="checkbox-list">
                        <div class="checkbox-custom">
                            <input type="checkbox" id="show_my" name="s_my" value="1" <?php if ($s_my[1]) echo 'checked'; ?>>
                            <label for="show_my"><?php echo $hesklang['s_my']; ?></label>
                        </div>
                        <?php
                        if ($can_view_unassigned)
                        {
                            ?>
                            <div class="checkbox-custom">
                                <input type="checkbox" id="show_un" name="s_un" value="1" <?php if ($s_un[1]) echo 'checked'; ?>>
                                <label for="show_un"><?php echo $hesklang['s_un']; ?></label>
                            </div>
                            <?php
                        }
                        ?>
                        <?php
                        if ($can_view_ass_others || $can_view_ass_by)
                        {
                            ?>
                            <div class="checkbox-custom">
                                <input type="checkbox" id="show_ot" name="s_ot" value="1" <?php if ($s_ot[1]) echo 'checked'; ?>>
                                <label for="show_ot"><?php echo $hesklang['s_ot']; ?></label>
                            </div>
                            <?php
                        }
                        ?>
                        <div class="checkbox-custom">
                            <input type="checkbox" id="show_archive" name="archive" value="1" <?php if ($archive[1]) echo 'checked'; ?>>
                            <label for="show_archive"><?php echo $hesklang['disp_only_archived']; ?></label>
                        </div>
                    </div>
                </div>
            </div>
            <div class="search-option">
                <div class="search-name">
                    <?php echo $hesklang['sort_by']; ?>
                </div>
                <div class="search-options">
                    <div class="radio-list">
                        <?php
                        array_unshift($hesk_settings['ticket_list'], 'priority');
                        $hesk_settings['possible_ticket_list']['priority'] = $hesklang['priority'];

                        foreach ($hesk_settings['ticket_list'] as $key):
                            if (!key_exists($key, $hesk_settings['possible_ticket_list'])) {
                                continue;
                            }
                            ?>
                            <div class="radio-custom">
                                <input type="radio" id="sort<?php echo $key; ?>" name="sort" value="<?php echo $key; ?>"
                                    <?php if ($sort == $key) { echo 'checked'; } ?>>
                                <label for="sort<?php echo $key; ?>">
                                    <?php echo $hesk_settings['possible_ticket_list'][$key]; ?>
                                </label>
                            </div>
                        <?php endforeach; ?>
                    </div>
                </div>
            </div>
            <div class="search-option">
                <div class="search-name">
                    <?php echo $hesklang['gb']; ?>
                </div>
                <div class="search-options">
                    <div class="radio-list">
                        <div class="radio-custom">
                            <input type="radio" name="g" value="" id="g_" <?php if (!$group) {echo 'checked';} ?>>
                            <label for="g_">
                                <?php echo $hesklang['dg']; ?>
                            </label>
                        </div>
                        <?php
                        if ($can_view_unassigned || $can_view_ass_others || $can_view_ass_by)
                        {
                            ?>
                            <div class="radio-custom">
                                <input type="radio" name="g" value="owner" id="g_owner" <?php if ($group == 'owner') {echo 'checked';} ?>>
                                <label for="g_owner">
                                    <?php echo $hesklang['owner']; ?>
                                </label>
                            </div>
                            <?php
                        }
                        ?>
                        <div class="radio-custom">
                            <input type="radio" name="g" value="category" id="g_category" <?php if ($group == 'category') {echo 'checked';} ?>>
                            <label for="g_category">
                                <?php echo $hesklang['category']; ?>
                            </label>
                        </div>
                        <div class="radio-custom">
                            <input type="radio" name="g" value="priority" id="g_priority" <?php if ($group == 'priority') {echo 'checked';} ?>>
                            <label for="g_priority">
                                <?php echo $hesklang['priority']; ?>
                            </label>
                        </div>
                    </div>
                </div>
            </div>
            <div class="search-option">
                <div class="search-name">
                    <?php echo $hesklang['due_date']; ?>
                </div>
                <div class="search-options">
                    <div class="radio-list">
                        <div class="radio-custom" style="margin-top: 0px;">
                            <input type="radio" id="duedate_specific" name="duedate_option" value="specific" <?php if ($duedate_search_type === 'specific') echo 'checked'; ?>>
                            <label for="duedate_specific"><?php echo $hesklang['search_due_date_specific']; ?></label>
                            <section class="param calendar">
                                <div class="calendar--button">
                                    <button type="button" onclick="document.getElementById('duedate_specific').checked = true" aria-label="Search by due date">
                                        <svg class="icon icon-calendar">
                                            <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-calendar"></use>
                                        </svg>
                                    </button>
                                    <input name="duedate_specific_date" id="duedate_specific_date"
                                        <?php if ($duedate_input) {echo 'value="'.hesk_htmlspecialchars($duedate_input).'"';} ?>
                                           type="text" class="datepicker" aria-label="Search by due date">
                                </div>
                                <div class="calendar--value" <?php echo ($duedate_input ? 'style="display: block"' : ''); ?>>
                                    <span><?php echo hesk_htmlspecialchars($duedate_input); ?></span>
                                    <i class="close">
                                        <svg class="icon icon-close">
                                            <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-close"></use>
                                        </svg>
                                    </i>
                                </div>
                            </section>
                        </div>
                        <div class="radio-custom">
                            <input type="radio" id="duedate_range" name="duedate_option" value="range" <?php if ($duedate_search_type === 'range') echo 'checked'; ?>>
                            <label for="duedate_range"><?php echo $hesklang['search_due_date_range']; ?></label>
                            <input class="form-control" type="text" id="duedate_amount_value" name="duedate_amount_value" value="<?php echo $duedate_amount_value; ?>" style="width: 25%"
                                   onkeyup="document.getElementById('duedate_range').checked = true">
                            <label for="duedate_amount_value">&nbsp;</label>
                            <select name="duedate_amount_unit" onclick="document.getElementById('duedate_range').checked = true" onchange="document.getElementById('duedate_range').checked = true" style="margin-top:5px;margin-bottom:5px;">
                                <option value="day" <?php if ($duedate_amount_unit === 'day') echo 'selected'; ?>><?php echo $hesklang['d_day']; ?></option>
                                <option value="week" <?php if ($duedate_amount_unit === 'week') echo 'selected'; ?>><?php echo $hesklang['d_week']; ?></option>
                            </select>
                        </div>
                    </div>
                </div>
            </div>
            <script>
                hesk_loadNoResultsSelectizePlugin('<?php echo hesk_jsString($hesklang['no_results_found']); ?>');
            </script>
            <div class="search-option">
                <div class="search-name">
                    <?php echo $hesklang['category']; ?>
                </div>
                <div class="search-options">
                    <select name="c[]"
                            class="read-write"
                            multiple
                            auto-load-plugins="no_results,remove_button"
                            placeholder="<?php echo hesk_addslashes($hesklang['search_by_category']); ?>">
                        <?php echo $category_options; ?>
                    </select>
                </div>
            </div>
            <div class="search-option">
                <div class="search-name">
                    <?php echo $hesklang['display']; ?>
                </div>
                <div class="search-options">
                    <input class="form-control" type="text" name="limit" value="<?php echo $maxresults; ?>" style="width: 25%" aria-label="<?php echo $hesklang['tickets_page']; ?>">
                    <?php echo $hesklang['tickets_page']; ?>
                </div>
            </div>
            <div class="search-option">
                <div class="search-name">
                    <?php echo $hesklang['order']; ?>
                </div>
                <div class="search-options">
                    <div class="radio-list">
                        <div class="radio-custom">
                            <input type="radio" name="asc" value="1" id="g_asc1" <?php if ($asc) {echo 'checked';} ?>>
                            <label for="g_asc1">
                                <?php echo $hesklang['ascending']; ?>
                            </label>
                        </div>
                        <div class="radio-custom">
                            <input type="radio" name="asc" value="0" id="g_asc0" <?php if (!$asc) {echo 'checked';} ?>>
                            <label for="g_asc0">
                                <?php echo $hesklang['descending']; ?>
                            </label>
                        </div>
                    </div>
                </div>
            </div>
            <div class="search-option">
                <div class="search-name">
                    <?php echo $hesklang['opt']; ?>
                </div>
                <div class="search-options">
                    <div class="checkbox-list">
                        <div class="checkbox-custom">
                            <input type="checkbox" name="cot" id="g_cot" value="1" <?php if ($cot) {echo 'checked';} ?>>
                            <label for="g_cot"><?php echo $hesklang['cot']; ?></label>
                        </div>
                        <div class="checkbox-custom">
                            <input type="checkbox" name="def" value="1" id="g_def">
                            <label for="g_def"><?php echo $hesklang['def']; ?></label>&nbsp;
                            (<a href="admin_main.php?reset=1&amp;token=<?php echo hesk_token_echo(0); ?>"><?php echo $hesklang['redv']; ?></a>)
                        </div>
                    </div>
                </div>
            </div>
            <div id="bottomSubmit">
                <div style="display: flex">
                    <button type="submit" id="bottom-showtickets" class="btn btn-full"><?php echo $hesklang['show_tickets']; ?></button>
                    <a class="btn btn--blue-border" href="javascript:void(0)" onclick="hesk_toggleLayerDisplay('divShow');hesk_toggleLayerDisplay('topSubmit');document.showt.more.value='0';"><?php echo $hesklang['lopt']; ?></a>
                    <input type="hidden" name="more" value="<?php echo $more ? 1 : 0 ; ?>">
                </div>
            </div>
        </div>
    </form>
</div>

<script>
// STATUS
$("#selectAllStatus").click(function(event) {
    event.preventDefault();
    $("[id^=status_][type=checkbox]").prop('checked', true);
});

$("#deselectAllStatus").click(function(event) {
    event.preventDefault();
    $("[id^=status_][type=checkbox]").prop('checked', false);
});

$("#toggleAllStatus").click(function(event) {
    event.preventDefault();
    $("[id^=status_][type=checkbox]").each(function() {
        this.checked = !this.checked;
    });
});

// PRIORITY
$("#selectAllPriority").click(function(event) {
    event.preventDefault();
    $("[id^=priority_][type=checkbox]").prop('checked', true);
});

$("#deselectAllPriority").click(function(event) {
    event.preventDefault();
    $("[id^=priority_][type=checkbox]").prop('checked', false);
});

$("#toggleAllPriority").click(function(event) {
    event.preventDefault();
    $("[id^=priority_][type=checkbox]").each(function() {
        this.checked = !this.checked;
    });
});

// SHOW
$("#selectAllShow").click(function(event) {
    event.preventDefault();
    $("[id^=show_][type=checkbox]").prop('checked', true);
});

$("#deselectAllShow").click(function(event) {
    event.preventDefault();
    $("[id^=show_][type=checkbox]").prop('checked', false);
});

$("#toggleAllShow").click(function(event) {
    event.preventDefault();
    $("[id^=show_][type=checkbox]").each(function() {
        this.checked = !this.checked;
    });
});
</script>

<!-- ** END SHOW TICKET FORM ** -->

<!-- ** START SEARCH TICKETS FORM ** -->
<h2 style="margin-top: 20px; font-size: 18px; font-weight: bold"><?php echo $hesklang['find_ticket_by']; ?></h2>
<div class="table-wrap" style="margin-bottom: 30px">
    <form action="find_tickets.php" method="get" name="findby" id="findby" class="show_tickets form">
        <div class="search-option">
            <div class="search-name">
                <?php echo $hesklang['s_in']; ?>
            </div>
            <div class="search-options">
                <select name="what" aria-label="<?php echo $hesklang['s_in']; ?>">
                    <option value="trackid" <?php if ($what=='trackid') {echo 'selected="selected"';} ?> ><?php echo $hesklang['trackID']; ?></option>
                    <?php
                    if ($hesk_settings['sequential'])
                    {
                        ?>
                        <option value="seqid" <?php if ($what=='seqid') {echo 'selected="selected"';} ?> ><?php echo $hesklang['seqid']; ?></option>
                        <?php
                    }
                    ?>
                    <option value="name"    <?php if ($what=='name') {echo 'selected="selected"';} ?> ><?php echo $hesklang['name']; ?></option>
                    <option value="email"	<?php if ($what=='email') {echo 'selected="selected"';} ?> ><?php echo $hesklang['email']; ?></option>
                    <option value="subject" <?php if ($what=='subject') {echo 'selected="selected"';} ?> ><?php echo $hesklang['subject']; ?></option>
                    <option value="message" <?php if ($what=='message') {echo 'selected="selected"';} ?> ><?php echo $hesklang['message']; ?></option>
                    <?php
                    foreach ($hesk_settings['custom_fields'] as $k=>$v)
                    {
                        $selected = ($what == $k) ? 'selected="selected"' : '';
                        if ($v['use'])
                        {
                            $v['name'] = (hesk_mb_strlen($v['name']) > 30) ? hesk_mb_substr($v['name'],0,30) . '...' : $v['name'];
                            echo '<option value="'.$k.'" '.$selected.'>'.$v['name'].'</option>';
                        }
                    }
                    ?>
                    <option value="customer" <?php if ($what=='customer') {echo 'selected="selected"';} ?> ><?php echo $hesklang['customer_id']; ?></option>
                    <option value="notes" <?php if ($what=='notes') {echo 'selected="selected"';} ?> ><?php echo $hesklang['notes']; ?></option>
                    <option value="ip" <?php if ($what=='ip') {echo 'selected="selected"';} ?> ><?php echo $hesklang['IP_addr']; ?></option>
                </select>
            </div>
        </div>    
        <div class="search-option">
            <div class="search-name">
                <?php echo $hesklang['s_for']; ?>
            </div>
            <div class="search-options">
                <input class="form-control" type="text" name="q" <?php if (isset($q)) {echo 'value="'.$q.'"';} ?> aria-label="<?php echo $hesklang['s_for']; ?>">
            </div>
        </div>
        <div id="topSubmit2" style="display:<?php echo $more2 ? 'none' : 'block' ; ?>">
            <div style="display: flex">
                <button type="submit" class="btn btn-full" id="findticket"><?php echo $hesklang['find_ticket']; ?></button>
                <a class="btn btn--blue-border" id="moreoptions2" href="javascript:void(0)" onclick="hesk_toggleLayerDisplay('divShow2');hesk_toggleLayerDisplay('topSubmit2');document.findby.more2.value='1';"><?php echo $hesklang['mopt']; ?></a>
            </div>
        </div>
        <div id="divShow2" style="display:<?php echo $more2 ? 'block' : 'none' ; ?>">
            <script>
                hesk_loadNoResultsSelectizePlugin('<?php echo hesk_jsString($hesklang['no_results_found']); ?>');
            </script>
            <div class="search-option">
                <div class="search-name">
                    <?php echo $hesklang['category']; ?>
                </div>
                <div class="search-options">
                    <select id="categoryfind" name="c[]"
                            class="read-write"
                            multiple
                            auto-load-plugins="no_results,remove_button"
                            placeholder="<?php echo hesk_addslashes($hesklang['search_by_category']); ?>">
                        <?php echo $category_options; ?>
                    </select>
                </div>
            </div>
            <?php
            if ($can_view_ass_others || $can_view_ass_by)
            {
                ?>
                <div class="search-option">
                    <div class="search-name">
                        <?php echo $hesklang['owner']; ?>
                    </div>
                    <div class="search-options">
                        <select id="ownerfind" name="owner">
                            <option value="0" ><?php echo $hesklang['anyown']; ?></option>
                            <?php
                            foreach ($admins as $staff_id => $staff_name)
                            {
                                echo '<option value="'.$staff_id.'" '.($owner_input == $staff_id ? 'selected="selected"' : '').'>'.$staff_name.'</option>';
                            }
                            ?>
                        </select>
                    </div>
                </div>
                <?php
            }
            ?>
            <div class="search-option">
                <div class="search-name">
                    <?php echo $hesklang['date']; ?>
                </div>
                <div class="search-options">
                    <section class="param calendar">
                        <div class="calendar--button">
                            <button type="button" aria-label="Find by due date">
                                <svg class="icon icon-calendar">
                                    <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-calendar"></use>
                                </svg>
                            </button>
                            <input name="dt" id="find-date"
                                <?php if ($date_input) {echo 'value="'.$date_input.'"';} ?>
                                   type="text" class="datepicker" aria-label="Find by due date">
                        </div>
                        <div class="calendar--value" <?php echo ($date_input ? 'style="display: block"' : ''); ?>>
                            <span><?php echo $date_input; ?></span>
                            <i class="close">
                                <svg class="icon icon-close">
                                    <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-close"></use>
                                </svg>
                            </i>
                    </div>
                    </section>
                </div>
            </div>
            <div class="search-option">
                <div class="search-name">
                    <?php echo $hesklang['s_incl']; ?>
                    <br><a href="" id="selectAllFind" class="plain_link"><?php echo $hesklang['a_select']; ?></a>
                    <br><a href="" id="deselectAllFind" class="plain_link"><?php echo $hesklang['a_deselect']; ?></a>
                    <br><a href="" id="toggleAllFind" class="plain_link"><?php echo $hesklang['a_toggle']; ?></a>
                </div>
                <div class="search-options">
                    <div class="checkbox-list">
                        <div class="checkbox-custom">
                            <input type="checkbox" id="find_show_my" name="s_my" value="1" <?php if ($s_my[2]) echo 'checked'; ?>>
                            <label for="find_show_my"><?php echo $hesklang['s_my']; ?></label>
                        </div>
                        <?php
                        if ($can_view_ass_others || $can_view_ass_by)
                        {
                            ?>
                            <div class="checkbox-custom">
                                <input type="checkbox" id="find_show_ot" name="s_ot" value="1" <?php if ($s_ot[2]) echo 'checked'; ?>>
                                <label for="find_show_ot"><?php echo $hesklang['s_ot']; ?></label>
                            </div>
                            <?php
                        }

                        if ($can_view_unassigned)
                        {
                            ?>
                            <div class="checkbox-custom">
                                <input type="checkbox" id="find_show_un" name="s_un" value="1" <?php if ($s_un[2]) echo 'checked'; ?>>
                                <label for="find_show_un"><?php echo $hesklang['s_un']; ?></label>
                            </div>
                            <?php
                        }
                        ?>
                        <div class="checkbox-custom">
                            <input type="checkbox" id="find_archive" name="archive" value="1" <?php if ($archive[2]) echo 'checked'; ?>>
                            <label for="find_archive"><?php echo $hesklang['disp_only_archived']; ?></label>
                        </div>
                    </div>
                </div>
            </div>
            <div class="search-option">
                <div class="search-name">
                    <?php echo $hesklang['display']; ?>
                </div>
                <div class="search-options">
                    <input type="text" name="limit" value="<?php echo $maxresults; ?>" class="form-control" style="width: 25%" aria-label="<?php echo $hesklang['results_page']; ?>">
                    <?php echo $hesklang['results_page']; ?>
                </div>
            </div>
            <div id="bottomSubmit">
                <div style="display: flex">
                    <button type="submit" id="findticket2" class="btn btn-full"><?php echo $hesklang['find_ticket']; ?></button>
                    <a class="btn btn--blue-border" href="javascript:void(0)" onclick="hesk_toggleLayerDisplay('divShow2');hesk_toggleLayerDisplay('topSubmit2');document.findby.more2.value='0';"><?php echo $hesklang['lopt']; ?></a>
                    <input type="hidden" name="more2" value="<?php echo $more2 ? 1 : 0 ; ?>">
                </div>
            </div>
        </div>
    </form>
</div>

<script>
$("#selectAllFind").click(function(event) {
    event.preventDefault();
    $("[id^=find_][type=checkbox]").prop('checked', true);
});

$("#deselectAllFind").click(function(event) {
    event.preventDefault();
    $("[id^=find_][type=checkbox]").prop('checked', false);
});

$("#toggleAllFind").click(function(event) {
    event.preventDefault();
    $("[id^=find_][type=checkbox]").each(function() {
        this.checked = !this.checked;
    });
});
</script>

<!-- ** END SEARCH TICKETS FORM ** -->
statuses.inc.php
wget 'https://lists2.roe3.org/hesk/inc/statuses.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

// Load available statuses
hesk_load_statuses();

/*** FUNCTIONS ***/


function hesk_load_statuses($use_cache=1)
{
	global $hesk_settings, $hesklang;

	// Do we have a cached version available
	$cache_dir = dirname(dirname(__FILE__)).'/'.$hesk_settings['cache_dir'].'/';
    $cache_file = $cache_dir . 'status_' . sha1($hesk_settings['language']).'.cache.php';

	if ($use_cache && file_exists($cache_file))
	{
		require($cache_file);
		return true;
	}

	// Define statuses array
	$hesk_settings['statuses'] = array();

    // HESK default statuses:
    //
    // 0 = NEW
    $hesk_settings['statuses'][0] = array(
        'name'  => $hesklang['open'],
        'class' => 'open',
    );
    // 1 = CUSTOMER REPLIED
    $hesk_settings['statuses'][1] = array(
        'name'  => $hesklang['wait_reply'],
        'class' => 'waitingreply',
    );
    // 2 = STAFF REPLIED
    $hesk_settings['statuses'][2] = array(
        'name'  => $hesklang['replied'],
        'class' => 'replied',
    );
    // 3 = RESOLVED
    $hesk_settings['statuses'][3] = array(
        'name'  => $hesklang['closed'],
        'class' => 'resolved',
    );
    // 4 = IN PROGRESS
    $hesk_settings['statuses'][4] = array(
        'name'  => $hesklang['in_progress'],
        'class' => 'inprogress',
    );
    // 5 = ON HOLD
    $hesk_settings['statuses'][5] = array(
        'name'  => $hesklang['on_hold'],
        'class' => 'onhold',
    );

    // Make sure we have database connection
    hesk_load_database_functions();
    hesk_dbConnect();

	$res = hesk_dbQuery("SELECT `id`, `name`, `color`, `can_customers_change` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."custom_statuses` ORDER BY `id` ASC");
	while ($row = hesk_dbFetchAssoc($res))
	{
		// Let's set status name for current language (or the first one we find)
		$names = json_decode($row['name'], true);
		$row['name'] = (isset($names[$hesk_settings['language']])) ? $names[$hesk_settings['language']] : reset($names);

		// Add to statuses array
		$hesk_settings['statuses'][$row['id']] = array(
            'name'  => $row['name'],
            'color' => '#'.$row['color'],
            'can_customers_change' => $row['can_customers_change'],
        );
	}

    // Try to cache results
    if ($use_cache && (is_dir($cache_dir) || ( @mkdir($cache_dir, 0777) && is_writable($cache_dir) ) ) )
    {
        // Is there an index.htm file?
        if ( ! file_exists($cache_dir.'index.htm'))
        {
            @file_put_contents($cache_dir.'index.htm', '');
        }

        // Write data
        @file_put_contents($cache_file, '<?php if (!defined(\'IN_SCRIPT\')) {die();} $hesk_settings[\'statuses\']=' . var_export($hesk_settings['statuses'], true) . ';' );
    }

	return true;
} // END hesk_load_statuses()


function hesk_get_status_select($ignore_status = '', $can_resolve = true, $select_status = '')
{
    global $hesk_settings;

    $options = '';
    $last = '';

    foreach ($hesk_settings['statuses'] as $k => $v)
    {
        if ($k === $ignore_status)
        {
            continue;
        }
        elseif ($k == 3)
        {
            if ($can_resolve || $k == $select_status)
            {
                $last = '<option value="'.$k.'" '.($k == $select_status ? 'selected' : '').'>'.$v['name'].'</option>';
            }
        }
        else
        {
            $options .= '<option value="'.$k.'" '.($k == $select_status ? 'selected' : '').'>'.$v['name'].'</option>';
        }
    }

    return $options . $last;

} // END hesk_get_status_select()


function hesk_get_status_checkboxes($selected = array())
{
    global $hesk_settings;

    $i = 0;

    echo '<div class="checkbox-group list">';

    $has_row = false;
    foreach ($hesk_settings['statuses'] as $k => $v) {
        if ($i % 3 === 0) {
            echo '<div class="row">';
            $has_row = true;
        }

        echo '
        <div class="checkbox-custom">
            <input type="checkbox" id="status_'.$k.'" name="s'.$k.'" value="1" '.(isset($selected[$k]) ? 'checked' : '').'>
            <label for="status_'.$k.'">'.hesk_get_admin_ticket_status($k).'</label>
        </div>';

        if ($i % 3 === 2) {
            echo '</div>';
            $has_row = false;
        }

        $i++;
    }
    if ($has_row) echo '</div>';
    echo '</div>';
} // END hesk_get_status_select()


function hesk_get_status_name($status)
{
    global $hesk_settings, $hesklang;
    return isset($hesk_settings['statuses'][$status]['name']) ? $hesk_settings['statuses'][$status]['name'] : $hesklang['unknown'];
} // END hesk_get_status_name()


function hesk_get_admin_ticket_status($status, $append = '')
{
    return hesk_get_ticket_status($status, $append, 0);
} // END hesk_get_admin_ticket_status()


function hesk_get_ticket_status($status, $append = '', $check_change = 1)
{
    global $hesk_settings, $hesklang;

    // Is this a valid status?
    if ( ! isset($hesk_settings['statuses'][$status]['name']))
    {
        return $hesklang['unknown'];
    }

    // In the customer side check if this status can be changed
    if ($check_change && ! hesk_can_customer_change_status($status))
    {
        if (isset($hesk_settings['statuses'][$status]['color']))
        {
            return '<span style="color:'.$hesk_settings['statuses'][$status]['color'].'">'.$hesk_settings['statuses'][$status]['name'].'</span>';
        }

        return $hesk_settings['statuses'][$status]['name'];
    }

    // Is this a default status? Use style class to add color
    if (isset($hesk_settings['statuses'][$status]['class']))
    {
        return '<span class="'.$hesk_settings['statuses'][$status]['class'].'">'.$hesk_settings['statuses'][$status]['name'].'</span>' . $append;
    }

    // Does this status have a color code?
    if (isset($hesk_settings['statuses'][$status]['color']))
    {
        return '<span style="color:'.$hesk_settings['statuses'][$status]['color'].'">'.$hesk_settings['statuses'][$status]['name'].'</span>' . $append;
    }

    // Just return the name if nothing matches
    return $hesk_settings['statuses'][$status]['name'] . $append;

} // END hesk_get_ticket_status()


function hesk_get_ticket_status_from_DB($trackingID)
{
    global $hesk_settings, $hesklang;

    if (empty($trackingID)) {
        hesk_error($hesklang['no_trackID']);
    }

    $result = hesk_dbQuery("SELECT `status` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `trackid`='".hesk_dbEscape($trackingID)."' LIMIT 1");
    if (hesk_dbNumRows($result) != 1) {
        hesk_error($hesklang['ticket_not_found']);
    }

    return hesk_dbResult($result);
} // END hesk_get_ticket_status_from_DB()


function hesk_can_customer_change_status($status)
{
    global $hesk_settings;
    return ( ! isset($hesk_settings['statuses'][$status]['can_customers_change']) || $hesk_settings['statuses'][$status]['can_customers_change'] == '1') ? true : false;
} // END hesk_get_ticket_status()


function hesk_print_status_select_box_jquery()
{
    global $hesk_settings;
    ?>
    <script>
    $(document).ready(function() {
        <?php
        $data_options = array();
        foreach ($hesk_settings['statuses'] as $id => $data)
        {
            // Is this a default status? Use style class to add color
            if (isset($data['class']))
            {
                $data_options[$id] = array('class' => $data['class']);
                echo '$("#ticket-status-div > div.dropdown-select > ul.dropdown-list > li[data-option=\''.$id.'\']").addClass("'.$data['class'].'");'."\n";
                echo '
                    $("#ticket-status-div > div.dropdown-select > div.label > span").filter(function () {
                        return $(this).text() == "'.addslashes($data['name']).'";
                    }).addClass("'.$data['class'].'");'."\n";
                echo '$("#submit-as-div > ul.dropdown-list > li[data-option=\'submit_as-'.$id.'\']").addClass("'.$data['class'].'");'."\n";
                continue;
            }

            // Does this status have a color code?
            if (isset($data['color']))
            {
                $data_options[$id] = array('color' => $data['color']);
                echo '$("#ticket-status-div > div.dropdown-select > ul.dropdown-list > li[data-option=\''.$id.'\']").css("color", "'.$data['color'].'");'."\n";
                echo '
                    $("#ticket-status-div > div.dropdown-select > div.label > span").filter(function () {
                        return $(this).text() == "'.addslashes($data['name']).'";
                    }).css("color", "'.$data['color'].'");'."\n";
                echo '$("#submit-as-div > ul.dropdown-list > li[data-option=\'submit_as-'.$id.'\']").css("color", "'.$data['color'].'");'."\n";
            }
        }
        ?>
    });

    function hesk_update_status_color(this_id)
    {
        $("#ticket-status-div > div.dropdown-select > div.label > span").removeClass();
        $("#ticket-status-div > div.dropdown-select > div.label > span").removeAttr('style');
        <?php
        foreach($data_options as $id => $data) {
            echo 'if (this_id == '.$id.') {';
            if (isset($data['class'])) {
                echo '$("#ticket-status-div > div.dropdown-select > div.label > span").addClass("'.$data['class'].'");';
            } else {
                echo '$("#ticket-status-div > div.dropdown-select > div.label > span").css("color", "'.$data['color'].'");';
            }
            echo 'return;}';
        }
        ?>
    }
    </script>
    <?php
} // END hesk_print_status_select_box_jquery()
theme_variables.inc.php
wget 'https://lists2.roe3.org/hesk/inc/theme_variables.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

/*
 * variable - name of the CSS variable
 * derivative - whether this color is a derivative of another color (i.e. via the use of var() or color-mix())
 */
$theme_color_settings = array(
    array('variable' => '--yellow-1', 'derivative' => false),
    array('variable' => '--yellow-2', 'derivative' => false),
    array('variable' => '--green-1', 'derivative' => false),
    array('variable' => '--red-1', 'derivative' => false),
    array('variable' => '--red-2', 'derivative' => false),

    array('variable' => '--success', 'derivative' => true),
    array('variable' => '--success-2', 'derivative' => false),
    array('variable' => '--error', 'derivative' => true),
    array('variable' => '--error-2', 'derivative' => false),
    array('variable' => '--error-3', 'derivative' => false),
    array('variable' => '--warning', 'derivative' => true),
    array('variable' => '--warning-2', 'derivative' => false),
    array('variable' => '--info', 'derivative' => false),
    array('variable' => '--info-2', 'derivative' => false),
    array('variable' => '--info-3', 'derivative' => false), // TODO sort of derivative, but from one of default color vars
    array('variable' => '--notification__clr', 'derivative' => true),

    array('variable' => '--primary', 'derivative' => false),
    array('variable' => '--secondary', 'derivative' => false),
    array('variable' => '--tertiary', 'derivative' => false),
    array('variable' => '--surface', 'derivative' => false),

    array('variable' => '--main-background', 'derivative' => false),
    array('variable' => '--font__pri-clr', 'derivative' => false),
    array('variable' => '--font__sec-clr', 'derivative' => false),

    array('variable' => '--header__bg', 'derivative' => true),
    array('variable' => '--header_logo__clr', 'derivative' => true),
    array('variable' => '--header_nav__clr', 'derivative' => true),
    array('variable' => '--header_nav__hover_clr', 'derivative' => true),
    array('variable' => '--input-bg', 'derivative' => false),
    array('variable' => '--input-clr', 'derivative' => false),
    array('variable' => '--link__pri-clr', 'derivative' => true),
    array('variable' => '--link__pri-hover-clr', 'derivative' => true),
    array('variable' => '--link__sec-clr', 'derivative' => true),
    array('variable' => '--link__sec-hover-clr', 'derivative' => true),
    array('variable' => '--footer__link-clr', 'derivative' => true),
    array('variable' => '--radio__bg', 'derivative' => true),
    array('variable' => '--radio__fill-clr', 'derivative' => true),
    array('variable' => '--radio__hover-bg', 'derivative' => true),
    array('variable' => '--radio__hover-fill-clr', 'derivative' => true),
    array('variable' => '--checkbox__bg', 'derivative' => true),
    array('variable' => '--checkbox__hover-bg', 'derivative' => true),

    // Navigation section
    array('variable' => '--breadcrumbs__a-clr', 'derivative' => true),
    array('variable' => '--breadcrumbs__a-hover-clr', 'derivative' => true),
    array('variable' => '--header_profile__clr', 'derivative' => true),
    array('variable' => '--header_profile__menu-bg', 'derivative' => true),
    array('variable' => '--header_profile__user-avatar-bg', 'derivative' => true),
    array('variable' => '--header_profile__mobile-user-avatar-bg', 'derivative' => true),
    array('variable' => '--navlink__bg', 'derivative' => true),
    array('variable' => '--navlink__clr', 'derivative' => true),
    array('variable' => '--navlink__hover-bg', 'derivative' => true),
    array('variable' => '--navlink__title-clr', 'derivative' => true),
    array('variable' => '--step_bar__item-clr', 'derivative' => true),

    // Articles/Previews
    array('variable' => '--preview__bg', 'derivative' => true),
    array('variable' => '--preview__border-clr', 'derivative' => true),
    array('variable' => '--preview__hover-bg', 'derivative' => true),
    array('variable' => '--preview__hover-icon-fill', 'derivative' => true),
    array('variable' => '--preview__title-clr', 'derivative' => true),
    array('variable' => '--suggest_preview__bg', 'derivative' => true),
    array('variable' => '--suggest_preview__clr', 'derivative' => true),
    array('variable' => '--suggest_preview__border-clr', 'derivative' => true),
    array('variable' => '--suggest_preview__hover-bg', 'derivative' => true),
    array('variable' => '--suggest_preview__hover-icon-fill', 'derivative' => true),
    array('variable' => '--suggest_preview__title-clr', 'derivative' => true),
    array('variable' => '--article_preview__hover-clr', 'derivative' => true),
    array('variable' => '--article_preview_suggestion_clr', 'derivative' => true),

    // Misc
    array('variable' => '--ticket_body__bg', 'derivative' => true),
    array('variable' => '--ticket_response__bg', 'derivative' => true),
    array('variable' => '--table_row__bg', 'derivative' => true),
    array('variable' => '--table_row__bg-even', 'derivative' => false), // TODO potentially change back later
    array('variable' => '--table_row__bg-hover', 'derivative' => false), // TODO sort of derivative, but from one of default color vars

    array('variable' => '--search__clr', 'derivative' => true),
    array('variable' => '--search__bg', 'derivative' => true),
    array('variable' => '--search__title-clr', 'derivative' => true),
    array('variable' => '--search__input-placeholder-clr', 'derivative' => true),
    array('variable' => '--search__icon-fill', 'derivative' => true),
    array('variable' => '--search__input-bg', 'derivative' => true),
    array('variable' => '--modal_body__bg', 'derivative' => true),
    array('variable' => '--btn__bg-clr-pri', 'derivative' => true),
    array('variable' => '--btn__clr-pri', 'derivative' => true),
    array('variable' => '--btn__border-clr-pri', 'derivative' => true),
    array('variable' => '--btn__disabled-bg-clr', 'derivative' => false), // TODO sort of derivative, but from one of default color vars
    array('variable' => '--btn__disabled-clr', 'derivative' => false), // TODO sort of derivative, but from one of default color vars
    array('variable' => '--dropdown__bg', 'derivative' => true),
    array('variable' => '--dropdown__clr', 'derivative' => true),
    array('variable' => '--dropdown__border-clr', 'derivative' => true),
    array('variable' => '--dropdown_ver1__border-clr', 'derivative' => false), // TODO sort of derivative, but from one of default color vars
    array('variable' => '--dropdown_ver2__border-clr', 'derivative' => true),
    array('variable' => '--dropdown__hover-bg', 'derivative' => true),
    array('variable' => '--dropdown__hover-clr', 'derivative' => true),
    array('variable' => '--dropdown__hover-border-clr', 'derivative' => true),
    array('variable' => '--dropdown_ver2__hover-border-clr', 'derivative' => true),
    array('variable' => '--datepicker_btn__bg', 'derivative' => true),
    array('variable' => '--datepicker_btn__clr', 'derivative' => true),
    array('variable' => '--datepicker__bg', 'derivative' => true),
    array('variable' => '--datepicker__clr', 'derivative' => true),
);

// Helper var that helps order the various settings into manageable/clear groups
/* Rough example of current variable counts:
Let's count the variables in theme_color_settings_groups:
1. main_brand: 7
2. main_elements: 17
3. buttons: 5
4. navigation: 11
5. dropdowns: 9
6. datepickers: 4
7. base_colors: 5
8. notifications: 11
9. search: 6
10. article_previews: 13
11. misc: 6
*/
$theme_color_settings_groups = array(
    'main_brand' => array('--primary', '--secondary', '--tertiary', '--surface', '--main-background', '--font__pri-clr', '--font__sec-clr'),
    'main_elements' => array('--header__bg', '--header_logo__clr', '--header_nav__clr', '--header_nav__hover_clr', '--input-bg', '--input-clr', '--link__pri-clr', '--link__pri-hover-clr', '--link__sec-clr', '--link__sec-hover-clr', '--footer__link-clr', '--radio__bg', '--radio__fill-clr', '--radio__hover-bg', '--radio__hover-fill-clr', '--checkbox__bg', '--checkbox__hover-bg' ),
    'buttons' => array('--btn__bg-clr-pri', '--btn__clr-pri', '--btn__border-clr-pri', '--btn__disabled-bg-clr', '--btn__disabled-clr'),
    'navigation' => array('--breadcrumbs__a-clr', '--breadcrumbs__a-hover-clr', '--header_profile__clr', '--header_profile__menu-bg', '--header_profile__user-avatar-bg', '--header_profile__mobile-user-avatar-bg', '--navlink__bg', '--navlink__clr', '--navlink__hover-bg', '--navlink__title-clr', '--step_bar__item-clr'),
    'dropdowns' => array('--dropdown__bg', '--dropdown__clr', '--dropdown__border-clr', '--dropdown_ver1__border-clr', '--dropdown_ver2__border-clr', '--dropdown__hover-bg', '--dropdown__hover-clr', '--dropdown__hover-border-clr', '--dropdown_ver2__hover-border-clr'),
    'datepickers' => array('--datepicker_btn__bg', '--datepicker_btn__clr', '--datepicker__bg', '--datepicker__clr'),
    'base_colors' => array('--yellow-1', '--yellow-2', '--green-1', '--red-1', '--red-2'),
    'notifications' => array('--success', '--success-2', '--error', '--error-2', '--error-3', '--warning', '--warning-2', '--info', '--info-2', '--info-3', '--notification__clr'),
    'search' => array('--search__clr', '--search__bg', '--search__title-clr', '--search__input-placeholder-clr', '--search__icon-fill', '--search__input-bg'),
    'article_previews' => array('--preview__bg', '--preview__border-clr', '--preview__hover-bg', '--preview__hover-icon-fill', '--preview__title-clr', '--suggest_preview__bg', '--suggest_preview__clr', '--suggest_preview__border-clr', '--suggest_preview__hover-bg', '--suggest_preview__hover-icon-fill', '--suggest_preview__title-clr', '--article_preview__hover-clr', '--article_preview_suggestion_clr'),
    'misc' => array('--ticket_body__bg', '--ticket_response__bg', '--table_row__bg', '--table_row__bg-even', '--table_row__bg-hover', '--modal_body__bg'),
);

function get_theme_color_setting($css_variable_key) {
    global $theme_color_settings;
    $color_setting = array_filter($theme_color_settings, function($setting) use ($css_variable_key) {
        return $setting['variable'] === $css_variable_key;
    });
    $color_setting = reset($color_setting);
    return $color_setting;
}

function hesk_is_valid_color_hex($hex)
{
    $hex = strtolower($hex);
    return preg_match('/^\#[a-f0-9]{6}$/', $hex) ? true : false;
} // END hesk_is_valid_color_hex()
ticket_list.inc.php
wget 'https://lists2.roe3.org/hesk/inc/ticket_list.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

// List of staff and check their permissions
$staff_that_can_view_tickets = hesk_getUserIdsWithAccessToFeatureAndCategory('can_view_tickets');
$admins = array();
$can_assign_to = array();
$res2 = hesk_dbQuery("SELECT `id`,`name`,`isadmin`,`active` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."users` ORDER BY `name` ASC");
while ($row = hesk_dbFetchAssoc($res2))
{
    $admins[$row['id']] = $row['name'];

    if ( ! $row['active']) {
        continue;
    }

    if ($row['isadmin'] || in_array($row['id'], $staff_that_can_view_tickets))
    {
        $can_assign_to[$row['id']] = $row['name'];
    }
}

/* List of categories */
if ( ! isset($hesk_settings['categories']))
{
    $hesk_settings['categories'] = array();
    $res2 = hesk_dbQuery('SELECT `id`, `name` FROM `'.hesk_dbEscape($hesk_settings['db_pfix']).'categories` WHERE ' . hesk_myCategories('id') . ' ORDER BY `cat_order` ASC');
    while ($row=hesk_dbFetchAssoc($res2))
    {
        $hesk_settings['categories'][$row['id']] = $row['name'];
    }
}

/* Current MySQL time */
$mysql_time = hesk_dbTime();

/* Get number of tickets and page number */
$result = hesk_dbQuery($sql_count);

while ($row = hesk_dbFetchAssoc($result))
{
    // Total tickets found
    $totals['filtered']['all'] += $row['cnt'];

    // Total by status
    if (isset($totals['filtered']['by_status'][$row['status']]))
    {
        $totals['filtered']['by_status'][$row['status']] += $row['cnt'];
    }
    else
    {
        $totals['filtered']['by_status'][$row['status']] = $row['cnt'];
    }

    // Count all filtered open tickets
    if (isset($row['status']) && $row['status'] != 3)
    {
        $totals['filtered']['open'] += $row['cnt'];
    }

    // Totals by assigned to
    if (isset($row['assigned_to'])):
    switch ($row['assigned_to'])
    {
        case 1:
            $totals['filtered']['assigned_to_me'] += $row['cnt'];
            break;
        case 2:
            $totals['filtered']['assigned_to_others'] += $row['cnt'];
            break;
        case 3:
            $totals['filtered']['assigned_to_others'] += $row['cnt'];
            $totals['filtered']['assigned_to_others_by_me'] += $row['cnt'];
            break;
        default:
            $totals['filtered']['unassigned'] += $row['cnt'];
    }

    if ( ! empty($row['is_collaborator']))
    {
        switch ($row['assigned_to'])
        {
            case 1:
                $totals['filtered']['collaborator_assigned_to_me'] += $row['cnt'];
                break;
            case 2:
                $totals['filtered']['collaborator_assigned_to_others'] += $row['cnt'];
                break;
            case 3:
                $totals['filtered']['collaborator_assigned_to_others'] += $row['cnt'];
                $totals['filtered']['collaborator_assigned_to_others_by_me'] += $row['cnt'];
                break;
            default:
                $totals['filtered']['collaborator_unassigned'] += $row['cnt'];
        }
    }
    endif;

    // Total by due date; ignore for Resolved tickets
    if ($row['status'] != 3)
    {
        switch ($row['due'])
        {
            case 1:
                $totals['filtered']['due_soon'] += $row['cnt'];
                break;
            case 2:
                $totals['filtered']['overdue'] += $row['cnt'];
                break;
        }
    }

    // Total bookmarks
    if ( ! empty($row['is_bookmark']))
    {
        $totals['filtered']['bookmarks'] += $row['cnt'];
    }
}

$result = hesk_dbQuery($sql_collaborator);
while ($row = hesk_dbFetchAssoc($result))
{
    // Total collaborator tickets
    $totals['filtered']['collaborator'] += $row['cnt'];
}

// Quick link: assigned to me
if ($is_quick_link == 'my')
{
    $total = $totals['filtered']['assigned_to_me'];
}
// Quick link: collaborator
elseif ($is_quick_link == 'cbm')
{
    $total = $totals['filtered']['collaborator'];
}
// Quick link: assigned to other
elseif ($is_quick_link == 'ot')
{
    $total = $can_view_ass_others ? $totals['filtered']['assigned_to_others'] : $totals['filtered']['assigned_to_others_by_me'];
}
// Quick link: unassigned
elseif ($is_quick_link == 'un')
{
    $total = $totals['filtered']['unassigned'];
}
// Quick link: bookmarks
elseif ($is_quick_link == 'bm')
{
    $total = $totals['filtered']['bookmarks'];
}
// Quick link: due soon
elseif ($is_quick_link == 'due')
{
    $total = $totals['filtered']['due_soon'];
}
// Quick link: overdue
elseif ($is_quick_link == 'ovr')
{
    $total = $totals['filtered']['overdue'];
}
// Quick link: all open
elseif ($is_quick_link == 'alo')
{
    $total = ($href == 'find_tickets.php') ? $totals['filtered']['open'] : $totals['open'];
}
// Quick link: all
elseif ($is_quick_link == 'all')
{
    $total = $totals['all'];
}
// No quick link
else
{
    $is_quick_link = false;

    $list_fid = ($href == 'find_tickets.php') ? 2 : 1;

    $totals['filtered']['show'] = 0;
    $assigned_to_others_total = $can_view_ass_others ? $totals['filtered']['assigned_to_others'] : $totals['filtered']['assigned_to_others_by_me'];

    if ($s_my[$list_fid] == 1)
    {
        $totals['filtered']['show'] += $totals['filtered']['assigned_to_me'];
    }

    if ($s_ot[$list_fid] == 1)
    {
        $totals['filtered']['show'] += $assigned_to_others_total;
    }

    if ($s_un[$list_fid] == 1)
    {
        $totals['filtered']['show'] += $totals['filtered']['unassigned'];
    }

    $total = $totals['filtered']['show'];
}

// Count the main filtered assignment selection separately from quick links.
// Quick links such as "Assigned to me" override the active SQL view, but
// the main "Filtered tickets" tab should still return to the user's
// original assignment filter selection.
$list_fid = ($href == 'find_tickets.php') ? 2 : 1;
$totals['filtered']['main_show'] = 0;
$main_assigned_to_others_total = $can_view_ass_others ? $totals['filtered']['assigned_to_others'] : $totals['filtered']['assigned_to_others_by_me'];
$main_s_my = isset($s_my_filter[$list_fid]) ? $s_my_filter[$list_fid] : $s_my[$list_fid];
$main_s_ot = isset($s_ot_filter[$list_fid]) ? $s_ot_filter[$list_fid] : $s_ot[$list_fid];
$main_s_un = isset($s_un_filter[$list_fid]) ? $s_un_filter[$list_fid] : $s_un[$list_fid];

if ($main_s_my == 1)
{
    $totals['filtered']['main_show'] += $totals['filtered']['assigned_to_me'];
}

if ($main_s_ot == 1)
{
    $totals['filtered']['main_show'] += $main_assigned_to_others_total;
}

if ($main_s_un == 1)
{
    $totals['filtered']['main_show'] += $totals['filtered']['unassigned'];
}

$hesk_main_assignment_all_selected = $main_s_my == 1
    && ( ! ($can_view_ass_others || $can_view_ass_by) || $main_s_ot == 1)
    && ( ! $can_view_unassigned || $main_s_un == 1);

if (empty($is_quick_link))
{
    $totals['filtered']['show'] = $totals['filtered']['main_show'];
    $total = $totals['filtered']['show'];
}

if (true)
{

	/* This query string will be used to browse pages */
    if ($href == 'admin_main.php' || $href == 'show_tickets.php')
	{
		#$query  = 'status='.$status;

        $query = '';
        $query .= 's' . implode('=1&amp;s',array_keys($status)) . '=1';
        $query .= '&amp;p' . implode('=1&amp;p',array_keys($priority)) . '=1';

		$query .= (count($categories) ? '&amp;c[]=' . implode('&amp;c[]=', $categories) : '&amp;c[]=0');
		$query .= '&amp;sort='.$sort;
		$query .= '&amp;asc='.$asc;
		$query .= '&amp;limit='.$maxresults;
		$query .= '&amp;archive='.$archive[1];
		$query .= '&amp;s_my='.(isset($s_my_filter[1]) ? $s_my_filter[1] : $s_my[1]);
		$query .= '&amp;s_ot='.(isset($s_ot_filter[1]) ? $s_ot_filter[1] : $s_ot[1]);
		$query .= '&amp;s_un='.(isset($s_un_filter[1]) ? $s_un_filter[1] : $s_un[1]);

        $query .= '&amp;duedate_option='.$duedate_search_type;
        $query .= '&amp;duedate_specific_date='.urlencode($duedate_input);
        $query .= '&amp;duedate_amount_value='.$duedate_amount_value;
        $query .= '&amp;duedate_amount_unit='.$duedate_amount_unit;

		$query .= '&amp;cot='.$cot;
		$query .= '&amp;g='.$group;
	}
	else
	{
		$query  = 'q='.urlencode($q);
	    $query .= '&amp;what='.$what;
		$query .= (count($categories) ? '&amp;c[]=' . implode('&amp;c[]=', $categories) : '&amp;c[]=0');
        $query .= '&amp;owner='.$owner_input;
		$query .= '&amp;dt='.urlencode($date_input);
		$query .= '&amp;sort='.$sort;
		$query .= '&amp;asc='.$asc;
		$query .= '&amp;limit='.$maxresults;
		$query .= '&amp;archive='.$archive[2];
		$query .= '&amp;s_my='.(isset($s_my_filter[2]) ? $s_my_filter[2] : $s_my[2]);
		$query .= '&amp;s_ot='.(isset($s_ot_filter[2]) ? $s_ot_filter[2] : $s_ot[2]);
		$query .= '&amp;s_un='.(isset($s_un_filter[2]) ? $s_un_filter[2] : $s_un[2]);
	}

    $query_for_quick_links = $query;

    if ($is_quick_link !== false)
    {
        $query .= '&amp;ql=' . $is_quick_link;
    }

    $query_for_pagination = $query . '&amp;page=';

	$pages = ceil($total/$maxresults) or $pages = 1;
	if ($page > $pages)
	{
		$page = $pages;
	}
	$limit_down = ($page * $maxresults) - $maxresults;

	$prev_page = ($page - 1 <= 0) ? 0 : $page - 1;
	$next_page = ($page + 1 > $pages) ? 0 : $page + 1;

	/* We have the full SQL query now, get tickets */
	$result = false;
	if ($total > 0)
	{
		$sql .= " LIMIT ".hesk_dbEscape($limit_down)." , ".hesk_dbEscape($maxresults)." ";
		$result = hesk_dbQuery($sql);
	}

    /* Uncomment for debugging */
    # echo "SQL: $sql\n<br>";

	/* This query string will be used to order and reverse display */
    if ($href == 'admin_main.php' || $href == 'show_tickets.php')
	{
		#$query  = 'status='.$status;

        $query = '';
        $query .= 's' . implode('=1&amp;s',array_keys($status)) . '=1';
        $query .= '&amp;p' . implode('=1&amp;p',array_keys($priority)) . '=1';

		$query .= (count($categories) ? '&amp;c[]=' . implode('&amp;c[]=', $categories) : '&amp;c[]=0');
		#$query .= '&amp;asc='.(isset($is_default) ? 1 : $asc_rev);
		$query .= '&amp;limit='.$maxresults;
		$query .= '&amp;archive='.$archive[1];
		$query .= '&amp;s_my='.(isset($s_my_filter[1]) ? $s_my_filter[1] : $s_my[1]);
		$query .= '&amp;s_ot='.(isset($s_ot_filter[1]) ? $s_ot_filter[1] : $s_ot[1]);
		$query .= '&amp;s_un='.(isset($s_un_filter[1]) ? $s_un_filter[1] : $s_un[1]);
		$query .= '&amp;page=1';
		#$query .= '&amp;sort=';

        $query .= '&amp;duedate_option='.$duedate_search_type;
        $query .= '&amp;duedate_specific_date='.urlencode($duedate_input);
        $query .= '&amp;duedate_amount_value='.$duedate_amount_value;
        $query .= '&amp;duedate_amount_unit='.$duedate_amount_unit;

		$query .= '&amp;cot='.$cot;
		$query .= '&amp;g='.$group;

	}
	else
	{
		$query  = 'q='.urlencode($q);
	    $query .= '&amp;what='.$what;
		$query .= (count($categories) ? '&amp;c[]=' . implode('&amp;c[]=', $categories) : '&amp;c[]=0');
        $query .= '&amp;owner='.$owner_input;
		$query .= '&amp;dt='.urlencode($date_input);
		#$query .= '&amp;asc='.$asc;
		$query .= '&amp;limit='.$maxresults;
		$query .= '&amp;archive='.$archive[2];
		$query .= '&amp;s_my='.(isset($s_my_filter[2]) ? $s_my_filter[2] : $s_my[2]);
		$query .= '&amp;s_ot='.(isset($s_ot_filter[2]) ? $s_ot_filter[2] : $s_ot[2]);
		$query .= '&amp;s_un='.(isset($s_un_filter[2]) ? $s_un_filter[2] : $s_un[2]);
		$query .= '&amp;page=1';
		#$query .= '&amp;sort=';
	}

    if ($is_quick_link !== false)
    {
        $query .= '&amp;ql=' . $is_quick_link;
    }

    $query .= '&amp;asc=';

	/* Print the table with tickets */
	$random=rand(10000,99999);

	$modal_id = hesk_generate_old_delete_modal($hesklang['confirm'],
        $hesklang['confirm_execute'],
    "javascript:document.getElementById('delete-tickets-form').submit()",
        $hesklang['confirm']);

    // Are some open tickets hidden?
    if ($href != 'find_tickets.php' && $hesk_main_assignment_all_selected && $totals['filtered']['open'] != $totals['open'])
    {
        hesk_show_info($hesklang['not_aos'], ' ', false, 'no-padding-top');
    }
	?>
    <section class="quick-links">
        <div class="filters__listing">
            <!--
            <a href="<?php echo $href . '?' . $query_for_quick_links . '&amp;ql=all&amp;s_my=1&amp;s_ot=1&amp;s_un=1&amp;category=0'; ?>" class="btn btn-transparent <?php if ($is_quick_link == 'all') echo 'is-bold is-selected'; ?>"><span><?php echo $hesklang['ql_all']; ?></span> <span class="filters__btn-value"><?php echo $totals['all']; ?></span></a>
            <a href="<?php echo $href . '?' . $query_for_quick_links . '&amp;ql=alo&amp;s_my=1&amp;s_ot=1&amp;s_un=1&amp;category=0'; ?>" class="btn btn-transparent <?php if ($is_quick_link == 'alo') echo 'is-bold is-selected'; ?>"><span><?php echo $hesklang['ql_alo']; ?></span> <span class="filters__btn-value"><?php echo $totals['open']; ?></span></a>
             -->
            <a href="<?php echo $href . '?' . $query_for_quick_links . '&amp;ql='; ?>" class="btn btn-transparent <?php if (empty($is_quick_link)) echo 'is-bold is-selected'; ?>"><span><?php
            if ($href == 'find_tickets.php') {
                echo $hesklang['tickets_found'];
            }
            elseif (
                $totals['filtered']['open'] == $totals['open'] &&
                $totals['filtered']['open'] == $totals['filtered']['all'] &&
                ( ! isset($totals['filtered']['main_show']) || $totals['filtered']['main_show'] == $totals['open'])) {
                echo $hesklang['open_tickets'];
            }
            else {
                echo $hesklang['ql_fit'];
            }
            ?></span> <span class="filters__btn-value"><?php echo (isset($totals['filtered']['main_show']) ? $totals['filtered']['main_show'] : $totals['filtered']['all']); ?></span></a>
            <a href="<?php echo $href . '?' . $query_for_quick_links . '&amp;ql=my'; ?>" class="btn btn-transparent <?php if ($is_quick_link == 'my') echo 'is-bold is-selected'; ?>"><span><?php echo $hesklang['ql_a2m']; ?></span> <span class="filters__btn-value"><?php echo $totals['filtered']['assigned_to_me']; ?></span></a>
            <a href="<?php echo $href . '?' . $query_for_quick_links . '&amp;ql=cbm'; ?>" class="btn btn-transparent <?php if ($is_quick_link == 'cbm') echo 'is-bold is-selected'; ?>"><span><?php echo $hesklang['ql_cbm']; ?></span> <span class="filters__btn-value"><?php echo $totals['filtered']['collaborator']; ?></span></a>
            <?php if ($can_view_ass_others || $can_view_ass_by): ?>
            <a href="<?php echo $href . '?' . $query_for_quick_links . '&amp;ql=ot'; ?>" class="btn btn-transparent <?php if ($is_quick_link == 'ot') echo 'is-bold is-selected'; ?>"><span><?php echo $hesklang['ql_a2o']; ?></span> <span class="filters__btn-value"><?php echo $can_view_ass_others ? $totals['filtered']['assigned_to_others'] : $totals['filtered']['assigned_to_others_by_me']; ?></span></a>
            <?php endif; ?>
            <?php if ($can_view_unassigned): ?>
            <a href="<?php echo $href . '?' . $query_for_quick_links . '&amp;ql=un'; ?>" class="btn btn-transparent <?php if ($is_quick_link == 'un') echo 'is-bold is-selected'; ?>"><span><?php echo $hesklang['ql_una']; ?></span> <span class="filters__btn-value"><?php echo $totals['filtered']['unassigned']; ?></span></a>
            <?php endif; ?>
            <a href="<?php echo $href . '?' . $query_for_quick_links . '&amp;ql=bm&amp;s_my=1&amp;s_ot=1&amp;s_un=1'; ?>" class="btn btn-transparent is-bookmarks <?php if ($is_quick_link == 'bm') echo 'is-bold is-selected'; ?>"><span><?php echo $hesklang['ql_bookmarks']; ?></span> <span class="filters__btn-value"><?php echo $totals['filtered']['bookmarks']; ?></span></a>
            <a href="<?php echo $href . '?' . $query_for_quick_links . '&amp;ql=due&amp;s_my=1&amp;s_ot=1&amp;s_un=1'; ?>" class="btn btn-transparent is-due-soon <?php if ($is_quick_link == 'due') echo 'is-bold is-selected'; ?>"><span><?php echo $hesklang['ql_due']; ?></span> <span class="filters__btn-value"><?php echo $totals['filtered']['due_soon']; ?></span></a>
            <a href="<?php echo $href . '?' . $query_for_quick_links . '&amp;ql=ovr&amp;s_my=1&amp;s_ot=1&amp;s_un=1'; ?>" class="btn btn-transparent is-overdue <?php if ($is_quick_link == 'ovr') echo 'is-bold is-selected'; ?>"><span><?php echo $hesklang['ql_ovr']; ?></span> <span class="filters__btn-value"><?php echo $totals['filtered']['overdue']; ?></span></a>
        </div>

        <div class="checkbox-custom auto-reload">
            <input type="checkbox" id="reloadCB" onclick="toggleAutoRefresh(this);">
            <label for="reloadCB"><?php echo $hesklang['arp']; ?></label>&nbsp;<span id="timer"></span>
            <script type="text/javascript">heskCheckReloading();</script>
        </div>
    </section>
    <?php
    $hesk_settings['hesk-group-count'] = 0;

    if ($total > 0)
    {
        ?>
        <form name="form1" id="delete-tickets-form" action="delete_tickets.php" method="post">
        <?php
        if (empty($group))
        {
            hesk_print_list_head();
        }
    }
    else
    {
        hesk_show_info($hesklang['no_tickets_crit'], ' ', false);
    }

	$checkall = '
    <div class="checkbox-custom">
        <input type="checkbox" id="ticket_checkall" onclick="hesk_changeAll()">
        <label for="ticket_checkall">&nbsp;</label>
    </div>';

    $group_tmp = '';
	$is_table = 0;
	$space = 0;

	if ($total > 0 && $result)
	{
	while ($ticket=hesk_dbFetchAssoc($result))
	{
		// Are we grouping tickets?
		if ($group)
        {
			require(HESK_PATH . 'inc/print_group.inc.php');
        }

		// Set owner (needed for row title)
		$owner = '';
        $first_line = '(' . $hesklang['unas'] . ')'." \n\n";
		if ($ticket['owner'] == $_SESSION['id'])
		{
			$owner = '<svg class="icon icon-assign" style="margin-right: 3px">
                    <use xlink:href="'. HESK_PATH . 'img/sprite.svg#icon-assign"></use>
                </svg>';
            $first_line = $hesklang['tasy2'] . " \n\n";
		}
		elseif ($ticket['owner'])
		{
        	if (!isset($admins[$ticket['owner']]))
            {
            	$admins[$ticket['owner']] = $hesklang['e_udel'];
            }
			$owner = '<svg class="icon icon-assign-plus" style="margin-right: 3px">
                    <use xlink:href="'. HESK_PATH . 'img/sprite.svg#icon-assign-plus"></use>
                </svg>';
            $first_line = $hesklang['taso3'] . ' ' . $admins[$ticket['owner']] . " \n\n";
		}

		// Set message (needed for row title)
		$ticket['message'] = $first_line . hesk_mb_substr(strip_tags($ticket['message']),0,200).'...';

		// Start ticket row
		echo '
		<tr title="'.$ticket['message'].'" class="status-'. $ticket['status'] .' '.($ticket['owner'] ? '' : 'new').($ticket['priority'] == '0' ? ' bg-critical' : '').'">
		<td class="table__first_th sindu_handle">
            <div class="checkbox-custom">
                <input type="checkbox" id="ticket_check_'.$ticket['id'].'" name="id[]" value="'.$ticket['id'].'" class="group' . $hesk_settings['hesk-group-count'] . '">
                <label for="ticket_check_'.$ticket['id'].'" aria-label="' . $hesklang['select'] . '">&nbsp;</label>
            </div>
        </td>
		';

		// Print sequential ID and link it to the ticket page
		if ( hesk_show_column('id') )
		{
			echo '<td><a href="admin_ticket.php?track='.$ticket['trackid'].'&amp;Refresh='.$random.'">'.$ticket['id'].'</a></td>';
		}

		// Print tracking ID and link it to the ticket page
		if ( hesk_show_column('trackid') )
		{
			echo '<td class="trackid">
                <div class="table__td-id">
                    <a class="link" href="admin_ticket.php?track='.$ticket['trackid'].'&amp;Refresh='.$random.'">'.$ticket['trackid'].'</a>
                </div>
            </td>';
		}

		// Print date submitted
		if ( hesk_show_column('dt') )
		{
			switch ($hesk_settings['submittedformat'])
			{
	        	case 1:
					$ticket['dt'] = hesk_date($ticket['dt'], true, true, true, $hesk_settings['format_timestamp']);
					break;
				case 2:
					$ticket['dt'] = hesk_time_lastchange($ticket['dt']);
					break;
                case 3:
                    $ticket['dt'] = hesk_date($ticket['dt'], true, true, true, $hesk_settings['format_date']);
                    break;
                case 4:
                    $ticket['dt'] = hesk_date($ticket['dt'], true, true, true, $hesk_settings['format_submitted']);
                    break;
				default:
					$ticket['dt'] = hesk_time_since( strtotime($ticket['dt']) );
			}
			echo '<td>'.$ticket['dt'].'</td>';
		}

		// Print last modified
		if ( hesk_show_column('lastchange') )
		{
            // Another usage over in my_tickets.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:
					$ticket['lastchange'] = hesk_time_since( strtotime($ticket['lastchange']) );
			}
			echo '<td>'.$ticket['lastchange'].'</td>';
		}

		// Print ticket category
		if ( hesk_show_column('category') )
		{
			$ticket['category_name'] = isset($hesk_settings['categories'][$ticket['category']]) ? $hesk_settings['categories'][$ticket['category']] : $hesklang['catd'];
			echo '<td class="category-'.intval($ticket['category']).'">'.$ticket['category_name'].'</td>';
		}

		// Print customer name
		if ( hesk_show_column('name') )
		{
            if ($ticket['name'] == '') {
                if (strlen($ticket['email']) && $ticket['email'] != $hesklang['anon_email']) {
                    $ticket['name'] = $ticket['email'];
                } else {
                    $ticket['name'] = $hesklang['anon_name'];
                }
            }

            echo '<td>'.$ticket['name'];

            if (intval($ticket['customer_count']) > 1) {
                echo '<span class="customer-count">'.sprintf($hesklang['customer_count_x_more'], intval($ticket['customer_count']) - 1).'</span>';
            }

			echo '</td>';
		}

		// Print customer email
		if ( hesk_show_column('email') )
		{
			echo '<td>' . (strlen($ticket['email']) ? '<a href="mailto:'.$ticket['email'].'">'.($hesk_settings['email_column'] ? $ticket['email'] : $hesklang['clickemail']).'</a>' : '');

            if (intval($ticket['email_count']) > 1) {
                $subtraction_amount = strlen($ticket['email']) ? 1 : 0;
                echo '<span class="customer-count">'.sprintf($hesklang['customer_count_x_more'], intval($ticket['email_count']) - $subtraction_amount).'</span>';
            }

            echo '</td>';
		}

		// Print subject and link to the ticket page
		if ( hesk_show_column('subject') )
		{
			echo '<td class="subject">'.($ticket['archive'] ? '<svg class="icon icon-tag '.($ticket['owner'] != $_SESSION['id'] ? 'fill-gray' : '').'" style="margin-right: 3px">
                        <use xlink:href="'. HESK_PATH .'img/sprite.svg#icon-tag"></use>
                    </svg>' : '').($ticket['is_bookmark'] ? '<svg class="icon icon-pin is-bookmark" style="margin-right: 6px">
                        <use xlink:href="'. HESK_PATH .'img/sprite.svg#icon-pin"></use>
                    </svg>' : '').$owner.'<a class="link" href="admin_ticket.php?track='.$ticket['trackid'].'&amp;Refresh='.$random.'">'.$ticket['subject'].'</a></td>';
		}

		// Print ticket status
		if ( hesk_show_column('status') )
		{
            echo '<td>' . hesk_get_admin_ticket_status($ticket['status']) . '&nbsp;</td>';
		}

		// Print ticket owner
		if ( hesk_show_column('owner') )
		{
			if ($ticket['owner'])
			{
				$ticket['owner'] = isset($admins[$ticket['owner']]) ? $admins[$ticket['owner']] : $hesklang['unas'];
			}
			else
			{
				$ticket['owner'] = $hesklang['unas'];
			}
			echo '<td>'.$ticket['owner'].'</td>';
		}

		// Print number of all replies
		if ( hesk_show_column('replies') )
		{
			echo '<td>'.$ticket['replies'].'</td>';
		}

		// Print number of staff replies
		if ( hesk_show_column('staffreplies') )
		{
			echo '<td>'.$ticket['staffreplies'].'</td>';
		}

		// Print last replier
		if ( hesk_show_column('lastreplier') )
		{
			if ($ticket['lastreplier'])
			{
				$ticket['repliername'] = isset($admins[$ticket['replierid']]) ? $admins[$ticket['replierid']] : $hesklang['staff'];
			}
			else
            {
                $customer_name = $ticket['lastreplier_customername'] === null ? $ticket['name'] : $ticket['lastreplier_customername'];
				$ticket['repliername'] = $customer_name;
			}
			echo '<td>'.$ticket['repliername'].'</td>';
		}

		// Print time worked
		if ( hesk_show_column('time_worked') )
		{
			echo '<td>'.$ticket['time_worked'].'</td>';
		}

		// Print due date
        if (hesk_show_column('due_date')) {
            $due_date = $hesklang['none'];
            if ($ticket['due_date'] != null) {
                $due_date = hesk_date($ticket['due_date'], false, true, false);
                $due_date = date($hesk_settings['format_date'], $due_date);
            }

            echo '<td>'.$due_date.'</td>';
        }

		// Print custom fields
		foreach ($hesk_settings['custom_fields'] as $key => $value)
		{
			if ($value['use'] && hesk_show_column($key) )
            {
				echo '<td>'.($value['type'] == 'date' ? hesk_custom_date_display_format($ticket[$key], $value['value']['date_format']) : $ticket[$key]).'</td>';
            }
		}

		// End ticket row
        echo '<td class="has-flex-item"><div class="td-flex">' . hesk_get_admin_ticket_priority_for_list($ticket['priority']) . '&nbsp;</div></td></tr>';

	} // End while
	} // End if total > 0 and tickets were queried

    // Only show all this if we found any tickets
    if ($total > 0)
    {
	    if (empty($group) || $is_table)
	    {
	?>
    </tbody>
	</table>
	</div>
	<?php
	    }
	?>
    <div class="pagination-wrap">
        <div class="pagination">
            <?php
            if ($pages > 1) {
                /* List pages */
                if ($pages >= 7) {
                    if ($page > 2) {
                        echo '
                        <a href="'.$href.'?'.$query_for_pagination.'1" class="btn pagination__nav-btn">
                            <svg class="icon icon-chevron-left" style="margin-right:-6px">
                              <use xlink:href="'. HESK_PATH .'img/sprite.svg#icon-chevron-left"></use>
                            </svg>
                            <svg class="icon icon-chevron-left">
                              <use xlink:href="'. HESK_PATH .'img/sprite.svg#icon-chevron-left"></use>
                            </svg>
                            '.$hesklang['pager_first'].'
                        </a>';
                    }

                    if ($prev_page)
                    {
                        echo '
                        <a href="'.$href.'?'.$query_for_pagination.$prev_page.'" class="btn pagination__nav-btn">
                            <svg class="icon icon-chevron-left">
                              <use xlink:href="'. HESK_PATH .'img/sprite.svg#icon-chevron-left"></use>
                            </svg>
                            '.$hesklang['pager_previous'].'
                        </a>';
                    }
                }

                echo '<ul class="pagination__list">';
                for ($i=1; $i<=$pages; $i++)
                {
                    if ($i <= ($page+5) && $i >= ($page-5))
                    {
                        if ($i == $page)
                        {
                            echo '
                            <li class="pagination__item is-current">
                              <a href="javascript:" class="pagination__link">'.$i.'</a>
                            </li>';
                        }
                        else
                        {
                            echo '
                            <li class="pagination__item ">
                              <a href="'.$href.'?'.$query_for_pagination.$i.'" class="pagination__link">'.$i.'</a>
                            </li>';
                        }
                    }
                }
                echo '</ul>';

                if ($pages >= 7)
                {
                    if ($next_page)
                    {
                        echo '
                        <a href="'.$href.'?'.$query_for_pagination.$next_page.'" class="btn pagination__nav-btn">
                            '.$hesklang['pager_next'].'
                            <svg class="icon icon-chevron-right">
                              <use xlink:href="'. HESK_PATH .'img/sprite.svg#icon-chevron-right"></use>
                            </svg>
                        </a>';
                    }

                    if ($page < ($pages - 1))
                    {
                        echo '
                        <a href="'.$href.'?'.$query_for_pagination.$pages.'" class="btn pagination__nav-btn">
                            '.$hesklang['pager_last'].'
                            <svg class="icon icon-chevron-right">
                              <use xlink:href="'. HESK_PATH .'img/sprite.svg#icon-chevron-right"></use>
                            </svg>
                            <svg class="icon icon-chevron-right" style="margin-left:-6px">
                              <use xlink:href="'. HESK_PATH .'img/sprite.svg#icon-chevron-right"></use>
                            </svg>
                        </a>';
                    }
                }
            } // end PAGES > 1
            ?>
        </div>
        <p class="pagination__amount"><?php echo sprintf($hesklang['tickets_on_pages'],$total,$pages); ?></p>
    </div>

    <section class="tickets__legend">
        <div>
            <?php
            if (hesk_checkPermission('can_add_archive',0))
            {
                ?>
                <div>
                    <svg class="icon icon-tag">
                        <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-tag"></use>
                    </svg>
                    <?php echo $hesklang['archived2']; ?>
                </div>
                <?php
            }
            ?>
            <div>
                <svg class="icon icon-assign">
                    <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-assign"></use>
                </svg> <?php echo $hesklang['tasy2']; ?>
            </div>
            <?php
            if (hesk_checkPermission('can_view_ass_others',0) || hesk_checkPermission('can_view_ass_by',0))
            {
                ?>
                <div>
                    <svg class="icon icon-assign-plus">
                        <use xlink:href="<?php echo HESK_PATH; ?>img/sprite.svg#icon-assign-plus"></use>
                    </svg> <?php echo $hesklang['taso2']; ?>
                </div>
                <?php
            }
            ?>
        </div>
        <div class="bulk-actions">
            <?php echo $hesklang['with_selected']; ?>
            <div class="clear-on-mobile"></div>
            <div class="inline-bottom">
                <select name="a" aria-label="<?php echo $hesklang['with_selected']; ?>">
                    <?php
                    foreach ($hesk_settings['priorities'] as $k => $v) {
                        ?>
                        <option value="<?php echo $k;?>"><?php echo $hesklang['set_pri_to'].' '.$v['name']; ?></option>
                        <?php
                    }
                    ?>
                    <?php
                    if ( hesk_checkPermission('can_resolve', 0) && ! defined('HESK_DEMO') )
                    {
                        ?>
                        <option value="close"><?php echo $hesklang['close_selected']; ?></option>
                        <?php
                    }

                    if ( hesk_checkPermission('can_add_archive', 0) )
                    {
                        ?>
                        <option value="tag"><?php echo $hesklang['add_archive_quick']; ?></option>
                        <option value="untag"><?php echo $hesklang['remove_archive_quick']; ?></option>
                        <?php
                    }

                    ?>
                    <option value="print"><?php echo $hesklang['print_selected']; ?></option>
                    <?php

                    if ( ! defined('HESK_DEMO') )
                    {
                        if ( hesk_checkPermission('can_merge_tickets', 0) )
                        {
                            ?>
                            <option value="merge"><?php echo $hesklang['mer_selected']; ?></option>
                            <?php
                        }
                        if ( hesk_checkPermission('can_link_tickets', 0) )
                        {
                            ?>
                            <option value="link_tickets"><?php echo $hesklang['link_selected_tickets']; ?></option>
                            <?php
                        }
                        if ( hesk_checkPermission('can_export', 0) )
                        {
                            ?>
                            <option value="export"><?php echo $hesklang['export_selected']; ?></option>
                            <?php
                        }
                        if ( hesk_checkPermission('can_privacy', 0) )
                        {
                            ?>
                            <option value="anonymize"><?php echo $hesklang['anon_selected']; ?></option>
                            <?php
                        }
                        if ( hesk_checkPermission('can_del_tickets', 0) )
                        {
                            ?>
                            <option value="delete"><?php echo $hesklang['del_selected']; ?></option>
                            <?php
                        }
                    } // End demo
                    ?>
                </select>
            </div>
            <input type="hidden" name="token" value="<?php hesk_token_echo(); ?>" />
            <button onclick="document.getElementById('action-type').value = 'bulk'" type="button" class="btn btn-full" ripple="ripple" data-modal="[data-modal-id='<?php echo $modal_id; ?>']">
                <?php echo $hesklang['execute']; ?>
            </button>

            <?php
            if (hesk_checkPermission('can_assign_others',0))
            {
                ?>
                <div style="height:6px"></div>

                <?php echo $hesklang['assign_selected']; ?>
                <div class="clear-on-mobile"></div>
                <div class="inline-bottom">
                    <select name="owner" aria-label="<?php echo $hesklang['assign_selected']; ?>">
                        <option value="" selected="selected"><?php echo $hesklang['select']; ?></option>
                        <option value="-1"> &gt; <?php echo $hesklang['unas']; ?> &lt; </option>
                        <?php
                        foreach ($can_assign_to as $k=>$v)
                        {
                            echo '<option value="'.$k.'">'.$v.'</option>';
                        }
                        ?>
                    </select>
                </div>
                <button type="button" name="assign" class="btn btn-full" data-modal="[data-modal-id='<?php echo $modal_id; ?>']"
                    onclick="document.getElementById('action-type').value = 'assi'">
                    <?php echo $hesklang['assi']; ?>
                </button>
                <?php
            }
            ?>
        </div>
    </section>
    <input id="action-type" name="action-type" type="hidden" value="-1">
	</form>
	<?php
    } // END ticket list if total > 0
    elseif (!isset($is_search) && $href != 'find_tickets.php' && !$is_quick_link)
    {
        // No tickets in the DB? Show a welcome message
        $res = hesk_dbQuery("SELECT COUNT(*) FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets`");
        $num = hesk_dbResult($res,0,0);
        if ($num == 0)
        {
            hesk_show_notice(
                $hesklang['welcome1'] . '<br><br>' .
                sprintf(
                    $hesklang['welcome2'],
                    '<a href="https://www.hesk.com/knowledgebase/?article=109" target="_blank">' . $hesklang['welcome3'] . '</a>'
                ), ' ', false
            );
        }
    }
} // END ticket list if total > 0 or if this is a quick link
else
{
    if (isset($is_search) || $href == 'find_tickets.php')
    {
        hesk_show_notice($hesklang['no_tickets_crit']);
    }
}


function hesk_print_list_head()
{
	global $hesk_settings, $href, $query, $sort_possible, $hesklang;

    // Make sure selecting works correctly when tickets are grouped
    if (isset($hesk_settings['hesk-group-count'])) {
        $hesk_settings['hesk-group-count']++;
    } else {
        $hesk_settings['hesk-group-count'] = 1;
    }

    $table_id = $hesk_settings['hesk-group-count'] == 1 ? 'default-table' : 'default-table-' . $hesk_settings['hesk-group-count'];
	?>
    <div class="table-wrap ignore-overflow">
	<table class="table sindu-table ticket-list sindu_origin_table" id="<?php echo $table_id; ?>">
    <thead>
    <tr>
        <th class="table__first_th sindu_handle" aria-label="<?php echo $hesklang['select']; ?>">
            <div class="checkbox-custom">
                <input type="checkbox" id="ticket_checkall<?php echo $hesk_settings['hesk-group-count']; ?>" name="checkall" value="2" onclick="hesk_changeAll(this, '<?php echo 'group' . $hesk_settings['hesk-group-count'] . "'"; ?>)">
                <label for="ticket_checkall<?php echo $hesk_settings['hesk-group-count']; ?>" aria-label="<?php echo $hesklang['a_select']; ?>">&nbsp;</label>
            </div>
        </th>
        <?php
        $sort = hesk_GET('sort', 'status');
        $sort_direction = '';
        if (isset($_GET['asc'])) {
            $sort_direction = intval($_GET['asc']) == 0 ? 'desc' : 'asc';
        } else {
            $sort_direction = 'asc';
        }

        foreach ($hesk_settings['ticket_list'] as $field)
        {
            if (!key_exists($field, $hesk_settings['possible_ticket_list'])) {
                continue;
            }

            echo '<th class="sindu-handle '.($sort == $field ? $sort_direction : '').' '.($field == 'trackid' ? 'trackid' : '').'">
                <a href="' . $href . '?' . $query . $sort_possible[$field] . '&amp;sort=' . $field . '" aria-label="' . ($hesklang['sort_by'] . ' ' . $hesk_settings['possible_ticket_list'][$field]). '">
                    <div class="sort">
                        <span>' . $hesk_settings['possible_ticket_list'][$field] . '</span>
                        <i class="handle"></i>
                    </div>
                </a>
            </th>';
        }
        ?>
        <th class="sindu-handle <?php echo $sort == 'priority' ? $sort_direction : ''; ?>">
            <a href="<?php echo $href . '?' . $query . $sort_possible['priority'] . '&amp;sort='; ?>priority" aria-label="<?php echo ($hesklang['sort_by'] . ' ' . $hesklang['priority']); ?>">
                <div class="sort">
                    <span><?php echo $hesklang['priority']; ?></span>
                    <i class="handle"></i>
                </div>
            </a>
        </th>
    </tr>
    </thead>
    <tbody>
	<?php
} // END hesk_print_list_head()
upload_attachment.inc.php
wget 'https://lists2.roe3.org/hesk/inc/upload_attachment.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}

require_once(HESK_PATH . 'inc/attachments.inc.php');
require_once(HESK_PATH . 'inc/posting_functions.inc.php');

$hesk_settings['db_failure_response'] = 'json';

function hesk_tempAttachmentJsonResponse($status_code, $message = '') {
    header('Content-Type: application/json; charset=utf-8');
    http_response_code($status_code);

    if ($message !== '') {
        print json_encode(array(
            'status' => 'failure',
            'status_code' => $status_code,
            'message' => $message
        ));
    }

    return '';
}

// Temporary attachment upload/delete requests must be POST requests protected by the session CSRF token.
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    return hesk_tempAttachmentJsonResponse(405, $hesklang['error']);
}

if (!hesk_token_compare(hesk_POST('token'))) {
    return hesk_tempAttachmentJsonResponse(403, $hesklang['eto']);
}

// Remove any expired temp attachments after the request method and CSRF token have been verified.
hesk_removeExpiredTempAttachments();

// Check if we are deleting an attachment or if we have a file to upload
if (hesk_POST('action') === 'delete') {
    $file_key = hesk_POST('fileKey', 'undefined');

    if ($file_key === 'undefined') {
        //-- Failed dropzone uploads will return an undefined saved name when removing them
        return http_response_code(204);
    }

    hesk_deleteTempAttachment($file_key, true);
    return http_response_code(204);
} elseif (!empty($_FILES)) {
    $info = hesk_uploadTempFile();
    header('Content-Type: application/json; charset=utf-8');
    http_response_code($info['status_code']);
    print json_encode($info);
    return '';
}

return hesk_tempAttachmentJsonResponse(400, $hesklang['error']);
users_online.inc.php
wget 'https://lists2.roe3.org/hesk/inc/users_online.inc.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
 *
 */

/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}


function hesk_initOnline($user_id)
{
	global $hesk_settings, $hesklang;

    /* Set user to online */
	hesk_setOnline($user_id);

    /* Can this user view online staff? */
    if (hesk_checkPermission('can_view_online',0))
    {
    	$hesk_settings['users_online'] = hesk_listOnline();
        define('SHOW_ONLINE',1);
    }

    return true;
} // END hesk_initOnline()


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

	echo '
    <div class="users-online">
        <svg class="icon icon-team" style="fill: #000">
          <use xlink:href="' . HESK_PATH . 'img/sprite.svg#icon-team"></use>
        </svg> ';
    $i = '';
    foreach ($hesk_settings['users_online'] as $tmp)
    {
        $i .= '<span ' . ($tmp['isadmin'] ? 'style="font-style:italic;"' : '') . '>';
        $i .= ($tmp['id'] == $_SESSION['id']) ? $tmp['name'] : '<a class="link" href="mail.php?a=new&id='.$tmp['id'].'">' . $tmp['name'] . '</a>';
        $i .= '</span>, ';
    }
    echo substr($i,0,-2);
    echo '</div>';

} // END hesk_printOnline()


function hesk_listOnline($list_names=1)
{
	global $hesk_settings, $hesklang, $hesk_db_link;

    $users_online = array();

    /* Clean expired entries */
    hesk_cleanOnline();

    /* Get a list of online users */
    /* --> With names */
    if ($list_names)
    {
        $res = hesk_dbQuery("SELECT `t1`.`user_id` , `t2`.`name` , `t2`.`isadmin` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."online` AS `t1` INNER JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."users` AS `t2` ON `t1`.`user_id` = `t2`.`id`");
		while ($tmp = hesk_dbFetchAssoc($res))
        {
        	$users_online[$tmp['user_id']] = array(
            	'id'		=> $tmp['user_id'],
                'name'		=> $tmp['name'],
                'isadmin'	=> $tmp['isadmin']
            );
        }
    }
    /* --> Without names */
    else
    {
        $res = hesk_dbQuery("SELECT `user_id` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."online`");
		while ($tmp = hesk_dbFetchAssoc($res))
        {
        	$users_online[] = $tmp['user_id'];
        }
    }

    return $users_online;

} // END hesk_listOnline()


function hesk_setOnline($user_id)
{
	global $hesk_settings, $hesklang, $hesk_db_link;

    /* If already online just update... */
    hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."online` SET `tmp` = `tmp` + 1 WHERE `user_id` = '".intval($user_id)."'");

	/* ... else insert a new entry */
    if ( ! hesk_dbAffectedRows() )
    {
	    hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."online` (`user_id`) VALUES (".intval($user_id).") ");
    }

    return true;

} // END hesk_setOnline()


function hesk_setOffline($user_id)
{
	global $hesk_settings, $hesklang, $hesk_db_link;

    /* If already online just update... */
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."online` WHERE `user_id` = '".intval($user_id)."'");

    return true;

} // END hesk_setOffline()


function hesk_cleanOnline()
{
	global $hesk_settings, $hesklang, $hesk_db_link;

    /* Delete old rows from the database */
    hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."online` WHERE `dt` < ( NOW() - INTERVAL ".intval($hesk_settings['online_min'])." MINUTE) ");

	return true;
} // END hesk_cleanOnline()