'file' => '', 'name' => SITENAME, 'sname' => preg_replace('/[^a-z0-9-\.\+]*/i', '', str_replace(' ', '-', SITENAME)), 'xml' => null, ]; } else{ echo "Found the following bundles:" . NL; foreach($bundles as $b){ echo $b['name'] . ' (' . $b['file'] . ')' . NL; } } sleep(1); // Prompt the user with what version the new bundles will be. $version = Core::GetComponent('core')->getVersion(); $version = CLI::PromptUser('Please set the bundled version or', 'text', $version); foreach($bundles as $b){ /** @var XMLLoader|null $xml */ $xml = $b['xml']; $destdir = $dir . '/' . $b['sname']; // Check the dest directory for current versions. if(!is_dir($destdir)) mkdir($destdir); $desttgz = $b['sname'] . '-' . $version; if(!is_dir($destdir . '/' . $desttgz)) mkdir($destdir . '/' . $desttgz); // Get a list of packages to export.
public function analytics() { $request = $this->getPageRequest(); $view = $this->getView(); $manager = \Core\user()->checkAccess('p:/package_repository/view_analytics'); if (!$manager) { return View::ERROR_ACCESSDENIED; } // Retrieve a list of connections to this repo for both downloading and checks! $where = new \Core\Datamodel\DatasetWhereClause(); $where->addWhereSub('OR', ['baseurl = /packagerepository', 'baseurl = /packagerepository/download']); // Default to a 3-month window for now just to have a relatively useful sample of data. // This will be expanded to include a filter at some point in time. $window = new \Core\Date\DateTime(); $window->modify('-3 months'); $window = $window->format('U'); // Generate a boilerplate dataset for the all-history view. // This is required because the graphing software expects all data to be in the same columns, // and these columns are simply indexed arrays. // As we're pulling the data potentially out-of-order and across different versions, // this array will provide a consistent scaffold for unset versions. $allboilerplate = []; // Series Boilerplate $allmonths = []; // Labels // This goes back 12 months. $date = new \Core\Date\DateTime(); $date->modify('-11 months'); for ($i = 1; $i <= 12; $i++) { $allboilerplate[$date->format('Ym')] = null; $allmonths[] = $date->format('M'); $date->nextMonth(); } $raw = UserActivityModel::FindRaw($where); // Will contain a list of useragents along with the count of how many access. $useragents = []; // Will contain how many times a given IP has requested the site. // This is for a metric that currently is not enabled. $ipaddresses = []; // A rich list of hosts along with the latest version, the IP connecting from, and the date of the last check. $hosts = []; // All series for the bar graph at the top of the page, Keyed by version and contains the total number of hits. $allseries = []; $allseries['Total'] = ['class' => 'series-other', 'name' => 'Total', 'title' => 'Total', 'useragent' => '', 'values' => $allboilerplate]; // Used so I can compare the version of the connecting useragent against the current version of Core. // This of course does noothing to ensure that this site is updated, but it at least should give some perspective. $currentVersion = Core::VersionSplit(Core::GetComponent('core')->getVersion()); foreach ($raw as $dat) { if (strpos($dat['useragent'], '(http://corepl.us)') !== false) { /** @var string $ua ex: "Core Plus 1.2.3" */ $ua = str_replace(' (http://corepl.us)', '', $dat['useragent']); /** @var string $version Just the version, ex: "1.2.3" */ $version = str_replace('Core Plus ', '', $ua); /** @var string $referrer Original Site/Server, ex: "http://corepl.us" */ $referrer = $dat['referrer'] ? $dat['referrer'] : $dat['ip_addr']; // The set of logic to compare the current version of Core against the version connecting. // This is used primarily to set a class name onto the graphs so that they can be coloured specifically. $v = Core::VersionSplit($version); // These two values are used in the historical map, (as revision may be a bit useless at this scale). $briefVersion = $v['major'] . '.' . $v['minor'] . '.x'; $briefUA = 'Core Plus ' . $briefVersion; if ($v['major'] == $currentVersion['major'] && $v['minor'] == $currentVersion['minor']) { // Check is same version as current (or newer), blue! $class = 'series-current'; } elseif ($v['major'] + 2 <= $currentVersion['major']) { // Check is at least 2 major versions out of date, red. $class = 'series-outdated-2'; } elseif ($v['major'] + 1 <= $currentVersion['major']) { // Check is at least 1 major version out of date, green. $class = 'series-outdated-1'; } else { // Same major version, close enough. $class = 'series-outdated-0'; } $month = date('Ym', $dat['datetime']); } else { $ua = 'Other'; $briefUA = 'Other'; $version = null; $briefVersion = null; $referrer = null; $class = 'series-other'; $month = null; } // All Data! if ($month && array_key_exists($month, $allboilerplate)) { if (!isset($allseries[$briefUA])) { $allseries[$briefUA] = ['class' => $class, 'name' => $briefVersion, 'title' => $briefUA, 'useragent' => $briefUA, 'values' => $allboilerplate]; } $allseries[$briefUA]['values'][$month]++; //$allseries['Total']['values'][$month]++; } // Is this data new enough to display on the graph? // This is required because the "all" graph at the top needs all-time, (or at least the past 12 months). if ($dat['datetime'] >= $window) { // USER AGENT DATA if (!isset($useragents[$ua])) { $useragents[$ua] = ['value' => 0, 'class' => $class, 'name' => $version, 'title' => $ua, 'useragent' => $ua]; } $useragents[$ua]['value']++; // IP ADDRESS DATA if (!isset($ipaddresses[$dat['ip_addr']])) { $ipaddresses[$dat['ip_addr']] = ['ip_addr' => $dat['ip_addr'], 'count' => 0]; } $ipaddresses[$dat['ip_addr']]['count']++; // HOSTS DATA if ($version && $referrer) { $k = $referrer . '-' . $dat['ip_addr']; if (!isset($hosts[$k])) { $hosts[$k] = ['servername' => $referrer, 'ip_addr' => $dat['ip_addr'], 'version' => '0.0', 'datetime' => 1]; } if (Core::VersionCompare($hosts[$k]['version'], $version, 'lt')) { $hosts[$k]['version'] = $version; } if ($hosts[$k]['datetime'] < $dat['datetime']) { $hosts[$k]['datetime'] = $dat['datetime']; } } } } ksort($useragents); ksort($hosts, SORT_NATURAL); ksort($allseries, SORT_NATURAL); ksort($ipaddresses, SORT_NATURAL); // Update the title of the values now that the totals have been created. // Also, take this opportunity to set the chart data as necessary, (as its format will be slightly different). $chart = ['versions' => ['labels' => [], 'series' => []], 'all' => ['labels' => array_values($allmonths), 'series' => []], 'ips' => []]; foreach ($useragents as &$dat) { $dat['title'] .= ' (' . $dat['value'] . ' Total Hits)'; $chart['versions']['labels'][] = $dat['name']; $chart['versions']['series'][] = ['value' => $dat['value'], 'className' => $dat['class'], 'name' => $dat['name'], 'title' => $dat['title']]; } foreach ($allseries as &$dat) { $data = []; foreach ($dat['values'] as $v) { $data[] = ['value' => $v, 'title' => $dat['title'] . ' (' . $v . ' Monthly Hits)']; //$data[] = $v; } $chart['all']['series'][] = ['data' => $data, 'className' => $dat['class'], 'name' => $dat['name'], 'title' => $dat['title']]; } // Convert these to JSON data! $chart['versions'] = json_encode($chart['versions']); $chart['all'] = json_encode($chart['all']); //$chart['ips'] = json_encode($chart['ips']); $view->title = 't:STRING_PACKAGE_REPOSITORY_ANALYTICS'; $view->assign('chart', $chart); $view->assign('raw', $raw); $view->assign('ip_addresses', $ipaddresses); $view->assign('useragents', $useragents); $view->assign('hosts', $hosts); //var_dump($chart, $useragents, $ipaddresses, $ipversion, $raw); die(); }
/** * Get the mailer responsible for sending this email. * * @return PHPMailer */ public function getMailer() { if (!$this->_mailer) { $this->_mailer = new PHPMailer(true); // Load in some default options for this email based on the configuration options. $this->_mailer->From = ConfigHandler::Get('/core/email/from'); if (!$this->_mailer->From) $this->_mailer->From = 'website@' . $_SERVER['HTTP_HOST']; $this->_mailer->Sender = $this->_mailer->From; $this->_mailer->FromName = ConfigHandler::Get('/core/email/from_name'); $this->_mailer->Mailer = ConfigHandler::Get('/core/email/mailer'); $this->_mailer->Sendmail = ConfigHandler::Get('/core/email/sendmail_path'); if ($this->_mailer->Mailer == 'smtp') { $this->_mailer->Host = ConfigHandler::Get('/core/email/smtp_host'); $this->_mailer->Port = ConfigHandler::Get('/core/email/smtp_port'); switch(ConfigHandler::Get('/core/email/smtp_auth')){ case 'LOGIN': case 'PLAIN': $this->_mailer->AuthType = ConfigHandler::Get('/core/email/smtp_auth'); $this->_mailer->Username = ConfigHandler::Get('/core/email/smtp_user'); $this->_mailer->Password = ConfigHandler::Get('/core/email/smtp_password'); $this->_mailer->SMTPAuth = true; break; case 'NTLM': $this->_mailer->AuthType = ConfigHandler::Get('/core/email/smtp_auth'); $this->_mailer->Username = ConfigHandler::Get('/core/email/smtp_user'); $this->_mailer->Password = ConfigHandler::Get('/core/email/smtp_password'); $this->_mailer->Realm = ConfigHandler::Get('/core/email/smtp_domain'); $this->_mailer->SMTPAuth = true; break; case 'NONE': $this->_mailer->SMTPAuth = false; break; } $this->_mailer->SMTPSecure = (ConfigHandler::Get('/core/email/smtp_security') == 'none') ? '' : ConfigHandler::Get('/core/email/smtp_security'); } // Tack on some anti-abuse and meta headers. // These don't actually serve an explict function, but are added because. $this->_mailer->AddCustomHeader('X-AntiAbuse: This header was added to track abuse, please include it with any abuse report'); if (\Core\user()->exists()) { $this->_mailer->AddCustomHeader('X-AntiAbuse: User_id - ' . \Core\user()->get('id')); $this->_mailer->AddCustomHeader('X-AntiAbuse: User_name - ' . \Core\user()->getDisplayName()); } $this->_mailer->AddCustomHeader('X-AntiAbuse: Original Domain - ' . SERVERNAME); $this->_mailer->AddCustomHeader('X-AntiAbuse: Sitename - ' . SITENAME); $this->_mailer->AddCustomHeader('MimeOLE: Core Plus'); $this->_mailer->AddCustomHeader('X-Content-Encoded-By: Core Plus ' . Core::GetComponent()->getVersion()); $this->_mailer->XMailer = 'Core Plus ' . Core::GetComponent()->getVersion() . ' (http://corepl.us)'; } return $this->_mailer; }
public static function GetVersion() { return Core::GetComponent()->getVersionInstalled(); }
public function fetch(){ $generator = 'Core Plus'; // Hide version numbers when not in development! if(DEVELOPMENT_MODE) $generator .= ' ' . Core::GetComponent()->getVersion(); return array( 'generator' => '<meta name="generator" content="' . $generator . '"/>' ); }
/** * Check if this package is already installed and current (at least as new version installed) * * @return boolean */ public function isCurrent() { switch($this->getType()){ case 'core': case 'component': $c = Core::GetComponent($this->getName()); if (!$c) return false; // Not installed? Not current. return version_compare($c->getVersion(), $this->getVersion(), 'ge'); case 'theme': $t = ThemeHandler::GetTheme($this->getName()); if (!$t) return false; // Not installed? Not current. return version_compare($t->getVersion(), $this->getVersion(), 'ge'); } }
/** * Render this view and send all appropriate headers to the browser, (if applicable) * * @return void */ public function render() { // Before I go about rendering anything, enable UTF-8 to ensure proper i18n! if ($this->contenttype && $this->contenttype == View::CTYPE_HTML) { View::AddMeta('http-equiv="Content-Type" content="text/html;charset=UTF-8"'); } $data = $this->fetch(); // Be sure to send the content type and status to the browser, (if it's a page) if ( !headers_sent() && ($this->mode == View::MODE_PAGE || $this->mode == View::MODE_PAGEORAJAX || $this->mode == View::MODE_AJAX || $this->mode == View::MODE_NOOUTPUT || $this->mode == View::MODE_EMAILORPRINT) ) { switch ($this->error) { case View::ERROR_NOERROR: header('Status: 200 OK', true, $this->error); break; case View::ERROR_BADREQUEST: header('Status: 400 Bad Request', true, $this->error); break; case View::ERROR_UNAUTHORIZED: header('Status: 401 Unauthorized', true, $this->error); break; case View::ERROR_PAYMENTREQUIRED: header('Status: 402 Payment Required', true, $this->error); break; case View::ERROR_ACCESSDENIED: header('Status: 403 Forbidden', true, $this->error); break; case View::ERROR_NOTFOUND: header('Status: 404 Not Found', true, $this->error); break; case View::ERROR_METHODNOTALLOWED: header('Status: 405 Method Not Allowed', true, $this->error); break; case View::ERROR_NOTACCEPTABLE: header('Status: 406 Not Acceptable', true, $this->error); break; case View::ERROR_PROXYAUTHENTICATIONREQUIRED: header('Status: 407 Proxy Authentication Required', true, $this->error); break; case View::ERROR_REQUESTTIMEOUT: header('Status: 408 Request Time-out', true, $this->error); break; case View::ERROR_CONFLICT: header('Status: 409 Conflict', true, $this->error); break; case View::ERROR_GONE: header('Status: 410 Gone', true, $this->error); break; case View::ERROR_LENGTHREQUIRED: header('Status: 411 Length Required', true, $this->error); break; case View::ERROR_PRECONDITIONFAILED: header('Status: 412 Precondition Failed', true, $this->error); break; case View::ERROR_ENTITYTOOLARGE: header('Status: 413 Request Entity Too Large', true, $this->error); break; case View::ERROR_URITOOLARGE: header('Status: 414 Request-URI Too Large', true, $this->error); break; case View::ERROR_UNSUPPORTEDMEDIATYPE: header('Status: 415 Unsupported Media Type', true, $this->error); break; case View::ERROR_RANGENOTSATISFIABLE: header('Status: 416 Requested range not satisfiable', true, $this->error); break; case View::ERROR_EXPECTATIONFAILED: header('Status: 417 Expectation Failed', true, $this->error); break; case View::ERROR_SERVERERROR: header('Status: 500 Internal Server Error', true, $this->error); break; default: header('Status: 500 Internal Server Error', true, $this->error); break; // I don't know WTF happened... } if ($this->contenttype) { if ($this->contenttype == View::CTYPE_HTML) header('Content-Type: text/html; charset=UTF-8'); else header('Content-Type: ' . $this->contenttype); } //mb_internal_encoding('utf-8'); header('X-Content-Encoded-By: Core Plus' . (DEVELOPMENT_MODE ? ' ' . Core::GetComponent()->getVersion() : '')); if(\ConfigHandler::Get('/core/security/x-frame-options')){ header('X-Frame-Options: ' . \ConfigHandler::Get('/core/security/x-frame-options')); } if(\ConfigHandler::Get('/core/security/csp-frame-ancestors')){ header('Content-Security-Policy: frame-ancestors \'self\' ' . \ConfigHandler::Get('/core/security/content-security-policy')); } if($this->updated !== null){ header('Last-Modified: ' . Time::FormatGMT($this->updated, Time::TIMEZONE_USER, Time::FORMAT_RFC2822)); //header('Last-Modified: ' . Time::FormatGMT($this->updated, Time::TIMEZONE_USER, Time::FORMAT_ISO8601)); } // Are there any custom headers to send also? foreach($this->headers as $k => $v){ header($k . ': ' . $v); } } // No SSL, skip all this! if(SSL_MODE != SSL_MODE_DISABLED){ // If SSL is required by the controller and it's available, redirect there! if($this->ssl && !SSL){ header('Location: ' . ROOT_URL_SSL . substr(REL_REQUEST_PATH, 1)); die('This page requires SSL, if it does not redirect you automatically, please <a href="' . ROOT_URL_SSL . substr(REL_REQUEST_PATH, 1) . '">Click Here</a>.'); } // If SSL is set to be ondemand and the page does not have it set but it's enabled, redirect to the non-SSL version. elseif(!$this->ssl && SSL && SSL_MODE == SSL_MODE_ONDEMAND){ header('Location: ' . ROOT_URL_NOSSL . substr(REL_REQUEST_PATH, 1)); die('This page does not require SSL, if it does not redirect you automatically, please <a href="' . ROOT_URL_NOSSL . substr(REL_REQUEST_PATH, 1) . '">Click Here</a>.'); } // Else, SSL_MODE_ALLOWED doesn't care if SSL is enabled or not! } //echo mb_convert_encoding($data, 'UTF-8', 'auto'); //echo mb_convert_encoding($data, 'HTML-ENTITIES', 'auto'); echo $data; }
/** * Save or get the package XML for this theme. This is useful for the * packager * * @param boolean $minified * @param string $filename */ public function savePackageXML($minified = true, $filename = false) { // Instantiate a new XML Loader object and get it ready to use. $dom = new \XMLLoader(); $dom->setRootName('package'); $dom->load(); // Populate the root attributes for this theme package. $dom->getRootDOM()->setAttribute('type', 'theme'); $dom->getRootDOM()->setAttribute('name', $this->getName()); $dom->getRootDOM()->setAttribute('version', $this->getVersion()); // Declare the packager $dom->createElement('packager[version="' . \Core::GetComponent()->getVersion() . '"]'); /* // Themes don't have any provide directives. // Copy over any provide directives. foreach ($this->_xmlloader->getRootDOM()->getElementsByTagName('provides') as $u) { $newu = $dom->getDOM()->importNode($u); $dom->getRootDOM()->appendChild($newu); } $dom->getElement('/provides[type="component"][name="' . strtolower($this->getName()) . '"][version="' . $this->getVersion() . '"]'); */ /* // Themes don't have any requrie directives. // Copy over any requires directives. foreach ($this->_xmlloader->getRootDOM()->getElementsByTagName('requires') as $u) { $newu = $dom->getDOM()->importNode($u); $dom->getRootDOM()->appendChild($newu); } */ // Copy over any upgrade directives. // This one can be useful for an existing installation to see if this // package can provide a valid upgrade path. foreach ($this->_xmlloader->getRootDOM()->getElementsByTagName('upgrade') as $u) { $newu = $dom->getDOM()->importNode($u); $dom->getRootDOM()->appendChild($newu); } // Tack on description $desc = $this->_xmlloader->getElement('/description', false); if ($desc) { $newd = $dom->getDOM()->importNode($desc); $newd->nodeValue = $desc->nodeValue; $dom->getRootDOM()->appendChild($newd); } $out = $minified ? $dom->asMinifiedXML() : $dom->asPrettyXML(); if ($filename) { file_put_contents($filename, $out); } else { return $out; } }
/** * Dispatch an event, optionally passing 1 or more parameters. * * @param string $hookName The name of the hook to dispatch * @param mixed $args * * @return mixed */ public static function DispatchHook($hookName, $args = null) { // Prevent any hooks from being dispatched until Core is ready! if(!Core::GetComponent()) return null; $hookName = strtolower($hookName); // Case insensitive will prevent errors later on. Core\Utilities\Logger\write_debug('Dispatching hook ' . $hookName); //echo "Calling hook $hookName<br>"; //var_dump(HookHandler::$RegisteredHooks[$hookName]); if (!isset(HookHandler::$RegisteredHooks[$hookName])) { trigger_error('Tried to dispatch an undefined hook ' . $hookName, E_USER_NOTICE); return null; } \Core\Utilities\Profiler\Profiler::GetDefaultProfiler()->record('Dispatching hook ' . $hookName); $args = func_get_args(); // Drop off the hook name from the arguments. array_shift($args); $hook = HookHandler::$RegisteredHooks[$hookName]; $result = call_user_func_array(array(&$hook, 'dispatch'), $args); Core\Utilities\Logger\write_debug('Dispatched hook ' . $hookName); \Core\Utilities\Profiler\Profiler::GetDefaultProfiler()->record('Dispatched hook ' . $hookName); return $result; }
'component' => $core, 'dir' => ROOT_PDIR . 'core/', ]; unset($core); // Load in all components currently on the system // Open the "component" directory and look for anything with a valid component.xml file. $dir = ROOT_PDIR . 'components'; $dh = opendir($dir); while(($file = readdir($dh)) !== false){ if($file{0} == '.') continue; if(!is_dir($dir . '/' . $file)) continue; if(!is_readable($dir . '/' . $file . '/' . 'component.xml')) continue; $c = Core::GetComponent($file); // Is this not valid? if(!$c){ CLI::PrintLine('Skipping invalid component ' . $file); continue; } // What's this file's version? $xml = new XMLLoader(); $xml->setRootName('component'); if(!$xml->loadFromFile($dir . '/' . $file . '/component.xml')){ CLI::PrintLine('Skipping component ' . $file . ', unable to load XML file'); continue; }
public static function PerformInstall($type, $name, $version, $dryrun = false, $verbose = false){ if($verbose){ // These are needed to force the output to be sent immediately. while ( @ob_end_flush() ); // even if there is no nested output buffer if(function_exists('apache_setenv')){ // This function doesn't exist in CGI mode :/ apache_setenv('no-gzip', '1'); } ini_set('output_buffering','on'); ini_set('zlib.output_compression', 0); ob_implicit_flush(); // Give some basic styles for this output. echo '<html> <head> <!-- Yes, the following is 1024 spaces. This is because some browsers have a 1Kb buffer before they start rendering text --> ' . str_repeat(" ", 1024) . ' <style> body { background: none repeat scroll 0 0 black; color: #22EE33; font-family: monospace; } </style> </head> <body>'; } $timer = microtime(true); // Give this script a few more seconds to run. set_time_limit(max(90, ini_get('max_execution_time'))); // This will get a list of all available updates and their sources :) if($verbose) self::_PrintHeader('Retrieving Updates'); $updates = UpdaterHelper::GetUpdates(); if($verbose){ self::_PrintInfo('Found ' . $updates['sitecount'] . ' repository site(s)!', $timer); self::_PrintInfo('Found ' . $updates['pkgcount'] . ' packages!', $timer); } // A list of changes that are to be applied, (mainly for the dry run). $changes = array(); // Target in on the specific object we're installing. Useful for a shortcut. switch($type){ case 'core': $initialtarget = &$updates['core']; break; case 'components': $initialtarget = &$updates['components'][$name]; break; case 'themes': $initialtarget = &$updates['themes'][$name]; break; default: return [ 'status' => 0, 'message' => '[' . $type . '] is not a valid installation type!', ]; } // This is a special case for testing the installer UI. $test = ($type == 'core' && $version == '99.1337~(test)'); if($test && $verbose){ self::_PrintHeader('Performing a test installation!'); } if($test){ if($verbose){ self::_PrintInfo('Sleeping for a few seconds... because servers are always slow when you don\'t want them to be!', $timer); } sleep(4); // Also overwrite some of the target's information. $repo = UpdateSiteModel::Find(null, 1); $initialtarget['source'] = 'repo-' . $repo->get('id'); $initialtarget['location'] = 'http://corepl.us/api/2_4/tests/updater-test.tgz.asc'; $initialtarget['destdir'] = ROOT_PDIR; $initialtarget['key'] = 'B2BEDCCB'; $initialtarget['status'] = 'update'; //if($verbose){ // echo '[DEBUG]' . $nl; // var_dump($initialtarget); //} } // Make sure the name and version exist in the updates list. // In theory, the latest version of core is the only one displayed. if(!$test && $initialtarget['version'] != $version){ return [ 'status' => 0, 'message' => $initialtarget['typetitle'] . ' does not have the requested version available.', 'debug' => [ 'versionrequested' => $version, 'versionfound' => $initialtarget['version'], ], ]; } // A queue of components to check. $pendingqueue = array($initialtarget); // A queue of components that will be installed that have satisfied dependencies. $checkedqueue = array(); // This will assemble the list of required installs in the correct order. // If a given dependency can't be met, the installation will be aborted. if($verbose){ self::_PrintHeader('CHECKING DEPENDENCIES'); } do{ $lastsizeofqueue = sizeof($pendingqueue); foreach($pendingqueue as $k => $c){ $good = true; if(isset($c['requires'])){ if($verbose){ self::_PrintInfo('Checking dependencies for ' . $c['typetitle'], $timer); } foreach($c['requires'] as $r){ // Sometimes there will be blank requirements in the metafile. if(!$r['name']) continue; $result = UpdaterHelper::CheckRequirement($r, $checkedqueue, $updates); if($result === false){ // Dependency not met return [ 'status' => 0, 'message' => $c['typetitle'] . ' requires ' . $r['name'] . ' ' . $r['version'] ]; } elseif($result === true){ // Dependency met via either installed components or new components // yay if($verbose){ self::_PrintInfo('Dependency [' . $r['name'] . ' ' . $r['version'] . '] met with already-installed packages.', $timer); } } else{ if($verbose){ self::_PrintInfo('Additional package [' . $result['typetitle'] . '] required to meet dependency [' . $r['name'] . ' ' . $r['version'] . '], adding to queue and retrying!', $timer); } // It's an array of requirements that are needed to satisfy this installation. $pendingqueue = array_merge(array($result), $pendingqueue); $good = false; } } } else{ if($verbose){ self::_PrintInfo('Skipping dependency check for ' . $c['typetitle'] . ', no requirements present', $timer); } // The require key isn't present... OK! // This happens with themes, as they do not have any dependency logic. } if($good === true){ $checkedqueue[] = $c; $changes[] = (($c['status'] == 'update') ? 'Update' : 'Install') . ' ' . $c['typetitle'] . ' ' . $c['version']; unset($pendingqueue[$k]); } } } while(sizeof($pendingqueue) && sizeof($pendingqueue) != $lastsizeofqueue); // Do validation checks on all these changes. I need to make sure I have the GPG key for each one. // This is done here to save having to download the files from the remote server first. foreach($checkedqueue as $target){ // It'll be validated prior to installation anyway. if(!$target['key']) continue; $output = array(); exec('gpg --homedir "' . GPG_HOMEDIR . '" --list-public-keys "' . $target['key'] . '"', $output, $result); if($result > 0){ // Key validation failed! if($verbose){ echo implode("<br/>\n", $output); } return [ 'status' => 0, 'message' => $c['typetitle'] . ' failed GPG verification! Is the key ' . $target['key'] . ' installed?' ]; } } // Check that the queued packages have not been locally modified if installed. if($verbose){ self::_PrintHeader('Checking for local modifications'); } foreach($checkedqueue as $target){ if($target['status'] == 'update'){ switch($target['type']){ case 'core': $c = Core::GetComponent('core'); break; case 'components': $c = Core::GetComponent($target['name']); break; case 'themes': $c = null; break; } if($c){ // Are there changes? if(sizeof($c->getChangedAssets())){ foreach($c->getChangedAssets() as $change){ $changes[] = 'Overwrite locally-modified asset ' . $change; } } if(sizeof($c->getChangedFiles())){ foreach($c->getChangedFiles() as $change){ $changes[] = 'Overwrite locally-modified file ' . $change; } } if(sizeof($c->getChangedTemplates())){ foreach($c->getChangedTemplates() as $change){ $changes[] = 'Overwrite locally-modified template ' . $change; } } } } } // If dry run is enabled, stop here. // After this stage, dragons be let loose from thar cages. if($dryrun){ return [ 'status' => 1, 'message' => 'All dependencies are met, ok to install', 'changes' => $changes, ]; } // Reset changes, in this case it'll be what was installed. $changes = array(); // By now, $checkedqueue will contain all the pending changes, theoretically with // the initially requested package at the end of the list. foreach($checkedqueue as $target){ if($verbose){ self::_PrintHeader('PERFORMING INSTALL (' . strtoupper($target['typetitle']) . ')'); } // This package is already installed and up to date. if($target['source'] == 'installed'){ return [ 'status' => 0, 'message' => $target['typetitle'] . ' is already installed and at the newest version.', ]; } // If this package is coming from a repo, install it from that repo. elseif(strpos($target['source'], 'repo-') !== false){ /** @var $repo UpdateSiteModel */ $repo = new UpdateSiteModel(substr($target['source'], 5)); if($verbose){ self::_PrintInfo('Using repository ' . $repo->get('url') . ' for installation source', $timer); } // Setup the remote file that will be used to download from. $file = new \Core\Filestore\Backends\FileRemote($target['location']); $file->username = $repo->get('username'); $file->password = $repo->get('password'); // The initial HEAD request pulls the metadata for the file, and sees if it exists. if($verbose){ self::_PrintInfo('Performing HEAD lookup on ' . $file->getFilename(), $timer); } if(!$file->exists()){ return [ 'status' => 0, 'message' => $target['location'] . ' does not seem to exist!' ]; } if($verbose){ self::_PrintInfo('Found a(n) ' . $file->getMimetype() . ' file that returned a ' . $file->getStatus() . ' status.', $timer); } // Get file contents will download the file. if($verbose){ self::_PrintInfo('Downloading ' . $file->getFilename(), $timer); } $downloadtimer = microtime(true); $obj = $file->getContentsObject(); // Getting the object simply sets it up, it doesn't download the contents yet. $obj->getContents(); // Now it has :p // How long did it take? if($verbose){ self::_PrintInfo('Downloaded ' . $file->getFilesize(true) . ' in ' . (round(microtime(true) - $downloadtimer, 2) . ' seconds'), $timer); } if(!($obj instanceof \Core\Filestore\Contents\ContentASC)){ return [ 'status' => 0, 'message' => $target['location'] . ' does not appear to be a valid GPG signed archive' ]; } if(!$obj->verify()){ // Maybe it can at least get the key.... if($key = $obj->getKey()){ return [ 'status' => 0, 'message' => 'Unable to locate public key for ' . $key . '. Is it installed?' ]; } return [ 'status' => 0, 'message' => 'Invalid GPG signature for ' . $target['typetitle'], ]; } // The object's key must also match what's in the repo. if($obj->getKey() != $target['key']){ return [ 'status' => 0, 'message' => '!!!WARNING!!!, Key for ' . $target['typetitle'] . ' is valid, but does not match what was expected form the repository data! This could be a major risk!', 'debug' => [ 'detectedkey' => $obj->getKey(), 'expectedkey' => $target['key'], ], ]; } if($verbose){ self::_PrintInfo('Found key ' . $target['key'] . ' for package maintainer, appears to be valid.', $timer); exec('gpg --homedir "' . GPG_HOMEDIR . '" --list-public-keys "' . $target['key'] . '"', $output, $result); foreach($output as $line){ if(trim($line)) self::_PrintInfo(htmlentities($line), $timer); } } if($verbose) self::_PrintInfo('Checking write permissions', $timer); $dir = \Core\directory($target['destdir']); if(!$dir->isWritable()){ return [ 'status' => 0, 'message' => $target['destdir'] . ' is not writable!' ]; } if($verbose) self::_PrintInfo('OK!', $timer); // Decrypt the signed file. if($verbose) self::_PrintInfo('Decrypting signed file', $timer); if(version_compare(Core::GetComponent('core')->getVersionInstalled(), '4.1.1', '<=') && $file->getBaseFilename() == 'download'){ // HACK < 4.1.2 // Retrieve the filename from the last part of the URL. // This is required because the URL may be /download?file=component/blah.tgz.asc $f = substr($file->getFilename(), strrpos($file->getFilename(), '/'), -4); /** @var $localfile \Core\Filestore\File */ $localfile = $obj->decrypt('tmp/updater/' . $f); } else{ /** @var $localfile \Core\Filestore\File */ $localfile = $obj->decrypt('tmp/updater/'); } /** @var $localobj \Core\Filestore\Contents\ContentTGZ */ $localobj = $localfile->getContentsObject(); if($verbose) self::_PrintInfo('OK!', $timer); // This tarball will be extracted to a temporary directory, then copied from there. if($verbose){ self::_PrintInfo('Extracting tarball ' . $localfile->getFilename(), $timer); } $tmpdir = $localobj->extract('tmp/installer-' . Core::RandomHex(4)); // Now that the data is extracted in a temporary directory, extract every file in the destination. /** @var $datadir \Core\Filestore\Directory */ $datadir = $tmpdir->get('data/'); if(!$datadir){ return [ 'status' => 0, 'message' => 'Invalid package, ' . $target['typetitle'] . ', does not contain a "data" directory.' ]; } if($verbose) self::_PrintInfo('OK!', $timer); if($verbose){ self::_PrintInfo('Installing files into ' . $target['destdir'], $timer); } // Will give me an array of Files in the data directory. $files = $datadir->ls(null, true); // Used to get the relative path for each contained file. $datalen = strlen($datadir->getPath()); foreach($files as $file){ if(!$file instanceof \Core\Filestore\Backends\FileLocal) continue; // It's a file, copy it over. // To do so, resolve the directory path inside the temp data dir. $dest = \Core\Filestore\Factory::File($target['destdir'] . substr($file->getFilename(), $datalen)); /** @var $dest \Core\Filestore\Backends\FileLocal */ if($verbose){ self::_PrintInfo('...' . substr($dest->getFilename(''), 0, 67), $timer); } $dest->copyFrom($file, true); } if($verbose) self::_PrintInfo('OK!', $timer); // Cleanup the temp directory if($verbose){ self::_PrintInfo('Cleaning up temporary directory', $timer); } $tmpdir->remove(); if($verbose) self::_PrintInfo('OK!', $timer); $changes[] = 'Installed ' . $target['typetitle'] . ' ' . $target['version']; } } // Clear the cache so the next pageload will pick up on the new components and goodies. \Core\Cache::Flush(); \Core\Templates\Backends\Smarty::FlushCache(); // Yup, that's it. // Just extract the files and Core will autoinstall/autoupgrade everything on the next page view. // yay... return [ 'status' => 1, 'message' => 'Performed all operations successfully!', 'changes' => $changes, ]; }
public function __construct() { // Load in the settings from Core. $this->width = ConfigHandler::Get('/captcha/width'); $this->height = ConfigHandler::Get('/captcha/height'); $this->minWordLength = ConfigHandler::Get('/captcha/minlength'); $this->maxWordLength = ConfigHandler::Get('/captcha/maxlength'); $this->lineWidth = ConfigHandler::Get('/captcha/linethrough'); $this->Yperiod = ConfigHandler::Get('/captcha/yperiod'); $this->Yamplitude = ConfigHandler::Get('/captcha/yamplitude'); $this->Xperiod = ConfigHandler::Get('/captcha/xperiod'); $this->Xamplitude = ConfigHandler::Get('/captcha/xamplitude'); $this->maxRotation = ConfigHandler::Get('/captcha/maxrotation'); $this->blur = ConfigHandler::Get('/captcha/blur'); // Ensure it knows where to look for the "resources"... $this->resourcesPath = Core::GetComponent('coolphpcaptcha')->getBaseDir() . "libs/cool-php-captcha/resources"; }
public function runRequirementChecks(){ $requires = $this->getRequires(); $results = []; foreach ($requires as $r) { $check = [ 'require' => $r, 'result' => [ 'passed' => false, 'available' => null, 'message' => null, ], ]; switch ($r['type']) { case 'component': if (!Core::IsComponentAvailable($r['name'])) { // Component is not available. $check['result']['message'] = $check['result']['message'] = 'Missing component ' . $r['name']; } elseif (!Core::IsComponentAvailable($r['name'], $r['version'], $r['operation'])) { $check['result']['available'] = Core::GetComponent($r['name'])->getVersionInstalled(); $check['result']['message'] = 'Requires component ' . $r['vstring'] . ', ' . $check['available'] . ' available.'; } else{ $check['result']['passed'] = true; $check['result']['available'] = Core::GetComponent($r['name'])->getVersionInstalled(); $check['result']['message'] = 'Component ' . $r['vstring'] . ' is available'; } $results[] = $check; break; case 'define': // Ensure that whatever define the script is expecting is there... this is useful for the EXEC_MODE define. if (!defined($r['name'])) { $check['result']['message'] = $check['result']['message'] = 'Missing define ' . $r['name']; } elseif ($r['value'] != null && constant($r['name']) != $r['value']) { // Also if they opted to include a value... check that too. $check['result']['message'] = $check['result']['message'] = 'Incorrect define ' . $r['name'] . ', expected value of: ' . $r['value']; } else{ $check['result']['passed'] = true; $check['result']['available'] = true; $check['result']['message'] = 'Define ' . $r['name'] . ' is set and correct'; } $results[] = $check; break; case 'function': // Requires a specific function to exist. This is most common with built-in PHP functions, // such as gd, ldap, or imap support. if(!function_exists($r['name'])){ $check['result']['message'] = $check['result']['message'] = 'Missing function ' . $r['name']; } else{ $check['result']['passed'] = true; $check['result']['available'] = true; $check['result']['message'] = 'Function ' . $r['name'] . ' is available'; } $results[] = $check; break; case 'jslibrary': if (!Core::IsJSLibraryAvailable($r['name'])) { // The library is not even available! $check['result']['message'] = 'Missing JSlibrary ' . $r['name']; } else{ $check['result']['passed'] = true; $check['result']['available'] = true; $check['result']['message'] = 'JSLibrary ' . $r['name'] . ' is available'; } $results[] = $check; break; case 'library': if (!Core::IsLibraryAvailable($r['name'])) { // The library is not even available! $check['result']['message'] = 'Missing library ' . $r['name']; } elseif (!Core::IsLibraryAvailable($r['name'], $r['version'], $r['operation'])) { // The library is available, but is out of date. $check['result']['available'] = Core::GetLibraryVersion($r['name']); $check['result']['message'] = 'Requires library ' . $r['vstring'] . ', ' . $check['available'] . ' available.'; } else{ $check['result']['passed'] = true; $check['result']['available'] = Core::GetLibraryVersion($r['name']); $check['result']['message'] = 'Library ' . $r['vstring'] . ' is available'; } $results[] = $check; break; case 'phpextension': $v = phpversion($r['name']); $l = extension_loaded($r['name']); if($l === false){ $check['result']['message'] = 'Missing PHP Extension ' . $r['name']; } elseif($r['version'] && !version_compare($v, $r['version'], $r['operation'])){ $check['result']['available'] = $v; $check['result']['message'] = 'Requires PHP Extension ' . $r['vstring'] . ', ' . $check['available'] . ' available.'; } else{ $check['result']['passed'] = true; $check['result']['available'] = $v; $check['result']['message'] = 'PHP Extension ' . $r['vstring'] . ' is available'; } $results[] = $check; break; } } return $results; }
/** * Page that is called to enable a given component. * * Performs all the necessary checks before enable, ie: dependencies from other components. */ public function component_enable() { $view = $this->getView(); $req = $this->getPageRequest(); // This is a json-only page. $view->contenttype = View::CTYPE_JSON; // This is a post-only page! /*if(!$req->isPost()){ $view->error = View::ERROR_BADREQUEST; return; }*/ $name = strtolower($req->getParameter(0)); $dryrun = $req->getParameter('dryrun'); $c = Core::GetComponent($name); if(!$c){ $view->error = View::ERROR_NOTFOUND; return; } if($c instanceof Component){ $view->error = View::ERROR_SERVERERROR; $view->jsondata = array('message' => 'Requested component is not a valid 2.1 version component, please upgrade ' . $name); return; } // Create a reverse map of what components are the basis of which components, this will make it easier // to do the necessary mapping. $provides = array('library' => array(), 'component' => array()); foreach(Core::GetComponents() as $ccheck){ // I only want to look at enabled components. if(!$ccheck->isEnabled()) continue; foreach($ccheck->getProvides() as $p){ $provides[$p['type']][$p['name']] = $p['version']; } } // And check this component's requirements. $requires = $c->getRequires(); foreach($requires as $r){ if(!isset($provides[$r['type']][$r['name']])){ $view->jsondata = array('message' => 'Unable to locate requirement ' . $r['type'] . ' ' . $r['name']); return; } $op = ($r['operation']) ? $r['operation'] : 'ge'; if(!Core::VersionCompare($provides[$r['type']][$r['name']], $r['version'], $op)){ $view->jsondata = array('message' => 'Dependency version for ' . $r['type'] . ' ' . $r['name'] . ' ' . $op . ' ' . $r['version'] . ' not met'); return; } } if(!$dryrun){ // I want to record a list of actual changes performed. $changes = array(); $changes[] = 'Enabling component ' . $c->getName(); $change = $c->enable(); if(is_array($change)) $changes = array_merge($changes, $change); $logmsg = implode("\n", $changes); SystemLogModel::LogSecurityEvent( '/updater/component/enabled', $logmsg ); } $view->jsondata = array('changes' => array($name), 'dryrun' => $dryrun); }
private function _setupComponent(){ $this->_xmlFile = \ComponentFactory::ResolveNameToFile($this->_keyname); if(!$this->_xmlFile){ throw new \Exception('XML file for requested component not found. [' . $this->_keyname . ']'); } // Resolve it to the full path $this->_xmlFile = ROOT_PDIR . $this->_xmlFile; $this->_base = new DirectoryLocal(dirname($this->_xmlFile)); $this->_iterator = new DirectoryIterator($this->_base); $baseDir = $this->_base->getPath(); // Get the XMLLoader object for this file. This will allow me to have more fine-tune control over the file. $this->_xmlLoader = new \XMLLoader(); $this->_xmlLoader->setRootName('component'); if(!$this->_xmlLoader->loadFromFile($this->_xmlFile)){ throw new \Exception('Unable to load XML file ' . $this->_xmlFile); } $this->_iterator->findDirectories = false; $this->_iterator->recursive = true; $this->_iterator->ignores = [ 'component.xml', 'dev/', 'tests/', ]; // @todo Should I support ignored files in the component.xml file? // advantage, developers can have tools in their directories that are not meant to be packaged. // disadvantage, currently no component other than core requires this..... /*$list = $this->getElements('/ignorefiles/file'); foreach($list as $el){ $it->addIgnores($this->getBaseDir() . $el->getAttribute('filename')); }*/ // Not a 2.1 component version?... well it needs to be! // This actually needs to work with a variety of versions. $componentapiversion = $this->_xmlLoader->getDOM()->doctype->systemId; switch($componentapiversion){ case 'http://corepl.us/api/2_1/component.dtd': case 'http://corepl.us/api/2_4/component.dtd': // Now I can load the component itself, now that I know that the metafile is a 2.1 compatible version. $comp = \Core::GetComponent($this->_keyname); // Because the editor may request a component by key name, translate that to the pretty name. $this->_name = $comp->getName(); //$comp = new \Component_2_1($this->_xmlFile); //$comp->load(); break; default: throw new \Exception('Unsupported component version, please ensure that your doctype systemid is correct.'); } $this->_licenses = []; foreach($this->_xmlLoader->getElements('//component/licenses/license') as $el){ /** @var \DOMElement $el */ $url = @$el->getAttribute('url'); $this->_licenses[] = [ 'title' => $el->nodeValue, 'url' => $url ]; } $this->_authors = []; foreach($this->_xmlLoader->getElements('//component/authors/author') as $el){ $this->_authors[] = [ 'name' => $el->getAttribute('name'), 'email' => @$el->getAttribute('email'), ]; } $this->_changelog = new Changelog\Parser($this->_name, $baseDir . 'CHANGELOG'); $this->_gitPaths = [ $baseDir, ]; }
// Resolve onlyComponent and onlyTheme to their appropriate theme, when applicable. if($onlyComponent){ if(Core::GetComponent($onlyComponent) === null){ // Run through and try to find it by keyname or regular name. foreach(Core::GetComponents() as $c){ /** @var Component_2_1 $c */ if($c->getKeyName() == $onlyComponent || $c->getName() == $onlyComponent){ $onlyComponent = $c->getKeyName(); break; } } unset($c); } if(Core::GetComponent($onlyComponent) === null){ \Core\CLI\CLI::PrintError('Unable to locate component ' . $onlyComponent); exit; } } if($onlyTheme){ if(ThemeHandler::GetTheme($onlyTheme) === false){ // Run through and try to find it by keyname or regular name. foreach(ThemeHandler::GetAllThemes() as $t){ /** @var \Theme\Theme $t */ if($t->getKeyName() == $onlyTheme || $t->getName() == $onlyTheme){ $onlyTheme = $c->getKeyName(); break; } }
/** * Display a preview of this file to the browser. Must be an image. * * @param string|int $dimensions A string of the dimensions to create the image at, widthxheight. * Also supports the previous version of simply "dimension", as an int. * @param boolean $includeHeader Include the correct mimetype header or no. */ public function displayPreview($dimensions = "300x300", $includeHeader = true) { $preview = $this->getPreviewFile($dimensions); //var_dump($preview, $this); die(';)'); if ($includeHeader){ header('Content-Disposition: filename="' . $this->getBaseFilename(true) . '-' . $dimensions . '.' . $this->getExtension() . '"'); header('Content-Type: ' . $this->getMimetype()); header('Content-Length: ' . $preview->getFilesize()); header('X-Alternative-Location: ' . $preview->getURL()); header('X-Content-Encoded-By: Core Plus ' . (DEVELOPMENT_MODE ? \Core::GetComponent()->getVersion() : '')); } echo $preview->getContents(); return; }
} */ if($component){ $dir = ROOT_PDIR . 'components/' . $component; $configKey = $component; $comp = Core::GetComponent($component); } elseif($theme){ $dir = ROOT_PDIR . 'themes/' . $theme; $configKey = 'theme/' . $theme; $comp = null; } elseif($core){ $dir = ROOT_PDIR . 'core/'; $configKey = 'core'; $comp = Core::GetComponent('core'); } else{ $dir = null; $configKey = null; $comp = null; } if(!is_dir($dir)){ $arguments->printError('Requested directory does not exist, ' . $dir); $arguments->printUsage(); exit; } // I need to get the list of only PHP, TPL, and XML files. exec("find " . escapeshellarg($dir) . " -type f -regex '.*\\.\\(php\\|tpl\\|xml\\)' -exec egrep -R 'STRING_|MESSAGE_|FORMAT_' {} \\;", $output);
/** * Handle an error and report it to the Core system log. * * @param $errno * @param $errstr * @param $errfile * @param $errline * @param null $errcontext */ function error_handler($errno, $errstr, $errfile, $errline, $errcontext = null){ $type = null; $fatal = false; $code = null; $class = ''; // The exception to this is when error_reporting is explictly set to 0. // This happens when a function is called with the "@" error suppressor. // In this event, I still want to log the error, but simply do not display it on the screen. // Damn f*****g "@" operator..... $suppressed = (error_reporting() === 0); switch($errno){ case E_ERROR: case E_USER_ERROR: $fatal = true; $type = 'error'; $class = 'error'; $code = 'PHP Error'; break; case E_WARNING: case E_USER_WARNING: $type = 'error'; $class = 'warning'; $code = 'PHP Warning'; break; case E_NOTICE: case E_USER_NOTICE: $type = 'info'; $class = 'info'; $code = 'PHP Notice'; break; case E_DEPRECATED: case E_USER_DEPRECATED: $type = 'info'; $class = 'deprecated'; $code = 'PHP Deprecated Notice'; break; case E_STRICT: $type = 'info'; $class = 'warning'; $code = 'PHP Strict Warning'; $suppressed = true; break; default: $type = 'info'; $class = 'unknown'; $code = 'Unknown PHP Error [' . $errno . ']'; break; } if($suppressed){ // Ignore suppressed errors when on production. // This is required because PHP < 7.0 has some functions that can only be called with the '@' operator. // Such as LDAP binding or many things in Smarty. if(!DEVELOPMENT_MODE){ return; } $code .= ' @SUPPRESSED'; } // All errors/warnings/notices get logged! if($errfile && strpos($errfile, ROOT_PDIR) === 0){ $details = '[src: ' . '/' . substr($errfile, strlen(ROOT_PDIR)) . ':' . $errline . '] '; } elseif($errfile){ $details = '[src: ' . $errfile . ':' . $errline . '] '; } else{ $details = ''; } try{ if(!\Core::GetComponent()){ // SQUAK! Core isn't even loaded yet! return; } // Allow external systems to hook into this event. \HookHandler::DispatchHook('/core/error_handler', $code, $errstr); $log = \SystemLogModel::Factory(); $log->setFromArray([ 'type' => $type, 'code' => $code, 'message' => $details . $errstr ]); $log->save(); } catch(\Exception $e){ // meh, try a traditional log. try{ if(class_exists('Core\\Utilities\\Logger\\LogFile')){ $log = new LogFile($type); $log->write($details . $errstr, $code); } else{ error_log($details . $errstr); } } catch(\Exception $e){ // Really meh now! } } // Display all errors when in development mode. if(DEVELOPMENT_MODE && !$suppressed){ // The correct way to handle output is via EXEC_MODE. // HOWEVER, since the unit tests emulate a WEB mode so that the scripts behave as they would in the web browser, // this is not a reliable test here. if(isset($_SERVER['TERM']) || isset($_SERVER['SHELL'])){ print_error_as_text($class, $code, $errstr); } elseif(EXEC_MODE == 'WEB'){ print_error_as_html($class, $code, $errstr); } else{ print_error_as_text($class, $code, $errstr); } } // If it's a fatal error and it's not in development mode, simply display a friendly error page instead. if($fatal){ if(EXEC_MODE == 'WEB'){ require(ROOT_PDIR . 'core/templates/halt_pages/fatal_error.inc.html'); } exit(); } }