GemsTracker/gemstracker-library

View on GitHub
classes/Gems/Util/SiteUtil.php

Summary

Maintainability
C
1 day
Test Coverage
F
46%
<?php

/**
 *
 * @package    Gems
 * @subpackage Util
 * @author     Matijs de Jong <mjong@magnafacta.nl>
 * @copyright  Copyright (c) 2021, Erasmus MC and MagnaFacta B.V.
 * @license    New BSD License
 */

namespace Gems\Util;

use Gems\Snippets\Ask\RedirectUntilGoodbyeSnippet;

/**
 *
 * @package    Gems
 * @subpackage Util
 * @license    New BSD License
 * @since      Class available since version 1.9.1
 */
class SiteUtil extends UtilAbstract
{
    CONST ORG_SEPARATOR = '|';

    /**
     * @var \MUtil_Registry_Source
     */
    protected $source;

    /**
     * @var \Gems_Util
     */
    protected $util;

    /**
     * @param $host
     * @param $basePath Optional addiitonal basepath
     * @return string Normalized https:// string for a host name
     */
    protected function _hostToUrl($host, $basePath)
    {
        return (\MUtil_Https::on() ? 'https' : 'http') . '://' . $host . $basePath;
    }

    /**
     * Called after the check that all required registry values
     * have been set correctly has run.
     *
     * This function is no needed if the classes are setup correctly
     *
     * @return void
     * /
    public function afterRegistry()
    {
        parent::afterRegistry();

        $this->loadUrlCache();
    } // */

    /**
     * @return \Gems\Util\SiteConsole A console only url
     */
    public function getConsoleUrl()
    {
        $site = new SiteConsole();
        $this->source->applySource($site);
        return $site;
    }

    /**
     * Get the first url for all organizations
     *
     * @return \Gems\Util\SiteUrl|null
     */
    public function getOneForAll()
    {
        try {
            $sql = "SELECT gsi_url FROM gems__sites 
                        WHERE gsi_select_organizations = 0 AND gsi_active = 1 AND gsi_blocked = 0 
                        ORDER BY gsi_order, gsi_id";

            $url = $this->db->fetchOne($sql);

            if ($url) {
                return $this->getSiteForUrl($url);
            }

        } catch (\Zend_Db_Statement_Exception $exc) {
            // Intentional fall through
        }
        return null;
    }

    /**
     * @param $orgId
     * @return string Preferred url for organization
     */
    public function getOrganizationPreferredUrl($orgId)
    {
        try {
            $id = self::ORG_SEPARATOR . intval($orgId) . self::ORG_SEPARATOR;
            $sql = "SELECT gsi_url FROM gems__sites 
                        WHERE (gsi_select_organizations = 0 OR gsi_organizations LIKE '%$orgId%') AND gsi_active = 1 AND gsi_blocked = 0
                        ORDER BY gsi_order, gsi_id";

            return $this->db->fetchOne($sql);
        } catch (\Zend_Db_Statement_Exception $exc) {
            // The old return value
            return $this->util->getCurrentURI();
        }
    }

    /**
     * @param false $blockOnCreation
     * @return \Gems\Util\SiteUrl
     * @throws \Gems_Exception_Coding
     */
    public function getSiteForCurrentUrl($blockOnCreation = false)
    {
        if (\MUtil_Console::isConsole()) {
            $site = new SiteConsole('https://console', false);
            $this->source->applySource($site);
            return $site;

        } elseif (\Zend_Session::$_unitTestEnabled) {
            $url = 'https://test.example.site';
            $blockOnCreation = false;

        } elseif (\Zend_Controller_Front::getInstance()->getResponse() instanceof \Zend_Controller_Request_Abstract) {
            // I found myself trying to do this so here we prefent this the hard way.
            throw new \Gems_Exception_Coding(
                __CLASS__ . '->' . __FUNCTION__ . "() cannot be called before the request object is initialized."
            );

        } else {
            $url = $this->util->getCurrentURI();

        }

        return $this->getSiteForUrl($url, $blockOnCreation);
    }

    /**
     * @param string $url A complete url (not just the server) or otherwise the current url is used
     * @param boolean $blockOnCreation
     * @return \Gems\Util\SiteUrl
     */
    public function getSiteByFullUrl($url, $blockOnCreation = true)
    {
        try {
            $sql = "SELECT gsi_url FROM gems__sites 
                        WHERE ? LIKE CONCAT(gsi_url, '%') 
                        ORDER BY gsi_order, gsi_id";

            // \MUtil_Echo::track(str_replace('?', "'$url'", $sql));
            $foundUrl = $this->db->fetchOne($sql, $url);

            if ($foundUrl) {
                return $this->getSiteForUrl($foundUrl);
            }

            return $this->getSiteForUrl($url, $blockOnCreation);

        } catch (\Zend_Db_Statement_Exception $exc) {
            return null;
        }

    }

    /**
     * @param string $url An url or otherwise the current url is used
     * @param boolean $blockOnCreation
     * @return \Gems\Util\SiteUrl
     */
    public function getSiteForUrl($url, $blockOnCreation = false)
    {
        $site = new SiteUrl($url, $blockOnCreation);
        $this->source->applySource($site);

        return $site;
    }

    /**
     * Returns the cron job lock
     *
     * @return \Gems_Util_LockFile
     */
    public function getSiteLock()
    {
        return $this->util->getLockFile('site_lock.txt');
    }

    /**
     * @param \Zend_Controller_Request_Abstract $request
     * @return string
     */
    public function getUsedHost(\Zend_Controller_Request_Abstract $request)
    {
        return \MUtil_String::stripToHost($request->getServer(
            'HTTP_ORIGIN',
            $request->getServer(
                'HTTP_REFERER',
                $this->util->getCurrentURI())));
    }

    /**
     * Get the organizations not served by a specific site
     *
     * @return array [$orgId]
     */
    public function getUnspecificOrganizations()
    {
        $existingOrganizations = array_keys($this->util->getDbLookup()->getOrganizations());

        try {
            $sql = "SELECT gsi_organizations FROM gems__sites WHERE gsi_select_organizations = 1";

            $organizationStrings = $this->db->fetchCol($sql);

            if ($organizationStrings) {
                $servedOrganizations = array_unique(array_filter(explode(self::ORG_SEPARATOR, implode('', $organizationStrings))));

                return array_diff($existingOrganizations, $servedOrganizations);
            }
        } catch (\Zend_Db_Statement_Exception $exc) {
            // Intentional fall through
        }
        return $existingOrganizations;
    }

    /**
     * @param \Zend_Controller_Request_Abstract $request
     * @return string The not allowed host
     */
    public function isRequestFromAllowedHost(\Zend_Controller_Request_Abstract $request)
    {
        if (\MUtil_Console::isConsole() || \Zend_Session::$_unitTestEnabled) {
            return null;
        }

        if (! $request instanceof \Zend_Controller_Request_Http) {
            // Should not really occur, but now the code knows the type
            return null;
        }

        $basePath = $request->getBasePath();
        $isPost   = $request->isPost();
        $locked   = $this->getSiteLock()->isLocked();

        $hosts = [];
        if (isset($_SERVER['HTTP_HOST'])) {
            $hosts[] = $_SERVER['HTTP_HOST'];
        }
        if (isset($_SERVER['SERVER_NAME'])) {
            $hosts[] = $_SERVER['SERVER_NAME'];
        }
        // $hosts[] = 'www.evilsite.com';
        // \MUtil_Echo::track($hosts);
        foreach (array_unique($hosts) as $host) {
            $url  = $this->_hostToUrl($host, $basePath);
            $site = $this->getSiteForUrl($url, $isPost);
            if ($site) {
                if ($site->isNew()) {
                    if ($isPost || $locked) {
                        return $host;
                    }
                } elseif ($site->isBlocked()) {
                    return $host;
                }
            }
        }

        if ($isPost) {
            $referrers = [];
            $referrers[] = $request->getServer('HTTP_ORIGIN');
            $referrers[] = $request->getServer('HTTP_REFERER');
            // $referrers[] = 'http://www.evilsite.com/';
            // $referrers[] = 'http://www.evilsite.com/pulse/id/1?attack=mode';
            // $referrers[] = 'http://www.evilsite2.com/';
            //\MUtil_Echo::track($referrers);
            foreach (array_unique(array_filter($referrers)) as $referrer) {
                if (!empty($basePath) && !\MUtil_String::contains($referrer, $basePath)) {
                    $referrer = rtrim($referrer, '/') . $basePath;
                }
                if (! (\MUtil_String::startsWith($referrer, 'https://') || \MUtil_String::startsWith($referrer, 'http://'))) {
                    $referrer = (\MUtil_Https::on() ? 'https' : 'http') . '://' . $referrer;
                }
                $site = $this->getSiteByFullUrl($referrer, $isPost);
                if ($site) {
                    if ($site->isNew()) {
                        if ($isPost) {
                            return \MUtil_String::beforeChars($referrer, '?&<>=');
                        }
                    } elseif ($site->isBlocked()) {
                        return $site->getUrl();
                    }
                }
            }
        }

        return null;
    }
}