<?php
/**
 * @package    VP Advanced User
 * @author     Abhishek Das <info@virtueplanet.com>
 * @copyright  Copyright (C) 2012-2019 VirtuePlanet Services LLP. All rights reserved.
 * @license    GNU General Public License version 2 or later; see LICENSE.txt
 * @link       https://www.virtueplanet.com
 */

defined('_JEXEC') or die;

use Joomla\Registry\Registry;

// Register utility helper
JLoader::register('VPAdvancedUserHelperUtility', JPATH_SITE . '/components/com_vpadvanceduser/helpers/utility.php');

/**
 * User JSON controller class.
 */
class VPAdvancedUserControllerAjax extends JControllerLegacy
{
	public function getArticle()
	{
		JModelLegacy::addIncludePath(JPATH_SITE . '/components/com_content/models');
		
		$id      = $this->input->getInt('id', 0);
		$model   = JModelLegacy::getInstance('Article', 'ContentModel', array('ignore_request' => true));
		$content = JText::_('JERROR_PAGE_NOT_FOUND');
		$error   = true;
		
		if($id > 0)
		{
			$model->setState('article.id', $id);

			// Load the parameters.
			$params = JComponentHelper::getParams('com_content');
			$model->setState('params', $params);

			// TODO: Tune these values based on other permissions.
			$user = JFactory::getUser();

			if ((!$user->authorise('core.edit.state', 'com_content')) && (!$user->authorise('core.edit', 'com_content')))
			{
				$model->setState('filter.published', 1);
				$model->setState('filter.archived', 2);
			}

			$model->setState('filter.language', 0);
			
			$article = $model->getItem($id);
			
			if(!empty($article))
			{
				// Load associated content items
				$assoc = JLanguageAssociations::isEnabled();
				$multiLang = JLanguageMultilang::isEnabled();

				if($assoc && $multiLang)
				{
					$article->associations = array();

					$associations = JLanguageAssociations::getAssociations('com_content', '#__content', 'com_content.item', $article->id);

					foreach ($associations as $tag => $association)
					{
						$article->associations[$tag] = $association->id;
					}
					
					$langTag = JFactory::getLanguage()->getTag();
					
					if(!empty($article->associations) && isset($article->associations[$langTag]))
					{
						$aid = $article->associations[$langTag];
						
						$model->setState('article.id', $aid);
						
						$articleByLang = $model->getItem($aid);

						if(!empty($articleByLang))
						{
							$article = $articleByLang;
						}
					}
				}
				
				$content = trim($article->fulltext) != '' ? $article->introtext . $article->fulltext : $article->introtext;
				$content = $this->renderArticle($content);

				$error = false;
			}
		}
		
		return VPAdvancedUserHelperUtility::jsonResponse(array('error' => $error, 'content' => $content));
	}

	/**
	 * Convert the site URL to fit to the HTTP request.
	 *
	 * @return  void
	 */
	protected function renderArticle($buffer)
	{
		$app = JFactory::getApplication();

		// Replace src links.
		$base = JUri::base(true) . '/';

		// Replace index.php URI by SEF URI.
		if (strpos($buffer, 'href="index.php?') !== false)
		{
			preg_match_all('#href="index.php\?([^"]+)"#m', $buffer, $matches);

			foreach ($matches[1] as $urlQueryString)
			{
				$buffer = str_replace(
					'href="index.php?' . $urlQueryString . '"',
					'href="' . JRoute::_('index.php?' . $urlQueryString) . '"',
					$buffer
				);
			}

			$this->checkBuffer($buffer);
		}

		// Check for all unknown protocals (a protocol must contain at least one alpahnumeric character followed by a ":").
		$protocols  = '[a-zA-Z0-9\-]+:';
		$attributes = array('href=', 'src=', 'poster=');

		foreach ($attributes as $attribute)
		{
			if (strpos($buffer, $attribute) !== false)
			{
				$regex  = '#\s' . $attribute . '"(?!/|' . $protocols . '|\#|\')([^"]*)"#m';
				$buffer = preg_replace($regex, ' ' . $attribute . '"' . $base . '$1"', $buffer);
				$this->checkBuffer($buffer);
			}
		}

		if (strpos($buffer, 'srcset=') !== false)
		{
			$regex = '#\s+srcset="([^"]+)"#m';

			$buffer = preg_replace_callback(
				$regex,
				function ($match) use ($base, $protocols)
				{
					preg_match_all('#(?:[^\s]+)\s*(?:[\d\.]+[wx])?(?:\,\s*)?#i', $match[1], $matches);

					foreach ($matches[0] as &$src)
					{
						$src = preg_replace('#^(?!/|' . $protocols . '|\#|\')(.+)#', $base . '$1', $src);
					}

					return ' srcset="' . implode($matches[0]) . '"';
				},
				$buffer
			);

			$this->checkBuffer($buffer);
		}

		// Replace all unknown protocals in javascript window open events.
		if (strpos($buffer, 'window.open(') !== false)
		{
			$regex  = '#onclick="window.open\(\'(?!/|' . $protocols . '|\#)([^/]+[^\']*?\')#m';
			$buffer = preg_replace($regex, 'onclick="window.open(\'' . $base . '$1', $buffer);
			$this->checkBuffer($buffer);
		}

		// Replace all unknown protocols in onmouseover and onmouseout attributes.
		$attributes = array('onmouseover=', 'onmouseout=');

		foreach ($attributes as $attribute)
		{
			if (strpos($buffer, $attribute) !== false)
			{
				$regex  = '#' . $attribute . '"this.src=([\']+)(?!/|' . $protocols . '|\#|\')([^"]+)"#m';
				$buffer = preg_replace($regex, $attribute . '"this.src=$1' . $base . '$2"', $buffer);
				$this->checkBuffer($buffer);
			}
		}

		// Replace all unknown protocols in CSS background image.
		if (strpos($buffer, 'style=') !== false)
		{
			$regex_url  = '\s*url\s*\(([\'\"]|\&\#0?3[49];)?(?!/|\&\#0?3[49];|' . $protocols . '|\#)([^\)\'\"]+)([\'\"]|\&\#0?3[49];)?\)';
			$regex  = '#style=\s*([\'\"])(.*):' . $regex_url . '#m';
			$buffer = preg_replace($regex, 'style=$1$2: url($3' . $base . '$4$5)', $buffer);
			$this->checkBuffer($buffer);
		}

		// Replace all unknown protocols in OBJECT param tag.
		if (strpos($buffer, '<param') !== false)
		{
			// OBJECT <param name="xx", value="yy"> -- fix it only inside the <param> tag.
			$regex  = '#(<param\s+)name\s*=\s*"(movie|src|url)"[^>]\s*value\s*=\s*"(?!/|' . $protocols . '|\#|\')([^"]*)"#m';
			$buffer = preg_replace($regex, '$1name="$2" value="' . $base . '$3"', $buffer);
			$this->checkBuffer($buffer);

			// OBJECT <param value="xx", name="yy"> -- fix it only inside the <param> tag.
			$regex  = '#(<param\s+[^>]*)value\s*=\s*"(?!/|' . $protocols . '|\#|\')([^"]*)"\s*name\s*=\s*"(movie|src|url)"#m';
			$buffer = preg_replace($regex, '<param value="' . $base . '$2" name="$3"', $buffer);
			$this->checkBuffer($buffer);
		}

		// Replace all unknown protocols in OBJECT tag.
		if (strpos($buffer, '<object') !== false)
		{
			$regex  = '#(<object\s+[^>]*)data\s*=\s*"(?!/|' . $protocols . '|\#|\')([^"]*)"#m';
			$buffer = preg_replace($regex, '$1data="' . $base . '$2"', $buffer);
			$this->checkBuffer($buffer);
		}

		return $buffer;
	}

	/**
	 * Check the buffer.
	 *
	 * @param   string  $buffer  Buffer to be checked.
	 *
	 * @return  void
	 */
	protected function checkBuffer($buffer)
	{
		if ($buffer === null)
		{
			switch (preg_last_error())
			{
				case PREG_BACKTRACK_LIMIT_ERROR:
					$message = 'PHP regular expression limit reached (pcre.backtrack_limit)';
					break;
				case PREG_RECURSION_LIMIT_ERROR:
					$message = 'PHP regular expression limit reached (pcre.recursion_limit)';
					break;
				case PREG_BAD_UTF8_ERROR:
					$message = 'Bad UTF8 passed to PCRE function';
					break;
				default:
					$message = 'Unknown PCRE error calling PCRE function';
			}

			return VPAdvancedUserHelperUtility::jsonResponse(array('error' => true, 'content' => $message));
		}
	}
}
