Ejemplo n.º 1
0
 public function render()
 {
     if (!$this->get('basedir')) {
         throw new Exception('MultiFileInput cannot be rendered without a basedir attribute!');
     }
     // Make sure it ends with a trailing slash.
     if (substr($this->get('basedir'), -1) != '/') {
         $this->_attributes['basedir'] .= '/';
     }
     //var_dump($_SESSION['multifileinputobjects'], serialize($this->_attributes)); die();
     // This is a slightly different element than the traditional form system, as it must be able to be called without
     // the rest of the form system on submit.
     // This is because this system will do an ajax submit to do the actual upload.
     if (!is_array(\Core\Session::Get('multifileinputobjects'))) {
         \Core\Session::Set('multifileinputobjects', []);
     }
     // I don't need this key to be cryptographically secure, just generally unique.
     $key = md5(serialize($this->_attributes));
     foreach (\Core\Session::Get('multifileinputobjects') as $obj) {
         if (!isset($obj['key'])) {
             continue;
         }
         if ($obj['key'] == $key) {
             $this->set('id', $obj['id']);
         }
     }
     if (!isset($this->_attributes['id'])) {
         // This system requires a valid id.
         $this->set('id', 'multifileinput-' . Core::RandomHex('2'));
     }
     $this->set('key', $key);
     $this->set('uploadkey', $key);
     // Convert the string representation of a filesize to the raw bytes.
     $size = strtoupper(str_replace(' ', '', ini_get('upload_max_filesize')));
     if (strpos($size, 'G') !== false) {
         $size = preg_replace('/[^0-9]/', '', $size);
         $size = $size * (1024 * 1024 * 1024);
     } elseif (strpos($size, 'M') !== false) {
         $size = preg_replace('/[^0-9]/', '', $size);
         $size = $size * (1024 * 1024);
     } elseif (strpos($size, 'K') !== false) {
         $size = preg_replace('/[^0-9]/', '', $size);
         $size = $size * 1024;
     }
     $this->set('maxsize', $size);
     // Now that the session variable has been initialized, the traditional session variable is reliable.
     $_SESSION['multifileinputobjects'][$key] = array('obj' => serialize($this), 'key' => $key, 'id' => $this->_attributes['id']);
     return parent::render();
 }
Ejemplo n.º 2
0
	/**
	 * Load the values from either the page request or the session data.
	 *
	 * @param PageRequest $request
	 */
	public function load(PageRequest $request){
		// First, load everything from the session.
		$this->loadSession();

		$a = array();
		$s = array();
		$p = array();

		// Check the sort keys?
		if($this->hassort){
			if($request->getParameter('sortkey')){
				$this->setSortKey($request->getParameter('sortkey'));
				$s['sortkey'] = $this->_sortkey;
			}
			if($request->getParameter('sortdir')){
				$this->setSortDirection($request->getParameter('sortdir'));
				$s['sortdir'] = $this->_sortdir;
			}
		}

		// Did the user change a filter?
		// If a filter was changed, reset back to page 1!
		if($request->getParameter('filter')){
			$filters = $request->getParameter('filter');

			foreach($filters as $f => $v){
				if(!isset($this->_elementindexes['filter[' . $f . ']'])) continue;
				/** @var $el FormElement */
				$el = $this->_elementindexes['filter[' . $f . ']'];
				$el->setValue($v);

				// Remember this for the session data.
				$a[$f] = $v;
				$this->setPage(1);
				$p['page'] = 1;
			}
		}
		// How 'bout the pagination?
		elseif($this->haspagination){
			if($request->getParameter('page')){
				$this->setPage($request->getParameter('page'));
				$p['page'] = $this->_currentpage;
			}
			elseif($request->getParameter('limit')){
				$this->setPage(1);
				$p['page'] = 1;
				
				$this->setLimit($request->getParameter('limit'));
				$p['limit'] = $this->_limit;
			}
			// Don't change the filter sets, those have been cached and are fine as-is.
		}
		else{
			// No pagination or filters were modified... don't do anything.
		}


		if(sizeof($a)){
			\Core\Session::Set('filters/' . $this->_name, $a);
		}
		if(sizeof($s)){
			\Core\Session::Set('filtersort/' . $this->_name, $s);
		}
		if(sizeof($p)){
			\Core\Session::Set('filterpage/' . $this->_name, $p);
		}
	}
Ejemplo n.º 3
0
 /**
  * Check the user's IP in the blacklist and see if it's found.
  *
  * If it is and has a high enough submission rate, (in a 24 hour period), then block the user completely and immediately.
  */
 public static function CheckIP()
 {
     $record = \sfsBlacklistModel::Construct(REMOTE_IP);
     // It's not in there, YAY!
     if (!$record->exists()) {
         return;
     }
     // Is the submission score high enough?
     $highscore = 100;
     if ($record->get('submissions') > $highscore) {
         // YOU can haz good party tiem nau
         \SystemLogModel::LogSecurityEvent('/security/blocked', 'Blocking IP due to over ' . $highscore . ' submissions to sfs in a 24 hour period.');
         die('IP Blocked due to high spam score');
     }
     // Submissions listed, but not exceedingly high?
     $warnlevel = 5;
     if ($record->get('submissions') > $warnlevel) {
         if (\Core\Session::Get('security_antispam_allowed') === null) {
             $html = '<html><body>';
             $html .= '<!-- You smell of spam.... are you sure you didn\'t come from a can?-->';
             if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['happyfuntime']) && \Core\Session::Get('happyfuntimecheck')) {
                 // It's an attempt!
                 if ($_POST['happyfuntime'] == \Core\Session::Get('happyfuntimecheck')) {
                     \SystemLogModel::LogSecurityEvent('/security/unblocked', 'User successfully answered an anti-bot math question, unblocking.');
                     \Core\Session::Set('security_antispam_allowed', true);
                 } else {
                     \SystemLogModel::LogSecurityEvent('/security/captchafailed', 'User attempted, but failed in answering an anti-bot math question.');
                     $html .= '<b>NOPE!</b>';
                 }
             }
             \SystemLogModel::LogSecurityEvent('/security/blocked', 'Blocking IP due to over ' . $warnlevel . ' submissions to sfs in a 24 hour period.');
             $random1 = rand(4, 6) * 2;
             $random2 = rand(1, 3) * 2;
             $random3 = rand(1, 2);
             switch ($random3) {
                 case 1:
                     $result = $random1 / $random2;
                     $operation = 'divided by';
                     break;
                 case 2:
                     $result = $random1 * $random2;
                     $operation = 'multiplied by';
                     break;
             }
             \Core\Session::Set('happyfuntimecheck', $result);
             switch ($random2) {
                 case 1:
                     $random2 = 'oNe';
                     break;
                 case 2:
                     $random2 = 'Tw0';
                     break;
                 case 3:
                     $random2 = 'ThRe';
                     break;
                 case 4:
                     $random2 = 'Foor';
                     break;
                 case 5:
                     $random2 = 'fIve';
                     break;
                 case 6:
                     $random2 = 'Siix';
                     break;
             }
             $html .= '<form method="POST"><p>What is ' . $random1 . ' ' . $operation . ' ' . $random2 . '?</p><input type="text" name="happyfuntime" size="3"/><input type="submit" value="GO"/></form></body></html>';
             die($html);
         }
     }
 }
Ejemplo n.º 4
0
 public function testSharedSessionBackedFacebookIgnoresUnsupportedKeyInClear()
 {
     $_SERVER['HTTP_HOST'] = 'fbrell.com';
     $fb = new PersistentFBPublic(array('appId' => self::APP_ID, 'secret' => self::SECRET, 'sharedSession' => true));
     $key = '--invalid--';
     $val = 'foo';
     $session_var_name = sprintf('%s_fb_%s_%s', $fb->publicGetSharedSessionID(), self::APP_ID, $key);
     \Core\Session::Set($session_var_name, $val);
     $fb->publicClearPersistentData($key);
     $this->assertTrue(array_key_exists($session_var_name, $_SESSION));
     $this->assertFalse($fb->publicGetPersistentData($key));
 }
Ejemplo n.º 5
0
	/**
	 * Add a message to the user's stack.
	 *    It will be displayed the next time the user (or session) renders the page.
	 *
	 * @param string $messageText The message to send to the user
	 * @param string $messageType The type of message, "success", "info", or "error"
	 *
	 * @return void
	 */
	static public function SetMessage($messageText, $messageType = 'info') {
		if(trim($messageText) == '') return;

		$messageType = strtolower($messageType);

		// CLI doesn't use sessions.
		if(EXEC_MODE == 'CLI'){
			$messageText = preg_replace('/<br[^>]*>/i', "\n", $messageText);
			echo "[" . $messageType . "] - " . $messageText . "\n";
		}
		else{
			$stack = Session::Get('message_stack', []);

			$stack[] = array(
				'mtext' => $messageText,
				'mtype' => $messageType,
			);
			Session::Set('message_stack', $stack);
		}
	}
Ejemplo n.º 6
0
	/**
	 * Sync the search index fields of every model on the system.
	 *
	 * @return int
	 */
	public function syncSearchIndex(){
		// Admin-only page.
		if(!\Core\user()->checkAccess('g:admin')){
			return View::ERROR_ACCESSDENIED;
		}

		// Just run through every component currently installed and reinstall it.
		// This will just ensure that the component is up to date and correct as per the component.xml metafile.
		$view = $this->getView();
		$changes = [];
		$outoftime = false;
		$counter = 0;
		$resume = \Core\Session::Get('syncsearchresume', 1);
		$timeout = ini_get('max_execution_time');
		// Dunno why this is returning 0, but if it is, reset it to 30 seconds!
		if(!$timeout) $timeout = 30;
		$memorylimit = ini_get('memory_limit');
		if(stripos($memorylimit, 'M') !== false){
			$memorylimit = str_replace(['m', 'M'], '', $memorylimit);
			$memorylimit *= (1024*1024);
		}
		elseif(stripos($memorylimit, 'G') !== false){
			$memorylimit = str_replace(['g', 'G'], '', $memorylimit);
			$memorylimit *= (1024*1024*1024);
		}

		foreach(\Core::GetComponents() as $c){
			/** @var Component_2_1 $c */

			if($outoftime){
				break;
			}

			foreach($c->getClassList() as $class => $file){
				if($outoftime){
					break;
				}

				if($class == 'model'){
					continue;
				}
				if(strrpos($class, 'model') !== strlen($class) - 5){
					// If the class doesn't explicitly end with "Model", it's also not a model.
					continue;
				}
				if(strpos($class, '\\') !== false){
					// If this "Model" class is namespaced, it's not a valid model!
					// All Models MUST reside in the global namespace in order to be valid.
					continue;
				}

				$ref = new ReflectionClass($class);
				if(!$ref->getProperty('HasSearch')->getValue()){
					// This model doesn't have the searchable flag, skip it.
					continue;
				}

				$c = ['name' => $class, 'count' => 0];
				$fac = new ModelFactory($class);
				while(($m = $fac->getNext())){
					++$counter;

					if($counter < $resume){
						// Allow this process to be resumed where it left off, since it may take more than 30 seconds.
						continue;
					}

					if(\Core\Utilities\Profiler\Profiler::GetDefaultProfiler()->getTime() + 5 >= $timeout){
						// OUT OF TIME!
						// Remember where this process left off and exit.
						\Core\Session::Set('syncsearchresume', $counter);
						$outoftime = true;
						break;
					}

					if(memory_get_usage(true) + 40485760 >= $memorylimit){
						// OUT OF MEMORY!
						// Remember where this process left off and exit.
						\Core\Session::Set('syncsearchresume', $counter);
						$outoftime = true;
						break;
					}

					/** @var Model $m */
					$m->set('search_index_pri', '!');
					$m->save();
					$c['count']++;
				}

				$changes[] = $c;
			}
		}

		if(!$outoftime){
			// It finished!  Unset the resume counter.
			\Core\Session::UnsetKey('syncsearchresume');
		}


		$view->title = 'Sync Searchable Index';
		$view->assign('changes', $changes);
		$view->assign('outoftime', $outoftime);
	}
Ejemplo n.º 7
0
	/**
	 * Handler to actually perform the import.
	 *
	 * @param \Form $form
	 * @return bool
	 */
	public static function FormHandler2(\Form $form) {
		$filename = Session::Get('user-import/file');
		$file = Factory::File($filename);
		/** @var $contents \Core\Filestore\Contents\ContentCSV */
		$contents = $file->getContentsObject();

		// If the user checked that it has a header... do that.
		$contents->_hasheader = $form->getElement('has_header')->get('checked');

		// Merge
		$merge = $form->getElement('merge_duplicates')->get('checked');

		// Handle the map-to directives.
		$maptos = array();
		foreach($form->getElements() as $el){
			if(strpos($el->get('name'), 'mapto[') === 0 && $el->get('value')){
				$k = substr($el->get('name'), 6, -1);
				$maptos[$k] = $el->get('value');
			}
		}

		// Handle the group mappings
		$groups = $form->getElement('groups[]')->get('value');

		// And keep a log of the bad transfers and some other data.
		$counts = ['created' => 0, 'updated' => 0, 'failed' => 0, 'skipped' => 0];
		Session::Set('user-import/fails', []);

		$incoming = $contents->parse();
		foreach($incoming as $record){
			try{
				// Create a data map of this record for fields to actually map over.
				$dat = array();
				foreach($maptos as $recordkey => $userkey){
					$dat[$userkey] = $record[$recordkey];
				}

				// No email, NO IMPORT!
				if(!$dat['email']){
					$counts['skipped']++;
					continue;
				}

				// Try to find this record by email, since that's a primary key.
				$existing = \UserModel::Find(['email = ' . $dat['email'] ], 1);
				if($existing && !$merge){
					// Skip existing records.
					$counts['skipped']++;
				}
				elseif($existing){
					// Update!
					$existing->setFromArray($dat);
					$existing->setGroups($groups);

					if($existing->save()){
						$counts['updated']++;
					}
					else{
						$counts['skipped']++;
					}
				}
				else{
					$new = new \UserModel();
					$new->setFromArray($dat);
					$new->setGroups($groups);
					$new->save();
					$counts['created']++;
				}
			}
			catch(\Exception $e){
				// @todo Handle this
				die($e->getMessage());
			}
			//
		}

		Session::Set('user-import/counts', $counts);

		return true;
	}
Ejemplo n.º 8
0
	/**
	 * This is the form handler for a password protected page.
	 *
	 * @return bool
	 */
	public static function PasswordProtectHandler(Form $form){
		/** @var PageModel $page */
		$page = $form->getElementValue('page');
		$val  = $form->getElementValue('passinput');
		if( $val !== $page->get('password_protected') ){
			\Core\set_message('t:MESSAGE_ERROR_INCORRECT_PASSWORD');
			return false;
		}
		else {
			\Core\Session::Set('page-password-protected/' . $page->get('baseurl'), $val);
			return true;
		}


	}
Ejemplo n.º 9
0
	/**
	 * Function that is fired off on page load.
	 * This checks if a form was submitted and that form was present in the SESSION.
	 *
	 * @return null
	 */
	public static function CheckSavedSessionData() {
		// This needs to ignore the /form/savetemporary.ajax page!
		// This is a custom page that's meant to intercept all POST submissions.
		if(preg_match('#^/form/(.*)\.ajax$#', REL_REQUEST_PATH)) return;

		// There has to be data in the session.
		$forms = \Core\Session::Get('FormData/*');

		$formid = (isset($_REQUEST['___formid'])) ? $_REQUEST['___formid'] : false;
		$form   = false;

		foreach ($forms as $k => $v) {
			// If the object isn't a valid object after unserializing...
			if (!($el = unserialize($v))) {
				\Core\Session::UnsetKey('FormData/' . $k);
				continue;
			}

			// Check the expires time
			if ($el->get('expires') <= Time::GetCurrent()) {
				\Core\Session::UnsetKey('FormData/' . $k);
				continue;
			}

			if ($k == $formid) {
				// Remember this for after all the checks have finished.
				$form = $el;
			}
		}

		// No form found... simple enough
		if (!$form) return;

		// Otherwise
		/** @var $form Form */

		// Ensure the submission types match up.
		if (strtoupper($form->get('method')) != $_SERVER['REQUEST_METHOD']) {
			\Core\set_message('t:MESSAGE_ERROR_FORM_SUBMISSION_TYPE_DOES_NOT_MATCH');
			return;
		}

		// Ensure the REFERRER and original URL match up.
		if($_SERVER['HTTP_REFERER'] != $form->originalurl){
			// @todo This is reported to be causing issues with production sites.
			//       If found true, this check may need to be removed / refactored.
			//\Core\set_message('Form submission referrer does not match, please try your submission again.', 'error');
			SystemLogModel::LogInfoEvent(
				'Form Referrer Mismatch',
				'Form referrer does not match!  Submitted: [' . $_SERVER['HTTP_REFERER'] . '] Expected: [' . $form->originalurl . ']'
			);
			//return;
		}

		// Run though each element submitted and try to validate it.
		if (strtoupper($form->get('method')) == 'POST') $src =& $_POST;
		else $src =& $_GET;

		$form->loadFrom($src);

		// Try to load the form from that form.  That will call all of the model's validation logic
		// and will throw exceptions if it doesn't.
		try{
			$form->getModel();

			// Still good?
			if (!$form->hasError()){
				$status = call_user_func($form->get('callsmethod'), $form);
			}
			else{
				$status = false;
			}
		}
		catch(ModelValidationException $e){
			\Core\set_message($e->getMessage(), 'error');
			$status = false;
		}
		catch(GeneralValidationException $e){
			\Core\set_message($e->getMessage(), 'error');
			$status = false;
		}
		catch(Exception $e){
			if(DEVELOPMENT_MODE){
				// Developers get the full message
				\Core\set_message($e->getMessage(), 'error');
			}
			else{
				// While users of production-enabled sites get a friendlier message.
				\Core\set_message('t:MESSAGE_ERROR_FORM_SUBMISSION_UNHANDLED_EXCEPTION');
			}
			Core\ErrorManagement\exception_handler($e);
			$status = false;
		}

		// The form was submitted.  Set its persistent flag to true so that whatever may be listening for it can retrieve the user's values.
		$form->persistent = true;

		// Regardless, bundle this form back into the session so the controller can use it if needed.
		\Core\Session::Set('FormData/' . $formid, serialize($form));

		// Fail statuses.
		if ($status === false) return;
		if ($status === null) return;

		// Guess it's not false and not null... must be good then.

		// @todo Handle an internal save procedure for "special" groups such as pageinsertables and what not.

		// Cleanup
		\Core\Session::UnsetKey('FormData/' . $formid);


		if ($status === 'die'){
			// If it's set to die, simply exit the script without outputting anything.
			exit;
		}
		elseif($status === 'back'){
			if($form->referrer && $form->referrer != REL_REQUEST_PATH){
				// Go back to the original form's referrer.
				\Core\redirect($form->referrer);
			}
			else{
				// Use Core to guess which page to redirect back to, (not as reliable).
				\Core\go_back();
			}
		}
		elseif ($status === true){
			// If the return code is boolean true, it's a reload.
			\Core\reload();
		}
		elseif($status === REL_REQUEST_PATH || $status === CUR_CALL){
			// If the page returned the same page as the current url, force a reload, (as redirect will ignore it)
			\Core\reload();
		}
		else{
			// Anything else gets sent to the redirect system.
			\core\redirect($status);
		}
	}
Ejemplo n.º 10
0
 /**
  * {@inheritdoc}
  *
  * @see BaseFacebook::clearPersistentData()
  */
 protected function clearPersistentData($key)
 {
     if (!in_array($key, self::$kSupportedKeys)) {
         self::errorLog('Unsupported key passed to clearPersistentData.');
         return;
     }
     $session_var_name = $this->constructSessionVariableName($key);
     \Core\Session::Set($session_var_name, null);
 }
Ejemplo n.º 11
0
	/**
	 * View to sudo as another user.
	 */
	public function sudo(){
		$view  = $this->getView();
		$req   = $this->getPageRequest();
		$id    = $req->getParameter(0);


		if($id){
			$model = UserModel::Construct($id);

			if(!\Core\user()->checkAccess('p:/user/users/sudo')){
				return View::ERROR_ACCESSDENIED;
			}

			if(!$req->isPost()){
				return View::ERROR_BADREQUEST;
			}

			if(!$model->exists()){
				return View::ERROR_NOTFOUND;
			}

			\Core\Session::Set('user_sudo', $model);
		}
		elseif(\Core\Session::Get('user_sudo') !== null){
			\Core\Session::UnsetKey('user_sudo');
		}

		\Core\redirect('/');
	}
Ejemplo n.º 12
0
/**
 * Add a message to the user's stack.
 *	It will be displayed the next time the user (or session) renders the page.
 *
 * @param string $message_text The message text or the MESSAGE_ string constant for i18n and automatic type detection!
 * @param string $message_type
 *
 * @return boolean (on success)
 */
function set_message($messageText, $messageType = 'info'){
	if(strpos($messageText, 't:MESSAGE_') === 0){
		// It's an i18n message!  Retrieve the locale version of text and the message type.
		$messageText = substr($messageText, 2);

		if(strpos($messageText, 'MESSAGE_SUCCESS_') === 0){
			$messageType = 'success';
		}
		elseif(strpos($messageText, 'MESSAGE_ERROR_') === 0){
			$messageType = 'error';
		}
		elseif(strpos($messageText, 'MESSAGE_TUTORIAL_') === 0){
			$messageType = 'tutorial';
		}
		elseif(strpos($messageText, 'MESSAGE_WARNING_') === 0){
			$messageType = 'warning';
		}
		elseif(strpos($messageText, 'MESSAGE_INFO_') === 0){
			$messageType = 'info';
		}
		else{
			$messageType = 'info';
		}

		if(func_num_args() > 1){
			// Use func_call to call 1, as I need to pass in the other options too!
			$messageText = call_user_func_array('t', func_get_args());
		}
		else{
			$messageText = t($messageText);
		}
	}

	// CLI doesn't use sessions, echo directly to stdout instead.
	if(EXEC_MODE == 'CLI'){
		$messageText = preg_replace('/<br[^>]*>/i', "\n", $messageText);
		echo "[" . $messageType . "] - " . $messageText . "\n";
	}
	else{
		$stack = Session::Get('message_stack', []);

		$stack[] = array(
			'mtext' => $messageText,
			'mtype' => $messageType,
		);
		Session::Set('message_stack', $stack);
	}
}
Ejemplo n.º 13
0
	public function stopError($code, $error){
		if(sizeof($this->_last) == 0){
			// Nothing to do, you must use start first!
			return;
		}

		$last = array_pop($this->_last);

		$time = microtime(true) - $last['start'];
		$timeFormatted = \Core\time_duration_format($time, 2);

		if($last['type'] == 'read'){
			++$this->_reads;
		}
		else{
			++$this->_writes;
		}

		if(DEVELOPMENT_MODE) {
			// Add this data to the SESSION if the site is currently in DEV mode.
			$events   = Session::Get('datamodel_profiler_events/events', []);
			$events[] = [
				'query'  => $last['query'],
				'type'   => $last['type'],
				'time'   => $time,
				'errno'  => $code,
				'error'  => $error,
				'caller' => $last['caller'],
				'rows'   => 0
			];
			Session::Set('datamodel_profiler_events/events', $events);

			if($last['type'] == 'read') {
				Session::Set('datamodel_profiler_events/reads', Session::Get('datamodel_profiler_events/reads') + 1);
			}
			else {
				Session::Set('datamodel_profiler_events/writes', Session::Get('datamodel_profiler_events/writes') + 1);
			}
		}

		if(defined('DMI_QUERY_LOG_TIMEOUT') && DMI_QUERY_LOG_TIMEOUT >= 0){
			if(DMI_QUERY_LOG_TIMEOUT == 0 || ($time * 1000) >= DMI_QUERY_LOG_TIMEOUT ){
				\Core\Utilities\Logger\append_to('query', '[' . $timeFormatted . '] ' . $last['query'], 0);
			}
		}
	}
Ejemplo n.º 14
0
 public function createImage()
 {
     $ini = microtime(true);
     /** Initialization */
     $this->imageAllocate();
     /** Text insertion */
     $text = $this->getCaptchaText();
     $fontcfg = $this->fonts[array_rand($this->fonts)];
     $this->writeText($text, $fontcfg);
     \Core\Session::Set($this->sessionVar, $text);
     /** Transformations */
     if (!empty($this->lineWidth)) {
         $this->writeLine();
     }
     $this->waveImage();
     if ($this->blur && function_exists('imagefilter')) {
         imagefilter($this->im, IMG_FILTER_GAUSSIAN_BLUR);
     }
     $this->reduceImage();
     if ($this->debug) {
         imagestring($this->im, 1, 1, $this->height - 8, "{$text} {$fontcfg['font']} " . round((microtime(true) - $ini) * 1000) . "ms", $this->gdFgColor);
     }
     /** Output */
     $this->writeImage();
     $this->cleanup();
 }