1:   2:   3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  14:  15:  16:  17:  18:  19:  20:  21:  22:  23:  24:  25:  26:  27:  28:  29:  30:  31:  32:  33:  34:  35:  36:  37:  38:  39:  40:  41:  42:  43:  44:  45:  46:  47:  48:  49:  50:  51:  52:  53:  54:  55:  56:  57:  58:  59:  60:  61:  62:  63:  64:  65:  66:  67:  68:  69:  70:  71:  72:  73:  74:  75:  76:  77:  78:  79:  80:  81:  82:  83:  84:  85:  86:  87:  88:  89:  90:  91:  92:  93:  94:  95:  96:  97:  98:  99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 
<?php
    /**
     * Copyright (C) Apis Networks, Inc - All Rights Reserved.
     *
     * Unauthorized copying of this file, via any medium, is
     * strictly prohibited without consent. Any dissemination of
     * material herein is prohibited.
     *
     * For licensing inquiries email <licensing@apisnetworks.com>
     *
     * Written by Matt Saladna <matt@apisnetworks.com>, May 2017
     */

    namespace Module\Support;

    use Module_Skeleton;
    use Opcenter;
    use Opcenter\Filesystem;
    use Opcenter\Http\Apache\Map;
    use Opcenter\Provisioning\ConfigurationWriter;
    use User_Module;

    /**
     * Support structure for auth module
     */
    abstract class Aliases extends Module_Skeleton implements \Opcenter\Contracts\Hookable
    {
        const CONFIG_DB_DIR = Opcenter\Http\Apache::DOMAIN_PATH;
        const BYPASS_FILE = '/etc/virtualhosting/dnsbypass';
        const CACHE_KEY = 'aliases';

        /**
         * Add domain to Apache map
         *
         * @param string $domain
         * @param string $path
         * @return bool
         */
        protected function addMap(string $domain, string $path): bool
        {
            $map = Map::fromSiteDomainMap($this->domain_info_path(), $this->site,
                Map::MODE_WRITE);
            $domain = strtolower($domain);
            if (isset($map[$domain])) {
                warn("overwriting domain `%s' with new path `%s'", $domain, $path);
            }
            if (0 !== strpos($path, FILESYSTEM_VIRTBASE)) {
                $path = $this->domain_fs_path($path);
            }
            $map[$domain] = $path;
            \Cache_Account::spawn($this->getAuthContext())->del(self::CACHE_KEY);

            return true;
        }


        /**
         * Remove domain from map
         *
         * @param string $domain
         * @return bool
         */
        protected function removeMap(string $domain): bool
        {
            $map = Map::fromSiteDomainMap($this->domain_info_path(), $this->site,
                Map::MODE_WRITE);
            $domain = strtolower($domain);
            unset($map[$domain]);
            \Cache_Account::spawn($this->getAuthContext())->del(self::CACHE_KEY);

            return true;
        }

        protected function cache(array $data): void
        {
            $cache = \Cache_Account::spawn($this->getAuthContext());
            $cache->set(static::CACHE_KEY, $data);
        }

        /**
         * Return a transformed map of domains
         *
         * @return array
         */
        protected function transformMap(): array
        {
            $map = Map::fromSiteDomainMap($this->domain_info_path(), $this->site,
                Map::MODE_READ);
            $domains = [];
            if (!$map) {
                return $domains;
            }

            foreach ($map as $domain => $path) {
                $domains[$domain] = $this->jailDomain($path);
            }

            unset($domains[$this->domain]);
            $this->cache($domains);

            return $domains;
        }

        private function jailDomain(string $path): string {
            $fst = $this->domain_fs_path();
            $len = \strlen($fst);
            return rtrim(0 === strpos($path, $fst) ? substr($path, $len) : $path, '/');
        }

        /**
         * Remove a domain from DNS bypass check
         *
         * @param  string $domain
         * @return bool
         */
        protected function removeBypass(string $domain): bool
        {
            if (!file_exists(self::BYPASS_FILE)) {
                return true;
            }

            $recs = file(self::BYPASS_FILE, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);

            if (false === ($line = array_search($domain, $recs, true))) {
                return -1;
            }

            unset($recs[$line]);
            if (\count($recs) < 1) {
                // save a few cpu cycles
                unlink(self::BYPASS_FILE);
            } else {
                file_put_contents(self::BYPASS_FILE, implode("\n", $recs));
            }

            return true;
        }

        protected function createDocumentRoot(string $path)
        {
            // sync up file cache
            $this->file_purge();
            if (file_exists($this->domain_fs_path() . '/' . $path)) {
                return true;
            }
            $gid = $this->group_id;
            if (preg_match('!^/home/([^/]+)/!', $path, $user)) {
                $user = $user[1];
                $home = '/home/' . $user;
                $users = $this->user_get_users();
                $uid = $users[$user]['uid'];
                if ($uid < User_Module::MIN_UID) {
                    return error('%s: user unknown in system', $user);
                }
                $stat = $this->file_stat($home);
                $perms = decoct($stat['permissions'] | ($this->php_jailed() ? 0010 : 0001));
                if ($stat && !$this->file_chmod($home, $perms)) {
                    warn("Failed to open permissions on `%s' to allow HTTP access");
                }
            } else {
                $uid = $this->user_id;
            }
            $fullpath = $this->domain_fs_path() . '/' . $path;
            if (!mkdir($fullpath)) {
                return error("z'huh!? failed to create doc root?");
            }
            chown($fullpath, $uid);
            chgrp($fullpath, $gid);
            $index = $fullpath . '/index.html';
            return file_put_contents($index, (string)(new ConfigurationWriter('apache.placeholder',
                \Opcenter\SiteConfiguration::import($this->getAuthContext())))->compile([])) &&
                Filesystem::chogp($index, $uid, $gid, 0644);
        }

        /**
         * Check to bypass addon domain DNS validation test
         *
         * @param string $domain
         * @return bool domain is bypassed
         */
        protected function isBypass(string $domain): bool
        {
            if (\defined('DOMAINS_DNS_CHECK') && !\constant('DOMAINS_DNS_CHECK')) {
                return true;
            }
            if (!file_exists(self::BYPASS_FILE)) {
                return false;
            }

            $recs = file(self::BYPASS_FILE, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);

            return (false !== ($line = array_search($domain, $recs, true)));
        }
    }