Exemple #1
0
	/**
	 * Uncompress this file contents and return the result.
	 * Obviously, if a multi-gigibyte file is read with no immediate destination,
	 * you'll probably run out of memory.
	 *
	 * @param Filestore\File|bool $dst The destination to write the uncompressed data to
	 *        If not provided, just returns the data.
	 *
	 * @return mixed
	 */
	public function uncompress($dst = false) {
		$zd = gzopen($this->_file->getLocalFilename(), "r");
		if (!$zd) return false;

		$contents = '';
		while (!feof($zd)) {
			$contents .= gzread($zd, 2048);
		}
		gzclose($zd);

		if($dst){
			$dst->putContents($contents);
		}
		else{
			return $contents;
		}
	}
	/**
	 * Get the remote file for this update site
	 *
	 * @return \Core\Filestore\File
	 */
	public function getFile(){
		if($this->_remotefile === null){
			$this->_remotefile           = new \Core\Filestore\Backends\FileRemote();
			$this->_remotefile->password = $this->get('password');
			$this->_remotefile->username = $this->get('username');
			
			// Set the license information if set.
			if(defined('SERVER_ID') && strlen(SERVER_ID) == 32){
				$this->_remotefile->setRequestHeader('X-Core-Server-ID', SERVER_ID);
			}
			$this->_remotefile->setFilename($this->get('url') . '/repo.xml.gz');
		}

		return $this->_remotefile;
	}
	/**
	 * Setup the internal DOMDocument for usage.
	 *
	 * This MUST be called before any operations are applied to this object!
	 *
	 * @return bool
	 */
	public function load() {
		// I need a filename.
		// Actually I don't........ creating a DOM on-the-fly is a possible use of this class too...... 0.o
		//if(!$this->_filename) return false;

		// I need a root node name.
		if (!$this->_rootname) return false;

		// w00t, new support for a schema declaration!
		if($this->_schema){
			$implementation = new DOMImplementation();
			$dtd = $implementation->createDocumentType($this->_rootname, 'SYSTEM', $this->_schema);
			$this->_DOM = $implementation->createDocument('', '', $dtd);
		}
		else{
			$this->_DOM = new DOMDocument();
		}

		$this->_DOM->encoding = 'UTF-8';

		// we want a nice output
		$this->_DOM->formatOutput = true;

		if ($this->_file) {
			$contents = $this->_file->getContentsObject();
			if (is_a($contents, '\Core\Filestore\Contents\ContentGZ')) {
				$dat = $contents->uncompress();
			}
			else {
				$dat = $contents->getContents();
			}

			// If an empty string is submitted...
			if(!$dat){
				return false;
			}

			$this->_DOM->loadXML($dat);
		}
		elseif ($this->_filename) {
			if (!$this->_DOM->load($this->_filename)) return false;
		}
		else {
			return false;
		}

		return true;
	}
Exemple #4
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;
		}
	}
Exemple #5
0
	/**
	 * Make a copy of a source Filestore\File into this Filestore\File.
	 *
	 * (Generally only useful internally)
	 *
	 * @param Filestore\File $src       Source file backend
	 * @param bool $overwrite true to overwrite existing file
	 *
	 * @throws \Exception
	 * @return bool True or False if succeeded.
	 */
	public function copyFrom(Filestore\File $src, $overwrite = false) {
		// Don't overwrite existing files unless told otherwise...
		if (!$overwrite) {
			$c    = 0;
			$ext  = $this->getExtension();
			$base = $this->getBaseFilename(true);
			$dir  = dirname($this->_filename);
			$prefix = $dir . '/' . $base;
			$suffix = (($ext == '') ? '' : '.' . $ext);
			$thathash = $src->getHash();

			$f = $prefix . $suffix;
			while(file_exists($f) && md5_file($f) != $thathash){
				$f = $prefix . ' (' . ++$c . ')' . $suffix;
			}

			$this->_filename = $f;
		}

		// And do the actual copy!
		// To save memory, try to use as low-level functions as possible.
		$localfilename = $src->getLocalFilename();
		$localhash     = $src->getHash();
		$localmodified = $src->getMTime();
		$localsize     = $src->getFilesize();

		// Resolve it from its default.
		// This is provided from a config define, (probably).
		$mode = (defined('DEFAULT_FILE_PERMS') ? DEFAULT_FILE_PERMS : 0644);

		// Make sure the directory exists first!
		$this->_mkdir(dirname($this->_filename), null, true);

		// FTP requires a filename, not data...
		// WELL how bout that!  I happen to have a local filename ;)
		if (!ftp_put($this->_ftp->getConn(), $this->_filename, $localfilename, FTP_BINARY)) {
			throw new \Exception(error_get_last()['message']);
		}

		if (!ftp_chmod($this->_ftp->getConn(), $mode, $this->_filename)){
			throw new \Exception(error_get_last()['message']);
		}

		// Don't forget to save the metadata for this file!
		$filename = $this->getFilename();
		$this->_ftp->setFileHash($filename, $localhash);
		$this->_ftp->setFileModified($filename, $localmodified);
		$this->_ftp->setFileSize($filename, $localsize);

		// woot...
		return true;
	}
Exemple #6
0
	/**
	 * Verify the signature on a given file
	 *
	 * If only one argument is provided, it is expected that file contains both the file and signature as an attached sig.
	 *
	 * If two arguments are provided, the detached signature is the first argument and the content to verify is the second.
	 *
	 * @throws \Exception
	 *
	 * @param string|\Core\Filestore\File $file       Filename or File object of the file to verify
	 * @param string|\Core\Filestore\File $verifyFile Filename or File object of any detached signature
	 *
	 * @return Signature
	 */
	public function verifyFileSignature($file, $verifyFile = null){
		if($file instanceof \Core\Filestore\File){
			$filename = $file->getFilename();
		}
		else{
			$filename = $file;
		}

		if(!file_exists($filename)){
			throw new \Exception('Requested file does not exist, unable to verify signature!');
		}

		if($verifyFile === null){
			// Standard attached sig
			$result = $this->_exec('--with-fingerprint --batch --no-tty --verify ' . escapeshellarg($filename));
		}
		else{
			// Detached signature
			if($verifyFile instanceof \Core\Filestore\File){
				$sourceFilename = $verifyFile->getFilename();
			}
			else{
				$sourceFilename = $verifyFile;
			}

			$result = $this->_exec('--with-fingerprint --batch --no-tty --verify ' . escapeshellarg($filename) . ' ' . escapeshellarg($sourceFilename));
		}


		// If the result failed, then nothing else to do here.
		if($result['return'] !== 0){
			throw new \Exception($result['error']);
		}

		// Else, the calling script may want to know the results of the verification, eg: the key and date.
		// The metadata here is send to STDERR.  _Shrugs_
		$sig = new Signature();
		$sig->_parseOutputText($result['error']);
		return $sig;
	}
	/**
	 * Add a file as an attachment!
	 *
	 * @param \Core\Filestore\File $file
	 *
	 * @throws phpmailerException
	 */
	public function addAttachment(\Core\Filestore\File $file){
		$this->getMailer()->AddAttachment(
			$file->getFilename(), // Full Path
			$file->getBasename(), // Base Filename (to be exposed in client)
			'base64', // Yup, just do this
			$file->getMimetype() // Mimetype, try to use correct hinting for client
		);
	}
Exemple #8
0
	/**
	 * Resize this image and save the output as another File object.
	 *
	 * This is used on conjunction with getPreview* and getQuickPreview.
	 * QuickPreview creates the destination file in the correct directory
	 * and getPreview* methods request the actual resizing.
	 *
	 * @param Filestore\File $file   The destination file
	 * @param int            $width  Width of the final image (in px)
	 * @param int            $height Height of the final image (in px)
	 * @param string         $mode   Mode (part of the geometry)
	 */
	private function _resizeTo(Filestore\File $file, $width, $height, $mode){

		if(!$this->isImage()){
			// :/
			return;
		}

		\Core\Utilities\Logger\write_debug('Resizing image ' . $this->getFilename('') . ' to ' . $width . 'x' . $height . $mode);

		$m = $this->getMimetype();

		// Make sure the directory of the destination file exists!
		// By touching the file, Core will create all parent directories as necessary.
		$file->putContents('');

		if($m == 'image/gif' && exec('which convert 2>/dev/null')){
			// The GIF resizer handles EVERYTHING :)
			// Granted of course, that imagemagick's convert is available on the server.
			$resize = escapeshellarg($mode . $width . 'x' . $height);
			exec('convert ' . escapeshellarg($this->getFilename()) . ' -resize ' . $resize . ' ' . escapeshellarg($file->getFilename()));

			\Core\Utilities\Logger\write_debug('Resizing complete (via convert)');
			return;
		}

		// Traditional resizing logic.
		switch ($m) {
			case 'image/jpeg':
				$thumbType = 'JPEG';
				$thumbWidth = $width;
				$thumbHeight = $height;
				if($width <= 200 && $height <= 200 && function_exists('exif_thumbnail')){
					// Try to write out from the thumbnail img instead of the full size.
					// This is done to increase server performance.
					// eg: resizing a 5MB JPEG can take upwards of 50-100ms,
					// whereas the embedded thumbnail will take only 2-10ms.
					// Not to mention professional JPEG management tools such as PS and Gimp
					// produce marginally higher-quality thumbnails than GD will.
					// (The resulting filesize is negligible.)
					// Of course if the requested image is larger than a thumbnail size, (200x200 in this case),
					// using the thumbnail is counter-productive!
					$img = exif_thumbnail($this->getFilename(), $thumbWidth, $thumbHeight, $thumbType);
					if($img){
						\Core\Utilities\Logger\write_debug('JPEG has thumbnail data of ' . $thumbWidth . 'x' . $thumbHeight . '!');
						$file->putContents($img);
						$img = imagecreatefromjpeg($file->getFilename());
					}
					else{
						$img = imagecreatefromjpeg($this->getFilename());
					}
				}
				else{
					$img = imagecreatefromjpeg($this->getFilename());
				}

				break;
			case 'image/png':
				$img = imagecreatefrompng($this->getFilename());
				break;
			case 'image/gif':
				$img = imagecreatefromgif($this->getFilename());
				break;
			default:
				// Hmmm...
				\Core\Utilities\Logger\write_debug('Resizing complete (failed, not sure what it was)');
				return;
		}
		if ($img) {
			$sW = imagesx($img);
			$sH = imagesy($img);

			$nW = $sW;
			$nH = $sH;


			switch($mode){
				// Standard mode, images are scaled down (only) while preserving aspect ratio
				case '':
				case '<':
					if ($nW > $width) {
						$nH = $width * $sH / $sW;
						$nW = $width;
					}

					if ($nH > $height) {
						$nW = $height * $sW / $sH;
						$nH = $height;
					}
					break;
				// Only resize up
				case '>':
					if ($nW < $width) {
						$nH = $width * $sH / $sW;
						$nW = $width;
					}

					if ($nH < $height) {
						$nW = $height * $sW / $sH;
						$nH = $height;
					}
					break;
				// Resize to new size, regardless about aspect ratio
				case '!':
					$nW = $width;
					$nH = $height;
					break;
				// Resize image based on smallest dimension
				case '^':
					$ratioheight = $sW / $height;
					$ratiowidth  = $sH / $width;

					if($ratioheight > 1 && $ratiowidth > 1){
						// The image is larger than any of the dimensions, I can use the reduction logic.
						if(($width * $sH / $sW) > ($height * $sW / $sH)){
							$nH = $width * $sH / $sW;
							$nW = $width;
						}
						else{
							$nH = $height;
							$nW = $height * $sW / $sH;
						}
					}
					elseif($ratiowidth > $ratioheight){
						// The image needs to be increased in size, this logic is slightly different.
						$nW = $width;
						$nH = round($width * $sH / $sW);
					}
					else{
						$nH = $height;
						$nW = round($height * $sW / $sH);
					}
			}

			// If it's a JPEG, try to find the original thumbnail.
			/*if(false && $m == 'image/jpeg'){
				$type = 'JPEG';
				$img = exif_thumbnail($this->getFilename(), $nW, $nH, $type);
				$file->putContents($img);
				return;
			}*/

			$img2 = imagecreatetruecolor($nW, $nH);
			imagealphablending($img2, false);
			imagesavealpha($img2, true);
			imagealphablending($img, true);
			// Assign a transparency color.
			//$trans = imagecolorallocatealpha($img2, 0, 0, 0, 0);
			//imagefill($img2, 0, 0, $trans);
			imagecopyresampled($img2, $img, 0, 0, 0, 0, $nW, $nH, $sW, $sH);
			imagedestroy($img);


			switch ($m) {
				case 'image/jpeg':
					imagejpeg($img2, $file->getFilename(), 60);
					\Core\Utilities\Logger\write_debug('Resizing complete (via imagejpeg)');
					break;
				case 'image/png':
					imagepng($img2, $file->getFilename(), 9);
					\Core\Utilities\Logger\write_debug('Resizing complete (via imagepng)');
					break;
				case 'image/gif':
					imagegif($img2, $file->getFilename());
					\Core\Utilities\Logger\write_debug('Resizing complete (via imagegif)');
					break;
				default:
					// Hmmm...
					\Core\Utilities\Logger\write_debug('Resizing complete (failed, not sure what it was)');
					return;
			}
		}
	}
Exemple #9
0
/**
 * Get an array of the various resize components from a given dimension set.
 * These include: width, height, mode, key.
 *
 * @param string|int $dimensions
 * @param File $file
 *
 * @return array
 */
function get_resized_key_components($dimensions, $file){
	// The legacy support for simply a number.
	if (is_numeric($dimensions)) {
		$width  = $dimensions;
		$height = $dimensions;
		$mode = '';
	}
	elseif ($dimensions === null) {
		$width  = 300;
		$height = 300;
		$mode = '';
	}
	elseif($dimensions === false){
		$width = false;
		$height = false;
		$mode = '';
	}
	else {
		// Allow some special modifiers.
		if(strpos($dimensions, '^') !== false){
			// Fit the smallest dimension instead of the largest, (useful for overflow tricks)
			$mode = '^';
			$dimensions = str_replace('^', '', $dimensions);
		}
		elseif(strpos($dimensions, '!') !== false){
			// Absolutely resize, regardless of aspect ratio
			$mode = '!';
			$dimensions = str_replace('!', '', $dimensions);
		}
		elseif(strpos($dimensions, '>') !== false){
			// Only increase images.
			$mode = '>';
			$dimensions = str_replace('>', '', $dimensions);
		}
		elseif(strpos($dimensions, '<') !== false){
			// Only decrease images.
			$mode = '<';
			$dimensions = str_replace('<', '', $dimensions);
		}
		else{
			// Default mode
			$mode = '';
		}
		// New method. Split on the "x" and that should give me the width/height.
		$vals   = explode('x', strtolower($dimensions));
		$width  = (int)$vals[0];
		$height = (int)$vals[1];
	}

	$ext = $file->getExtension();
	// Ensure that an extension is used if none present, (may happen with temporary files).
	if(!$ext){
		$ext = mimetype_to_extension($file->getMimetype());
	}

	// The basename is for SEO purposes, that way even resized images still contain the filename.
	// The hash is just to ensure that no two files conflict, ie: /public/a/file1.png and /public/b/file1.png
	//  might conflict without this hash.
	// Finally, the width and height dimensions are there just because as well; it gives more of a human
	//  touch to the file. :p
	// Also, keep the original file extension, this way PNGs remain PNGs, GIFs remain GIFs, JPEGs remain JPEGs.
	// This is critical particularly when it comes to animated GIFs.
	$key = str_replace(' ', '-', $file->getBasename(true)) . '-' . $file->getHash() . '-' . $width . 'x' . $height . $mode . '.' . $ext;

	// The directory can be used with the new File backend to create this file in a correctly nested subdirectory.
	$dir = dirname($file->getFilename(false)) . '/';

	if(substr($dir, 0, 7) == 'public/'){
		// Replace the necessary prefix with a more useful one.
		// Anything within public/ needs to be remapped to public/tmp
		$dir = 'public/tmp/' . substr($dir, 7);
	}
	else{
		// Everything else gets prepended to public/tmp/
		// so if the original file is in themes/blah/imgs/blah.jpg,
		// it will be copied to public/tmp/blah.jpg
		$dir = 'public/tmp/';
	}

	return array(
		'width'  => $width,
		'height' => $height,
		'mode'   => $mode,
		'key'    => $key,
		'ext'    => $ext,
		'dir'    => $dir,
	);
}
	/**
	 * Save this component metadata back to its XML file.
	 * Useful in packager scripts.
	 */
	public function save($minified = false) {
		// Set the schema version to the newest API version.
		$this->_xmlloader->setSchema('http://corepl.us/api/2_4/component.dtd');
		// Ensure there's a required namespace on the root node.
		$this->_xmlloader->getRootDOM()->setAttribute('xmlns:xsi', "http://www.w3.org/2001/XMLSchema-instance");

		// Hack
		// If there is an empty smartydir set, don't let that get saved.
		if(!$this->getSmartyPluginDirectory()){
			$this->_xmlloader->removeElements('//smartyplugins');
		}

		/*
		///////////////  Handle the hard-set pages, ie: admin ones \\\\\\\\\\\\\
		if(!isset($viewclasses)) $viewclasses = array();
		foreach($viewclasses as $c){
			// Should end in Controller.
			if(strlen($c) - strpos($c, 'Controller') == 10) $c = substr($c, 0, -10);
			$data = Dataset::Init()->table('page')->select('*')->where("baseurl = /$c", 'admin=1', 'fuzzy=0')->execute();
			
			//$rs = DB::Execute("SELECT * FROM " . DB_PREFIX . "page WHERE ( `baseurl` = '/$c' OR `baseurl` LIKE '/$c/%' ) AND `fuzzy` = '0' AND `admin` = '1'");
			foreach($data as $row){
				$node = $this->_xmlloader->getElement('/pages/page[@baseurl="' . $row['baseurl'] . '"]');
				$node->setAttribute('admin', $row['admin']);
				$node->setAttribute('widget', $row['widget']);
				$node->setAttribute('access', $row['access']);
				$node->setAttribute('title', $row['title']);
			}
			
			$data = Dataset::Init()->table('page')->select('*')->where("baseurl LIKE /$c/%", 'admin=1', 'fuzzy=0')->execute();
			
			//$rs = DB::Execute("SELECT * FROM " . DB_PREFIX . "page WHERE ( `baseurl` = '/$c' OR `baseurl` LIKE '/$c/%' ) AND `fuzzy` = '0' AND `admin` = '1'");
			foreach($data as $row){
				$node = $this->_xmlloader->getElement('/pages/page[@baseurl="' . $row['baseurl'] . '"]');
				$node->setAttribute('admin', $row['admin']);
				$node->setAttribute('widget', $row['widget']);
				$node->setAttribute('access', $row['access']);
				$node->setAttribute('title', $row['title']);
			}
		}
		*/

		/*
		///////////////////////  Handle the config options \\\\\\\\\\\\\\\\\\\\\
		$data = Dataset::Init()->table('config')->select('*')->where('key LIKE /' . $this->getName() . '/%')->execute();
		//$rs = DB::Execute("SELECT * FROM " . DB_PREFIX . "config WHERE `key` LIKE '/" . $this->getName() . "/%'");
		foreach($data as $row){
			$node = $this->_xmlloader->getElement('/configs/config[@key="' . $row['key'] . '"]');
			$node->setAttribute('type', $row['type']);
			$node->setAttribute('default', $row['default_value']);
			$node->setAttribute('description', $row['description']);
			
			if($row['options']) $node->setAttribute('options', $row['options']);
			else $node->removeAttribute('options');
		}
		*/

		// This needs to be the final step... write the XML doc back to the file.
		$XMLFilename = $this->_file->getFilename();
		//echo $this->asPrettyXML(); // DEBUG //
		if ($minified) {
			file_put_contents($XMLFilename, $this->_xmlloader->asMinifiedXML());
		}
		else {
			file_put_contents($XMLFilename, $this->_xmlloader->asPrettyXML());
		}
	}