/**
 * @param $params
 * @param $template
 *
 * @return string
 * @throws SmartyException
 */
function smarty_function_geoiplookup($params, $template)
{
    $ip = $params[0];
    $getflag = isset($params['flag']) ? $params['flag'] : true;
    $lookup = new \geocode\IPLookup($ip);
    if ($getflag) {
        $flag = 'assets/images/iso-country-flags/' . strtolower($lookup->country) . '.png';
        $file = \Core\Filestore\Factory::File($flag);
        $cname = $lookup->getCountryName();
        if ($file->exists()) {
            $out = '<img src="' . $file->getPreviewURL('20x20') . '" title="' . $cname . '" alt="' . $lookup->country . '"/> ';
        } else {
            $out = '';
        }
    } else {
        $out = '';
    }
    if ($lookup->province && $lookup->city) {
        $out .= $lookup->city . ', ' . $lookup->province;
    } elseif ($lookup->province) {
        $out .= $lookup->province;
    } elseif ($lookup->city) {
        $out .= $lookup->city;
    }
    return $out;
}
	/**
	 * The constructor takes a File object, filename string, or URL string, and provides easy access for
	 * handling operations on that file.
	 */
	public function __construct($file) {
		if ($file instanceof File) {
			$this->_file = $file;
		}
		else {
			// @todo Add support for URLs.
			$this->_file = \Core\Filestore\Factory::File($file);
		}
	}
 /**
  * Get the image object or null
  *
  * @return Core\Filestore\File|null
  */
 public function getImage()
 {
     if ($this->get('image')) {
         $f = \Core\Filestore\Factory::File($this->get('image'));
         return $f;
     } else {
         return null;
     }
 }
示例#4
0
	public function testFilePrivate(){
		$src = new \Core\Filestore\Backends\FileLocal('core/tests/ivak_TV_Test_Screen.png');

		$dst = \Core\Filestore\Factory::File('private/tests/ivak_TV_Test_Screen.png');
		// Verify that this is a valid object.
		$this->assertInstanceOf('\Core\Filestore\File', $dst);

		// Make sure it's writable... just because :p
		$src->copyTo($dst);
		$this->assertTrue($dst->exists());
	}
示例#5
0
/**
 * Resolve a dynamic or static link with smarty.
 *
 * @todo Finish documentation of smarty_function_link
 *
 * @param array  $params  Associative (and/or indexed) array of smarty parameters passed in from the template
 * @param Smarty $smarty  Parent Smarty template object
 *
 * @return string
 */
function smarty_function_link($params, $smarty){
	
	$assign= (isset($params['assign']))? $params['assign'] : false;
	
	// I don't really care what the parameter's called to be honest...
	if(isset($params['href'])) $href = $params['href'];
	elseif(isset($params['link'])) $href = $params['link'];
	elseif(isset($params['to'])) $href = $params['to'];
	elseif(isset($params[0])) $href = $params[0];
	else $href = '/';

	$originalHref = $href;
	
	$href = \Core\resolve_link($href);

	if(!$href && strpos($originalHref, 'public/') === 0){
		$file = \Core\Filestore\Factory::File($originalHref);
		if($file->exists()){
			$href = $file->getURL();
		}
	}

	if(isset($params['ssl'])){
		// Perform SSL translation of some sort.
		$ssl = (isset($_SERVER['HTTPS']));

		if(
			($ssl && $params['ssl'] == 'auto') ||
			$params['ssl'] == '1' ||
			$params['ssl'] == 'true'
		){
			$href = str_replace('http://', 'https://', $href);
		}
		elseif(!$params['ssl']){
			$href = str_replace('https://', 'http://', $href);
		}
	}

	if($assign){
		$smarty->assign($assign, $href);
	}
	else{
		return $href;
	}
}
 /**
  * Simple method to handle any legacy call to favicon.ico
  */
 public function index()
 {
     $view = $this->getView();
     $image = ConfigHandler::Get('/favicon/image');
     $file = \Core\Filestore\Factory::File($image);
     $view->contenttype = 'image/png';
     $view->record = false;
     $view->mode = View::MODE_NOOUTPUT;
     // Fix Bug #562, Favicon "none" option.
     // Do not render anything if no icon is selected.
     if (!$image) {
         return;
     }
     if (!$file->exists()) {
         return;
     }
     $file->displayPreview('32x32!');
 }
 public function fetch()
 {
     $data = [];
     $image = ConfigHandler::Get('/favicon/image');
     if ($image) {
         $file = \Core\Filestore\Factory::File($image);
     } else {
         // Check the theme default.
         $file = \Core\Filestore\Factory::File('asset/images/favicon.png');
         //return [];
     }
     if (!$file->exists()) {
         return [];
     }
     $data['favicon'] = '<link rel="icon" type="image/png" href="' . $file->getPreviewURL('32x32!') . '"/>';
     $data['favicon-apple-touch-icon'] = '<link rel="apple-touch-icon" type="image/png" sizes="72x72" href="' . $file->getPreviewURL('72x72!') . '"/>';
     $data['favicon-apple-touch-icon-2'] = '<link rel="apple-touch-icon" type="image/png" sizes="114x114" href="' . $file->getPreviewURL('114x114!') . '"/>';
     $data['favicon-apple-touch-icon-retina'] = '<link rel="apple-touch-icon" sizes="512x512" type="image/png" href="' . $file->getPreviewURL('512x512!') . '"/>';
     $data['favicon-windows-8'] = '<meta name="msapplication-TileImage" content="' . $file->getPreviewURL('270x270!') . '"/>';
     return $data;
 }
 /**
  * Save a new or existing album
  *
  * @static
  * @param Form $form
  * @return mixed
  */
 public static function SaveAlbum(Form $form)
 {
     /** @var GalleryAlbumModel $model */
     $model = $form->getModel('model');
     /** @var $page PageModel */
     $page = $form->getModel('page');
     $page->set('fuzzy', 1);
     if ($page->get('rewriteurl') == '/' || $page->get('rewriteurl') == '') {
         Core::SetMessage('Galleries cannot be installed on the root of your site!  Please change the URL to something other than "/".', 'error');
         return false;
     }
     // Update the model cache data
     $model->set('title', $page->get('title'));
     //var_dump($model); die();
     $model->save();
     // Make sure the directory exists.
     $dir = \Core\Filestore\Factory::Directory($model->getFullUploadDirectory());
     $dir->mkdir();
     // w00t
     return $page->getResolvedURL();
 }
示例#9
0
	public function render(){

		if(!$this->get('templatename')){
			throw new Exception('Unable to render pageselectinput element without templatename set!');
		}
		// Figure out the template directory for custom pages, (if it exists)
		// In order to get the types, I need to sift through all the potential template directories and look for a directory
		// with the matching name.
		$tmpname = substr($this->get('templatename'), 0, -4) . '/';

		$matches = array();

		foreach(\Core\Templates\Template::GetPaths() as $d){
			if(is_dir($d . $tmpname)){
				// Yay, sift through that and get the files!
				$dir = \Core\Filestore\Factory::Directory($d . $tmpname);
				foreach($dir->ls('tpl') as $file){
					// Skip directories
					if($file instanceof \Core\Filestore\Directory) continue;

					/** @var $file \Core\Filestore\File */
					//$fullpath = $tmpname . $file->getBaseFilename();
					$fullpath = $file->getBaseFilename();
					$name = $file->getBaseFilename();
					// Do some template updates and make it a little more friendlier to read.
					$name = ucwords(str_replace('-', ' ', substr($name, 0, -4))) . ' Template';
					$matches[ $fullpath ] = $name;
				}
			}
		}

		// Are there any matches?  If not just return a blank string.
		if(!sizeof($matches)){
			return '';
		}

		$options = array_merge(array('' => '-- Default Page Template --'), $matches);
		$this->set('options', $options);

		return parent::render();
	}
	/**
	 * Get an array of alternative display templates for this instance.
	 *
	 * This is based on the widget's baseurl.
	 *
	 * @return array
	 */
	public function getAlternativeTemplateOptions(){
		$parts = $this->splitParts();

		// Figure out the template directory for custom pages, (if it exists)
		// In order to get the types, I need to sift through all the potential template directories and look for a directory
		// with the matching name.
		$tmpname = 'widgets' . strtolower('/' . substr($parts['controller'], 0, -6) . '/' . $parts['method']);

		$matches = [];

		foreach(\Core\Templates\Template::GetPaths() as $d){
			if(is_dir($d . $tmpname)){
				// Yay, sift through that and get the files!
				$dir = \Core\Filestore\Factory::Directory($d . $tmpname);
				foreach($dir->ls('tpl') as $file){
					// Skip directories
					if($file instanceof \Core\Filestore\Directory) continue;

					/** @var $file \Core\Filestore\File */
					//$fullpath = $tmpname . $file->getBaseFilename();
					$name = $fullpath = $file->getBaseFilename();
					// Do some template updates and make it a little more friendlier to read.
					$name = ucwords(str_replace('-', ' ', substr($name, 0, -4))) . ' Template';
					$matches[ $fullpath ] = $name;
				}
			}
		}

		return ['' => '-- Default Template --'] + $matches;
	}
	public static function _UploadHandler(Form $form) {
		$localfile = \Core\Filestore\Factory::File($form->getElement('upload')->get('value'));
		$localobj = $localfile->getContentsObject();
		if(!$localobj instanceof Core\Filestore\Contents\ContentTGZ){
			$localfile->delete();
			\Core\set_message('Invalid file uploaded', 'error');
			return false;
		}
		
		$tmpdir = $localobj->extract('tmp/installer-' . Core::RandomHex(4));
		
		// There should now be a package.xml metafile inside that temporary directory.
		// Parse it to get the necessary information for this package.
		$metafile = \Core\Filestore\Factory::File($tmpdir->getPath() . 'package.xml');
		if(!$metafile->exists()){
			$localfile->delete();
			$tmpdir->delete();
			\Core\set_message('Invalid package, package does not contain a "package.xml" file.');
			return false;
		}
		
		$pkg     = new PackageXML($metafile->getFilename());
		$key     = str_replace(' ', '-', strtolower($pkg->getName()));
		$name    = $pkg->getName();
		$type    = $pkg->getType();
		$version = $pkg->getVersion();
		
		// Validate the contents of the package.
		if(!(
			$type == 'component' ||
			$type == 'theme' ||
			$type == 'core'
		)){
			$localfile->delete();
			$tmpdir->delete();
			\Core\set_message('Invalid package, package does not appear to be a valid Core package.');
			return false;
		}

		// Now that the data is extracted in a temporary directory, extract every file in the destination.
		/** @var $datadir \Core\Filestore\Directory */
		$datadir = $tmpdir->get('data/');
		if(!$datadir){
			\Core\set_message('Invalid package, package does not contain a "data" directory.');
			return false;
		}
		
		if($type == 'component'){
			$destdir = ROOT_PDIR . 'components/' . $key . '/';
		}
		elseif($type == 'theme'){
			$destdir = ROOT_PDIR . 'themes/' . $key . '/';
		}
		else{
			$destdir = ROOT_PDIR . '/';
		}

		try{
			// Will give me an array of Files in the data directory.
			$files = $datadir->ls(null, true);
			// Used to get the relative path for each contained file.
			$datalen = strlen($datadir->getPath());
			foreach($files as $file){
				if(!$file instanceof \Core\Filestore\Backends\FileLocal) continue;

				// It's a file, copy it over.
				// To do so, resolve the directory path inside the temp data dir.
				$dest = \Core\Filestore\Factory::File($destdir . substr($file->getFilename(), $datalen));
				/** @var $dest \Core\Filestore\Backends\FileLocal */
				$dest->copyFrom($file, true);
			}
		}
		catch(Exception $e){
			// OH NOES!
			$localfile->delete();
			$tmpdir->delete();
			\Core\set_message($e->getMessage(), 'error');
			return false;
		}
		
		
		// Cleanup everything
		$localfile->delete();
		$tmpdir->delete();

		// Clear the cache so the next pageload will pick up on the new components and goodies.
		\Core\Cache::Flush();
		\Core\Templates\Backends\Smarty::FlushCache();
		
		// Print a nice message to the user that it completed.
		\Core\set_message('Successfully installed ' . $name . ' ' . $version, 'success');
		return '/updater';
	}
示例#12
0
	/**
	 * Decrypt the encrypted/signed file and return a valid File object
	 *
	 * @return mixed
	 */
	public function decrypt($dest = false) {
		if ($dest) {
			if (is_a($dest, 'File') || $dest instanceof Filestore\File) {
				// Don't need to do anything! The object either is a File
				// Or is an implmentation of the File interface.
			}
			else {
				// Well it should be damnit!....
				$file = $dest;

				// Is the destination a directory or filename?
				// If it's a directory just tack on this current file's basename.
				if (substr($file, -1) == '/') {
					$file .= $this->_file->getBaseFilename();
				}

				// Drop the .asc extension if it's there.
				if ($this->_file->getExtension() == 'asc') $file = substr($file, 0, -4);

				$dest = Filestore\Factory::File($file);
			}

			// And load up the contents!
			$dest->putContents($this->decrypt());

			return $dest;
		}
		else {
			// Extract and return the file contents
			ob_start();
			passthru('gpg --homedir "' . GPG_HOMEDIR . '" --no-permission-warning --decrypt "' . $this->_file->getLocalFilename() . '"');
			$content = ob_get_contents();
			ob_end_clean();

			return $content;
		}
	}
示例#13
0
			}
			else{
				CLI::PrintActionStatus('fail');
				CLI::PrintError('Unable to find required component ' . $dat['name']);
				die();
			}
		}
		else{
			CLI::PrintActionStatus('ok');
			CLI::PrintLine('Found ' . $dat['name'] . '-' . $compversion . ' in a local package.');
			$dat['tgz'] = $dat['src'] . '/' . $dat['keyname'] . '-' . $compversion . '.tgz';
		}

		CLI::PrintActionStart("Extracting tarball");
		if(!is_dir($dat['dest'])){
			$d = \Core\Filestore\Factory::Directory($dat['dest']);
			$d->mkdir();
		}
		exec('tar -xzf ' . $dat['tgz'] . ' -C ' . $dat['dest'] . ' --transform "s:\./data::" ./data', $out, $result);
		if($result != 0){
			CLI::PrintActionStatus('fail');
			CLI::PrintLine($out);
			die();
		}
		CLI::PrintActionStatus('ok');

		CLI::PrintActionStart("Processing CHANGELOG");
		if($dat['keyname'] == 'core'){
			$parser = new Core\Utilities\Changelog\Parser($dat['name'], $dat['dest'] . '/core/CHANGELOG');
		}
		else{
示例#14
0
 /**
  * Get all screenshots in this metafile
  *
  * @return array
  */
 public function getScreenshots()
 {
     $s = $this->_xmlloader->getElements('//screenshots/screenshot');
     if (!$s) {
         return [];
     } else {
         $f = \Core\Filestore\Factory::File($this->getBaseDir() . $s->getAttribute('file'));
         return ['file' => $f, 'title' => $s->getAttribute('title') ? $s->getAttribute('title') : $this->getName()];
     }
 }
示例#15
0
	/**
	 * Process an SQL file and return an array of generic dataset objects.
	 *
	 * <h3>Usage Examples</h3>
	 *
	 *
	 * <h4>Example 1</h4>
	 * <p>Standard Usage</p>
	 * <code>
	 * // Some code for example 1
	 * $file = ROOT_PDIR . 'components/foo/upgrades/000-do-something-awesome.sql';
	 * $records = mysqli_backend::ProcessSQLFile($file);
	 * foreach($records as $rec){
	 *     $rec->execute();
	 * }
	 * </code>
	 *
	 * @param $file
	 *
	 * @throws \Exception
	 *
	 * @return array
	 */
	public static function ProcessSQLFile($file){
		if(is_scalar($file)){
			$file = Factory::File($file);
		}
		elseif(!$file instanceof File){
			throw new \Exception('Please ensure that the argument for ProcessSQLFile is either a string or a valid File object!');
		}

		$contents = $file->getContents();

		$parser = new \SQL_Parser_Dataset($contents, \SQL_Parser::DIALECT_MYSQL);
		return $parser->parse();
	}
示例#16
0
	public static function FlushCache(){
		
		$dir = \Core\Filestore\Factory::Directory(TMP_DIR . 'smarty_templates_c');
		foreach($dir->ls('php') as $file){
			/** @var \Core\Filestore\File $file */
			$file->delete();
		}

		$dir = \Core\Filestore\Factory::Directory(TMP_DIR . 'smarty_cache');
		foreach($dir->ls('php') as $file){
			/** @var \Core\Filestore\File $file */
			$file->delete();
		}
	}
 public static function _SaveEditorHandler(Form $form)
 {
     $newmodel = $form->getModel();
     $file = $form->getElement('file')->get('value');
     $activefile = $form->getElement('filetype')->get('value');
     // The inbound file types depends on how to read the file.
     switch ($activefile) {
         case 'template':
             $filename = \Core\Templates\Template::ResolveFile($file);
             $customfilename = ROOT_PDIR . 'themes/custom/' . $file;
             break;
         case 'file':
             $filename = $file;
             // It'll get transposed.
             $customfilename = ROOT_PDIR . 'themes/custom/' . $file;
             break;
         default:
             \Core\set_message('Unsupported file type: ' . $activefile, 'error');
             return false;
     }
     $customfh = \Core\Filestore\Factory::File($customfilename);
     if ($customfh->exists()) {
         // If the custom one exists... this will be the source file too!
         $sourcefh = $customfh;
     } else {
         $sourcefh = \Core\Filestore\Factory::File($filename);
     }
     // Check and see if they're the same, ie: no change.  I don't want to create a bunch of moot revisions.
     if ($newmodel->get('content') == $sourcefh->getContents()) {
         \Core\set_message('No changes performed.', 'info');
         return '/theme';
     }
     // Before I overwrite this file, check and see if the original has been snapshot first!
     $c = ThemeTemplateChangeModel::Count(['filename = ' . $file]);
     if (!$c) {
         $original = new ThemeTemplateChangeModel();
         $original->setFromArray(['comment' => 'Original File', 'filename' => $file, 'content' => $sourcefh->getContents(), 'content_md5' => $sourcefh->getHash(), 'updated' => $sourcefh->getMTime()]);
         $original->save();
     }
     // All destination files get written to the custom directory!
     $customfh->putContents($newmodel->get('content'));
     $hash = $customfh->getHash();
     /*
     		// What happens now is based on the type of the inbound file.
     		switch($activefile){
     			case 'skin':
     				// Just replace the contents of that file.
     				$fh->putContents($newmodel->get('content'));
     				$hash = $fh->getHash();
     				break;
     			case 'template':
     				// This gets written into the current theme directory.
     				$themefh = \Core\Filestore\Factory::File(ROOT_PDIR . 'themes/' . ConfigHandler::Get('/theme/selected') . '/' . $file);
     				$themefh->putContents($newmodel->get('content'));
     				$hash = $themefh->getHash();
     				break;
     			case 'style':
     			case 'file':
     				// This gets written into the current theme directory.
     				$themefh = \Core\Filestore\Factory::File(ROOT_PDIR . 'themes/' . ConfigHandler::Get('/theme/selected') . '/' . $file);
     				$themefh->putContents($newmodel->get('content'));
     				$hash = $themefh->getHash();
     
     				// This is required to get assets updated to the CDN correctly.
     				$theme = ThemeHandler::GetTheme();
     				$hash = $themefh->getHash();
     				$theme->addAssetFile(array('file' => $file, 'md5' => $hash));
     				$theme->save();
     				$theme->reinstall();
     			default:
     		}
     */
     // Make a record of this change too!
     $change = new ThemeTemplateChangeModel();
     $change->setFromArray(['comment' => $newmodel->get('comment'), 'filename' => $file, 'content' => $newmodel->get('content'), 'content_md5' => $hash]);
     $change->save();
     if ($activefile == 'file') {
         // Reinstall all assets too!
         foreach (Core::GetComponents() as $component) {
             $component->reinstall();
         }
         // And the current theme.
         ThemeHandler::GetTheme(ConfigHandler::Get('/theme/selected'))->reinstall();
     }
     \Core\set_message('Updated file successfully', 'success');
     return '/theme';
 }
示例#18
0
	public function testPutContents() {
		$contents = 'Some Example Content';

		$file1 = \Core\Filestore\Factory::File('tmp/test-filelocaltest-putcontents.dat');

		$this->assertTrue($file1->putContents($contents));
		$this->assertTrue($file1->exists());
		$this->assertEquals($contents, $file1->getContents());
		$this->assertTrue($file1->delete());
	}
示例#19
0
	/**
	 *  Load the data from the files
	 */
	private static function _LoadData() {

		// The key for this data, must be unique on the system.
		$cachekey = 'useragent-browsecap-data';
		// The number of seconds to have Core cache the records.
		$cachetime = SECONDS_ONE_WEEK;

		$cache = Cache::Get($cachekey, $cachetime);

		if($cache === false){
			$file   = \Core\Filestore\Factory::File('tmp/php_browscap.ini');
			$remote = \Core\Filestore\Factory::File(self::$_ini_url);

			$rcontents = $remote->getContentsObject();
			if($rcontents instanceof ContentGZ){
				// yay...
				// Core handles all the remote file caching automatically, so no worries about anything here.
				$rcontents->uncompress($file);
			}
			else {
				// Ok, it may be a standard text file then... try the conventional logic.
				// Doesn't exist? download it!
				if(!$file->exists()){
					$remote->copyTo($file);
				}

				// Too old? download it!
				if($file->getMTime() < (\Time::GetCurrent() - self::$updateInterval)){
					$remote->copyTo($file);
				}
			}

			$_browsers = parse_ini_file($file->getFilename(), true, INI_SCANNER_RAW);
			$patterns = [];
			$browsers = [];

			// Trim the header off the file.
			array_shift($_browsers);


			// Grab all the property keys for browsers.
			$properties = array_keys($_browsers['DefaultProperties']);
			array_unshift(
				$properties,
				'browser_name',
				'browser_name_regex',
				'browser_name_pattern',
				'Parent'
			);

			// This creates a sorted set of user agents... used by the internal logic.
			$uas = array_keys($_browsers);
			usort(
				$uas,
				create_function(self::ORDER_FUNC_ARGS, self::ORDER_FUNC_LOGIC)
			);


			$user_agents_keys = array_flip($uas);
			$properties_keys = array_flip($properties);

			$search = ['\*', '\?'];
			$replace = ['.*', '.'];

			foreach ($uas as $user_agent) {
				$browser = [];
				$pattern = preg_quote($user_agent, self::REGEX_DELIMITER);
				$patterns[] = self::REGEX_DELIMITER
					. '^'
					. str_replace($search, $replace, $pattern)
					. '$'
					. self::REGEX_DELIMITER;

				if (!empty($_browsers[$user_agent]['Parent'])) {
					$parent = $_browsers[$user_agent]['Parent'];
					$_browsers[$user_agent]['Parent'] = $user_agents_keys[$parent];
				}

				foreach ($_browsers[$user_agent] as $key => $value) {
					//$key = $properties_keys[$key] . ".0";
					$key = $properties_keys[$key];
					$browser[$key] = $value;
				}

				$browsers[] = $browser;
				unset($browser);
			}
			unset($user_agents_keys, $properties_keys, $_browsers);

			Cache::Set(
				$cachekey,
				[
					'browsers'   => $browsers,
					'useragents' => $uas,
					'patterns'   => $patterns,
					'properties' => $properties,
				],
				$cachetime
			);
		}

		return Cache::Get($cachekey, $cachetime);
	}
示例#20
0
	/**
	 * Get the temporary local version of the file.
	 * This is useful for doing operations such as hash and identicalto.
	 *
	 * @return FileLocal
	 */
	private function _getTmpLocal() {
		if ($this->_tmplocal === null) {
			// If this FTP object is simply a proxy for the local file store, I can cheat and not actually request the files over FTP.
			// This makes it quicker.
			if($this->_ftp->isLocal){
				$this->_tmplocal = new FileLocal(ROOT_PDIR . $this->_filename);
			}
			else{
				$filename = $this->getFilename();
				$fhash = md5($filename);

				$this->_tmplocal = Filestore\Factory::File('tmp/remotefile-cache/' . $fhash);

				if(!$this->_tmplocal->exists()){
					ftp_get($this->_ftp->getConn(), $this->_tmplocal->getFilename(), $filename, FTP_BINARY);
				}

				// Since I have a local copy, I might as well make sure that the cache is as updated as possible.
				if(!$this->_ftp->getFileHash($filename)){
					$this->_ftp->setFileHash($filename, $this->_tmplocal->getHash());
				}
				if(!$this->_ftp->getFileModified($filename)){
					$this->_ftp->setFileModified($filename, ftp_mdtm($this->_ftp->getConn(), $filename));
				}
				if(!$this->_ftp->getFileSize($filename)){
					$this->_ftp->setFileSize($filename, ftp_size($this->_ftp->getConn(), $filename));
				}
			}
		}
		return $this->_tmplocal;
	}
示例#21
0
	/**
	 * Get the temporary local version of the file.
	 * This is useful for doing operations such as hash and identicalto.
	 *
	 * @return FileLocal
	 */
	protected function _getTmpLocal() {
		if ($this->_tmplocal === null) {
			$f = md5($this->getFilename());

			// Gotta love obviously-named flags.
			$needtodownload = true;

			$this->_tmplocal = Filestore\Factory::File('tmp/remotefile-cache/' . $f);

			// File exists already!  Check and see if it needs to be redownloaded.
			if ($this->cacheable && $this->_tmplocal->exists()) {
				// Lookup this file in the system cache.
				$systemcachedata = Cache::Get('remotefile-cache-header-' . $f);
				if ($systemcachedata && isset($systemcachedata['headers'])) {
					// I can only look them up if the cache is available.

					// First check will be the expires header.
					if(isset($systemcachedata['headers']['Expires']) && strtotime($systemcachedata['headers']['Expires']) > time()){
						$needtodownload = false;
						// And set the headers!
						// This is required
						$this->_headers = $systemcachedata['headers'];
						$this->_response = $systemcachedata['response'];
					}
					// Next, try ETag.
					elseif ($this->_getHeader('ETag') && isset($systemcachedata['headers']['ETag'])) {
						$needtodownload = ($this->_getHeader('ETag') != $systemcachedata['headers']['ETag']);
					}
					// No?  How 'bout 
					elseif ($this->_getHeader('Last-Modified') && isset($systemcachedata['headers']['Last-Modified'])) {
						$needtodownload = ($this->_getHeader('Last-Modified') != $systemcachedata['headers']['Last-Modified']);
					}
					// Still no?  The default is to download it anyway.
				}
			}

			if ($needtodownload || !$this->cacheable) {
				// Make sure that the headers are updated, this is a requirement to use the 302 tag.
				$this->_getHeaders();
				if(($this->_response == '302' || $this->_response == '301') && $this->_redirectFile !== null){
					$this->_tmplocal = $this->_redirectFile->_getTmpLocal();
				}
				else{
					// BTW, use cURL.
					$curl = curl_init();
					curl_setopt_array(
						$curl, array(
							CURLOPT_HEADER         => false,
							CURLOPT_NOBODY         => false,
							CURLOPT_RETURNTRANSFER => true,
							CURLOPT_URL            => $this->getURL(),
							CURLOPT_HTTPHEADER     => \Core::GetStandardHTTPHeaders(true),
						)
					);

					$result = curl_exec($curl);
					if($result === false){
						switch(curl_errno($curl)){
							case CURLE_COULDNT_CONNECT:
							case CURLE_COULDNT_RESOLVE_HOST:
							case CURLE_COULDNT_RESOLVE_PROXY:
								$this->_response = 404;
								return $this->_tmplocal;
								break;
							default:
								$this->_response = 500;
								return $this->_tmplocal;
								break;
						}
					}

					curl_close($curl);

					// Copy the data down to the local file.
					$this->_tmplocal->putContents($result);
				}

				// And remember this header data for nexttime.
				Cache::Set(
					'remotefile-cache-header-' . $f,
					[
						'headers'  => $this->_getHeaders(),
						'response' => $this->_response,
					]
				);
			}
		}

		return $this->_tmplocal;
	}
示例#22
0
 private function _lookup()
 {
     // At least address or city are required.
     if (!($this->address1 && $this->city || $this->city || $this->postal || $this->fullAddress)) {
         throw new \Exception('At least the address or city are required for geocode lookups.');
     }
     // a couple of required vars to sign the request url
     $private_key = \ConfigHandler::Get('/googlemaps/enterprise/privatekey');
     $clientname = \ConfigHandler::Get('/googlemaps/enterprise/clientname');
     $params = ['address' => null, 'sensor' => $this->sensor ? 'true' : 'false'];
     if ($this->address1 && $this->city) {
         $params['address'] = $this->address1 . ($this->city ? ', ' . $this->city : '') . ($this->state ? ', ' . $this->state : '');
     } elseif ($this->postal) {
         $params['address'] = $this->postal;
     } elseif ($this->fullAddress) {
         $params['address'] = $this->fullAddress;
     }
     if ($this->country) {
         $params['region'] = $this->country;
     }
     if ($clientname) {
         // Only add the client parameter if it's set in the config, otherwise it's a guest connection.
         $params['client'] = $clientname;
     }
     // Make a request to google and update the record.
     // http://maps.googleapis.com/maps/api/geocode/json?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&sensor=true_or_false
     $ps = [];
     foreach ($params as $k => $v) {
         $ps[] = $k . '=' . urlencode(trim($v));
     }
     if ($private_key) {
         // Use the enterprise-friendly URL.
         // the url to sign
         $signurl = '/maps/api/geocode/json?' . implode('&', $ps);
         //zee signature!
         $signature = hash_hmac("sha1", $signurl, base64_decode(strtr($private_key, '-_', '+/')), true);
         $signature = strtr(base64_encode($signature), '+/', '-_');
         //var_dump($signature); die();
         $url = 'http://maps.googleapis.com/maps/api/geocode/json?' . implode('&', $ps) . '&signature=' . $signature;
     } else {
         $url = 'http://maps.googleapis.com/maps/api/geocode/json?' . implode('&', $ps);
     }
     // Make the request
     // Since this is making use of Core's native File system, caching of remote files is builtin for free :)
     $request = \Core\Filestore\Factory::File($url);
     $json = $request->getContents();
     $contents = json_decode($json, true);
     $result = new GeocodeResponse($contents);
     return $result;
 }
示例#23
0
	/**
	 * Get an associative array of all metadata associated to the requested file.
	 *
	 * @param string $file
	 *
	 * @return array
	 * @throws \Exception
	 */
	public function getMetas($file){
		$allkeys = ['filename', 'hash', 'modified', 'size'];

		if($this->_contents === null){

			$this->_contents = [];

			$remotefile = $this->_dir . '.ftpmetas';
			$f = md5($remotefile);

			$this->_local = Factory::File('tmp/remotefile-cache/' . $f);

			if(
				(!$this->_local->exists()) ||
				($this->_local->exists() && $this->_local->getMTime() + 1800 < DateTime::NowGMT())
			){
				// Only try to open the remote file if it exists.
				if(ftp_size($this->_ftp->getConn(), $remotefile) != -1){
					// The file doesn't exist OR the file does but it hasn't been modified in the past 30 minutes.
					$this->_local->putContents('');
					ftp_get($this->_ftp->getConn(), $this->_local->getFilename(), $remotefile, FTP_BINARY);
				}
			}


			if(!$this->_local->exists()){
				// The remote file doesn't exist, so nothing was downloaded.
				// Just return a blank array.
				return array_merge($allkeys, ['filename' => $file]);
			}

			// Read this CSV file into the contents array.
			$fh = fopen($this->_local->getFilename(), 'r');
			if(!$fh){
				throw new \Exception('Unable to open ' . $this->_local->getFilename() . ' for reading.');
			}
			$line    = 0;
			$map     = [];
			$headers = [];
			do{
				$data = fgetcsv($fh, 2048);

				// Meh.  Could do this inside a standard while statement, but same diff.
				if($data === null) break;
				if($data === false) break;

				$line++;
				if($line == 1){
					// This is the header.
					$map = $data;
					foreach($data as $k => $v){
						$headers[$v] = $k;
					}

					foreach($allkeys as $key){
						if(!isset($headers[$key])){
							$map[] = $key;
							$headers[$key] = -1;
						}
					}
				}
				else{
					$assoc = [];
					foreach($map as $k => $v){
						$assoc[$v] = isset($data[$k]) ? $data[$k] : '';
					}
					if(!isset($assoc['filename'])){
						// Invalid CSV input.
						fclose($fh);
						return array_merge($allkeys, ['filename' => $file]);
					}

					$this->_contents[ $assoc['filename'] ] = $assoc;
				}
			}
			while(true);
		}

		return isset($this->_contents[$file]) ? $this->_contents[$file] : array_merge($allkeys, ['filename' => $file]);
	}
示例#24
0
	/**
	 * Verify that some given data has a valid signature.
	 *
	 * Calls verifyFileSignature internally!
	 *
	 * @param string $signature
	 * @param string $content
	 *
	 * @throws \Exception
	 *
	 * @return Signature
	 */
	public function verifyDataSignature($signature, $content){
		// First, write a temporary file to contain the signature.
		$sig = \Core\Filestore\Factory::File('tmp/gpg-verify-' . \Core::RandomHex(6) . '.asc');
		$sig->putContents($signature);

		// And the content
		$con = \Core\Filestore\Factory::File('tmp/gpg-verify-' . \Core::RandomHex(6) . '.dat');
		$con->putContents($content);

		try{
			$result = $this->verifyFileSignature($sig, $con);
		}
		catch(\Exception $e){
			// Cleanup.
			$sig->delete();
			$con->delete();

			throw $e;
		}

		$sig->delete();
		$con->delete();
		return $result;
	}
示例#25
0
 /**
  * Get the original file associated to this image.
  *
  * This is critical because the getFile may return the *rotated* image!
  *
  * @return \Core\Filestore\File
  */
 public function getOriginalFile()
 {
     return \Core\Filestore\Factory::File($this->get('file'));
 }
 public static function ImportList($filename)
 {
     // Download the latest blacklist from sfs and import it into the system.
     // Each record will be in a format such as
     // "5.144.176.232","7","2012-12-11 00:08:10"
     // IP, number of submissions, date
     $remotefile = \Core\Filestore\Factory::File($filename);
     // Does it exist?
     if (!$remotefile->exists()) {
         echo 'Unable to read file ' . $remotefile->getFilename() . ', it does not appear to exist!';
         return false;
     }
     $contents = $remotefile->getContentsObject();
     // Make sure it's a zip file.  If it's not, then we'll have problems.
     if (!$contents instanceof \Core\Filestore\Contents\ContentZIP) {
         echo 'File ' . $remotefile->getFilename() . ' does not appear to be a zip file, aborting extraction!';
         return false;
     }
     $dest = 'tmp/sfs-blacklist/';
     /** @var $extracted \Core\Filestore\Directory */
     $extracted = $contents->extract($dest);
     /** @var $file \Core\Filestore\File */
     $file = $extracted->get('listed_ip_1_all.txt');
     // Do this line by line instead of reading the entire contents into memory.
     $fh = fopen($file->getFilename(), 'r');
     if (!$fh) {
         echo 'Unable to open file ' . $file->getFilename() . ' for reading, aborting extraction!';
         return false;
     }
     $recordscount = 0;
     $newcount = 0;
     $updatedcount = 0;
     $skippedcount = 0;
     while (!feof($fh)) {
         $line = fgetcsv($fh);
         // Don't know why.....
         if (!$line[0]) {
             continue;
         }
         ++$recordscount;
         // If the record count is too low to even care about... just skip it.
         if ($line[1] <= 2) {
             ++$skippedcount;
             continue;
         }
         $record = \sfsBlacklistModel::Construct($line[0]);
         $record->setFromArray(array('submissions' => $line[1], 'lastseen' => $line[2]));
         if (!$record->exists()) {
             ++$newcount;
             $record->save();
         } elseif ($record->save()) {
             ++$updatedcount;
         } else {
             ++$skippedcount;
         }
     }
     fclose($fh);
     echo 'Processed ' . $recordscount . ' records from ' . $remotefile->getFilename() . ' successfully!' . "\n";
     echo 'New Records: ' . $newcount . "\n";
     echo 'Updated Records: ' . $updatedcount . "\n";
     echo 'Skipped Records: ' . $skippedcount . "\n";
     return true;
 }
	public function now(){
		$request = $this->getPageRequest();
		$view = $this->getView();

		if(!$request->isJSON()) return View::ERROR_BADREQUEST;
		$view->contenttype = View::CTYPE_JSON;
		$view->mode = View::MODE_AJAX;
		$view->record = false;

		$limit = $this->_getQueryLimit();
		$duration = 60;
		// Use FindRaw because it's faster than using full Models for everything, especially when dealing with 20k + records.
		$listings = UserActivityModel::FindRaw('datetime >= ' . (Time::GetCurrent() - $duration), $limit, 'datetime DESC');


		$data = array();

		// Performance reports
		$data['information'] = array(
			'duration' => $duration,
		);
		$data['performance'] = array('get' => 0, 'post' => 0);
		$data['requests'] = array('get' => 0, 'post' => 0);
		$users = array();
		$bots = array();

		$guestname = \ConfigHandler::Get('/user/displayname/anonymous');

		foreach($listings as $log){
			if($log['type'] == 'GET'){
				$data['performance']['get'] += $log['processing_time'];
				$data['requests']['get']++;
			}
			elseif($log['type'] == 'POST'){
				$data['performance']['post'] += $log['processing_time'];
				$data['requests']['post']++;
			}

			$ua = new \Core\UserAgent($log['useragent']);
			
			if(class_exists('\\geocode\\IPLookup')){
				// If the geo library is available, use that to resolve the IP to something more meaningful than just a number.
				$lookup = new \geocode\IPLookup($log['ip_addr']);

				$file = \Core\Filestore\Factory::File('assets/images/iso-country-flags/' . strtolower($lookup->country) . '.png');
				
				if($lookup->province && $lookup->city){
					$title = $lookup->city . ', ' . $lookup->province . ', ' . $lookup->getCountryName();
				}
				elseif($lookup->province){
					$title = $lookup->province . ', ' . $lookup->getCountryName();
				}
				elseif($lookup->city){
					$title = $lookup->city . ' ' . $lookup->getCountryName();
				}
				else{
					$title =  $lookup->getCountryName();
				}

				$ip = '<span title="' . $title . '">';
				if($file->exists()){
					$ip .= '<img src="' . $file->getPreviewURL('20x20') . '" alt="' . $lookup->country . '"/> ';
				}
				$ip .= $log['ip_addr'];
				$ip .= '</span>';
			}
			else{
				$ip = $log['ip_addr'];
			}

			// Bots have their own data, because, well... they're bots.
			// Damn bots!
			if($ua->isBot()){
				if(!isset($bots[ $log['ip_addr'] ])){
					$bots[ $log['ip_addr'] ] = array(
						'ip'        => $ip,
						'useragent' => $log['useragent'],
						'lastpage'  => $log['request'],
						'status'    => $log['status'],
						//'type'      => $ua->,
						'browser'   => $ua->getAsHTML(),
						'count'     => 1,
					);
				}
				else{
					$bots[$log['ip_addr']]['count']++;
				}
			}
			// The user agent information I want to know on a per-user basis, not a per-click basis.
			else{
				if(!isset($users[ $log['session_id'] ])){
					$thisuser = UserModel::Construct($log['user_id']);

					$users[ $log['session_id'] ] = array(
						'session'   => $log['session_id'],
						'ip'        => $ip,
						'user_id'   => $log['user_id'],
						'username'  => ($thisuser ? $thisuser->getDisplayName() : $guestname),
						'useragent' => $log['useragent'],
						'lastpage'  => $log['request'],
						//'type'      => $ua->type,
						'browser'   => $ua->getAsHTML(),
						'os'        => '',
						'count'     => 1,
					);
				}
				else{
					$users[$log['session_id']]['count'] ++;
				}
			}
		}
//UserAgent::Test(); die();
		if($data['requests']['get'] > 0) $data['performance']['get'] = round($data['performance']['get'] /  $data['requests']['get'], 2);
		if($data['requests']['post'] > 0) $data['performance']['post'] = $data['performance']['post'] /  $data['requests']['post'];

		$data['users'] = array_values($users);
		$data['bots'] = array_values($bots);

		//var_dump($data, $users, $listings); die();
		$view->jsondata = $data;
	}
 /**
  * Get the repository XML as a string that can be returned to the browser or cached for future use.
  *
  * @return string
  */
 private function _getRepoXML()
 {
     $repo = new RepoXML();
     $repo->setDescription(ConfigHandler::Get('/package_repository/description'));
     $dir = Factory::Directory(\ConfigHandler::Get('/package_repository/base_directory'));
     $coredir = $dir->getPath() . 'core/';
     $componentdir = $dir->getPath() . 'components/';
     $themedir = $dir->getPath() . 'themes/';
     $tmpdir = Factory::Directory('tmp/exports/');
     $gpg = new Core\GPG\GPG();
     $keysfound = [];
     $private = ConfigHandler::Get('/package_repository/is_private') || strpos($dir->getPath(), ROOT_PDIR) !== 0;
     $addedpackages = 0;
     $failedpackages = 0;
     $iterator = new \Core\Filestore\DirectoryIterator($dir);
     // Only find signed packages.
     $iterator->findExtensions = ['asc'];
     // Recurse into sub directories
     $iterator->recursive = true;
     // No directories
     $iterator->findDirectories = false;
     // Just files
     $iterator->findFiles = true;
     // And sort them by their filename to make things easy.
     $iterator->sortBy('filename');
     // Ensure that the necessary temp directory exists.
     $tmpdir->mkdir();
     foreach ($iterator as $file) {
         /** @var \Core\Filestore\File $file */
         $fullpath = $file->getFilename();
         // Used in the XML file.
         if ($private) {
             $relpath = \Core\resolve_link('/packagerepository/download?file=' . substr($file->getFilename(), strlen($dir->getPath())));
         } else {
             $relpath = $file->getFilename(ROOT_PDIR);
         }
         // Drop the .asc extension.
         $basename = $file->getBasename(true);
         // Tarball of the temporary package
         $tgz = Factory::File($tmpdir->getPath() . $basename);
         $output = [];
         // I need to 1) retrieve and 2) verify the key for this package.
         try {
             $signature = $gpg->verifyFileSignature($fullpath);
             if (!in_array($signature->keyID, $keysfound)) {
                 $repo->addKey($signature->keyID, null, null);
                 $keysfound[] = $signature->keyID;
             }
         } catch (\Exception $e) {
             trigger_error($fullpath . ' was not able to be verified as authentic, (probably because the GPG public key was not available)');
             $failedpackages++;
             continue;
         }
         // decode and untar it in a temp directory to get the package.xml file.
         exec('gpg --homedir "' . GPG_HOMEDIR . '" -q -d "' . $fullpath . '" > "' . $tgz->getFilename() . '" 2>/dev/null', $output, $ret);
         if ($ret) {
             trigger_error('Decryption of file ' . $fullpath . ' failed!');
             $failedpackages++;
             continue;
         }
         exec('tar -xzf "' . $tgz->getFilename() . '" -C "' . $tmpdir->getPath() . '" ./package.xml', $output, $ret);
         if ($ret) {
             trigger_error('Unable to extract package.xml from' . $tgz->getFilename());
             unlink($tmpdir->getPath() . $basename);
             $failedpackages++;
             continue;
         }
         // Read in that package file and append it to the repo xml.
         $package = new PackageXML($tmpdir->getPath() . 'package.xml');
         $package->getRootDOM()->setAttribute('key', $signature->keyID);
         $package->setFileLocation($relpath);
         $repo->addPackage($package);
         $addedpackages++;
         // But I can still cleanup!
         unlink($tmpdir->getPath() . 'package.xml');
         $tgz->delete();
     }
     return $repo->asPrettyXML();
 }
示例#29
0
			$output .= $keyData['key'] . " = \"\";\n";
		}
		$output .= "\n";
	}
	
	// Now generate any dialect-specific keys.
	foreach($baseData['dialects'] as $dialect){
		$output .= "; Dialect-specific overrides for " . $dialect . "\n[" . $dialect . "]\n";
		foreach($matches as $m){
			$keyData = \Core\i18n\I18NLoader::Get($m, $dialect);
			if($keyData['found'] && $keyData['match_key'] == $dialect){
				// This specific dialect has an override.
				$output .= $keyData['key'] . " = \"" . str_replace('"', '\\"', $keyData['match_str']) . "\";\n";
			}
		}
		$output .= "\n";
	}

	if($arguments->getArgumentValue('dry-run')){
		echo $output;
	}
	else{
		// Write this output to the requested ini file!
		$file = \Core\Filestore\Factory::File($dir . '/i18n/' . $lang . '.ini');
		$file->putContents($output);

		\Core\CLI\CLI::PrintSuccess('Updated ' . $file->getFilename() . ' successfully!');
	}
}

示例#30
0
/**
 * Render markup for an image tag in smarty
 *
 * The {img} smarty function is the recommended way to load images in templates from asset or public directories.
 * In addition to automatically resolving URLs, it can also handle server-side resizing and a few other nifty features.
 *
 * #### Image Types &amp; Animations
 *
 * As of Core 3.3.0, image types are preserved, so if a .jpg is requested, an image/jpeg is returned.
 * This changed from the previous behaviour where all images were converted to a PNG.
 *
 * Supported image types are `.jp[e]g`, `.png`, and `.gif`.
 *
 * If an animated gif is resized, the server will attempt to preserve the animation on the resized image.
 * This is done via imagemagick, (so that library needs to be present on the server in order for this trick to work).
 *
 * #### SEO Data
 *
 * As of Core 3.2.0, alt tags are automatically added to every image that does not have the alt attribute explicitly set.
 * This alt name is pulled from the filename of the image, with automatic capitalization and '_' => (space) converting.
 *
 * #### Smarty Parameters
 *
 *  * assign
 *    * string
 *    * Set to a string to assign that variable name instead of returning the output.
 *  * dimensions
 *    * Provide both width and height in pixels, along with special instructions
 *    * Structure is "widthxheight" with no spaces between the "x" and the two integers.
 *    * Special modes available are:
 *    * Carat "`^`" at the end of the string fits the smallest dimension instead of the largest.
 *    * Exclamation mark "`!`" at the end forces size regardless of aspect ratio.
 *    * Greater than "`>`" at the end will only increase image sizes.
 *    * Less than "`<`" at the end will only decrease image sizes.
 *  * file
 *    * \Core\Filestore\File
 *    * File object passed in to display
 *    * Either "file" or "src" is required.
 *  * height
 *    * int
 *    * Maximum image height (in pixels).
 *    * If both width and height are provided, the image will be constrained to both without any distortion.
 *  * inline
 *    * int "1" or "0", (default "0")
 *    * New in Core 4.2.0
 *    * Request that the resized image be encoded as base64 and inserted inline in the markdown instead of returned as the URL.
 *  * placeholder
 *    * string
 *    * placeholder image if the requested image is blank or not found.  Useful for optional fields that should still display something.
 *    * Current values: "building", "generic", "person", "person-tall", "person-wide", "photo"
 *  * src
 *    * string
 *    * Source filename to display.  This can start with "assets" for an asset, or "public" for a public file.
 *    * Either "file" or "src" is required.
 *  * width
 *    * int
 *    * Maximum image width (in pixels).
 *    * If both width and height are provided, the image will be constrained to both without any distortion.
 *
 * Any other parameter is transparently sent to the resulting `<img/>` tag.
 *
 *
 * #### Example Usage
 *
 * <pre>
 * {img src="public/gallery/photo123.png" width="123" height="123" placeholder="photo" alt="My photo 123"}
 * </pre>
 *
 * @param array  $params  Associative (and/or indexed) array of smarty parameters passed in from the template
 * @param Smarty $smarty  Parent Smarty template object
 *
 * @return string
 * @throws SmartyException
 */
function smarty_function_img($params, $smarty){

	// Key/value array of attributes for the resulting HTML.
	$attributes = array();

	if(isset($params['file'])){
		$f = $params['file'];
		if(!$f instanceof \Core\Filestore\File){
			throw new SmartyException('{img} tag expects a \Core\Filestore\File object for the "file" parameter.');
		}
		unset($params['file']);
	}
	elseif(isset($params['src'])){
		$f = \Core\Filestore\Factory::File($params['src']);
		unset($params['src']);
	}
	else{
		$f = null;
	}

	// Some optional parameters, (and their defaults)
	$assign = $width = $height = $dimensions = $inline = false;
	$placeholder = $previewfile = null;

	if(isset($params['assign'])){
		$assign = $params['assign'];
		unset($params['assign']);
	}

	if(isset($params['width'])){
		$width = $params['width'];
		unset($params['width']);
	}

	if(isset($params['height'])){
		$height = $params['height'];
		unset($params['height']);
	}

	if(isset($params['dimensions'])){
		$dimensions = $params['dimensions'];
		$width = preg_replace('#[^0-9]?([0-9]*)x.*#', '$1', $dimensions);
		$height = preg_replace('#.*x([0-9]*)[^0-9]?#', '$1', $dimensions);
		unset($params['dimensions']);
	}

	if(isset($params['placeholder'])){
		$placeholder = $params['placeholder'];
		unset($params['placeholder']);
	}

	if(isset($params['inline'])){
		$inline = ($params['inline'] == '1');
		unset($params['inline']);
	}


	if($dimensions){
		// Passing in dimensions raw will allow the user more control over the size of the images.
		$d = $dimensions;
	}
	else{
		// If one is provided but not the other, just make them the same.
		if($width && !$height) $height = $width;
		if($height && !$width) $width = $height;

		$d = ($width && $height) ? $width . 'x' . $height : false;
	}

	// If the file doesn't exist and a placeholder was provided, use the appropriate placeholder image!
	if(!($f && $f->exists() && $f->isImage()) && $placeholder){
		// Try that!
		$f = \Core\Filestore\Factory::File('assets/images/placeholders/' . $placeholder . '.png');
	}

	if(!$f){
		throw new SmartyException('{img} tag requires either "src", "file", or a "placeholder" parameter.');
	}

	// Do the rest of the attributes that the user sent in (if there are any)
	foreach($params as $k => $v){
		$attributes[$k] = $v;
	}

	if($f instanceof Core\Filestore\Backends\FileRemote){
		// Erm... Give the original URL with the dimension requests.
		$attributes['src'] = $f->getURL();
		if($width) $attributes['width'] = $width;
		if($height) $attributes['height'] = $height;
	}
	else{
		// Try to lookup the preview file.
		// if it exists, then YAY... I can return that direct resource.
		// otherwise, I should check and see if the file is larger than a set filesize.
		// if it is, then I want to return a link to a controller to render that file instead of rendering the file from within the {img} tag.
		//
		// This is useful because any logic contained within this block will halt page execution!
		// To improve the perception of performance, that can be offloaded to the browser requesting the <img/> contents.
		$previewfile = $d ? $f->getQuickPreviewFile($d) : $f;

		if(!$previewfile){
			$attributes['src'] = '#';
			$attributes['title'] = 'No preview files available!';
		}
		elseif($inline && $f->getFilesize() < 1048576*4){
			// Overwrite the src attribute with the base64 contents.
			// This can only happen after the preview file exists!

			if(!$previewfile->exists()){
				// Since quick ran, ensure that it's actually resized!
				$previewfile = $f->getPreviewFile($d);
			}
			$attributes['src'] = 'data:' . $previewfile->getMimetype() . ';base64,' . base64_encode($previewfile->getContents());
		}
		elseif(!$previewfile->exists()){
			// Ok, it doesn't exist... return a link to the controller to render this file.
			$attributes['src'] = \Core\resolve_link('/file/preview') . '?f=' . $f->getFilenameHash() . '&d=' . $d;
		}
		else{
			$attributes['src'] = $previewfile->getURL();
		}
	}

	// All images need alt data!
	if(!isset($attributes['alt'])){
		$attributes['alt'] = $f->getTitle();
	}

	// Merge them back together in one string.
	$html = '<img';
	foreach($attributes as $k => $v) $html .= " $k=\"$v\"";
	$html .= '/>';

	// If the extended metadata was requested... look that up too!
	if(isset($params['includemeta']) && $params['includemeta']){

		$metahelper  = new \Core\Filestore\FileMetaHelper($f);
		$metacontent = $metahelper->getAsHTML();
		if($metacontent){
			$html = '<div class="image-metadata-wrapper">' . $html . $metacontent . '</div>';
		}
	}

	return $assign ? $smarty->assign($assign, $html) : $html;
}