add('name', PLUGIN_EVENT_SPARTACUS_NAME);
$propbag->add('description', PLUGIN_EVENT_SPARTACUS_DESC);
$propbag->add('stackable', false);
$propbag->add('author', 'Garvin Hicking');
$propbag->add('version', '2.9');
$propbag->add('requirements', array(
'serendipity' => '0.9',
'smarty' => '2.6.7',
'php' => '4.1.0'
));
$propbag->add('event_hooks', array(
'backend_plugins_fetchlist' => true,
'backend_plugins_fetchplugin' => true,
'backend_templates_fetchlist' => true,
'backend_templates_fetchtemplate' => true,
'backend_pluginlisting_header' => true,
'backend_pluginlisting_header_upgrade' => true
));
$propbag->add('groups', array('BACKEND_FEATURES'));
$propbag->add('configuration', array('mirror_xml', 'mirror_files', 'chown', 'chmod_files', 'chmod_dir'));
}
function generate_content(&$title) {
$title = $this->title;
}
function cleanup() {
global $serendipity;
// Purge DB cache
serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}pluginlist");
serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}plugincategories");
// Purge cached XML files.
$files = serendipity_traversePath($serendipity['serendipityPath'] . PATH_SMARTY_COMPILE, '', false, '/package_.+\.xml$/');
if (!is_array($files)) {
return false;
}
foreach ($files as $file) {
printf(DELETING_FILE . '
', $file['name']);
@unlink($serendipity['serendipityPath'] . PATH_SMARTY_COMPILE . '/' . $file['name']);
}
}
function &getMirrors($type = 'xml', $loc = false) {
static $mirror = array(
'xml' => array(
'Netmirror.org',
's9y.org'
),
'files' => array(
'Netmirror.org',
'SourceForge.net',
's9y.org',
'BerliOS.de'
)
);
static $http = array(
'xml' => array(
'http://netmirror.org/mirror/serendipity/',
'http://s9y.org/mirror/'
),
'files' => array(
'http://netmirror.org/mirror/serendipity/',
'http://php-blog.cvs.sourceforge.net/*checkout*/php-blog/',
'http://s9y.org/mirror/',
'http://svn.berlios.de/viewcvs/serendipity/'
)
);
if ($loc) {
return $http[$type];
} else {
return $mirror[$type];
}
}
function introspect_config_item($name, &$propbag) {
global $serendipity;
switch($name) {
case 'chmod_files':
$propbag->add('type', 'string');
$propbag->add('name', PLUGIN_EVENT_SPARTACUS_CHMOD);
$propbag->add('description', PLUGIN_EVENT_SPARTACUS_CHMOD_DESC);
$propbag->add('default', '');
break;
case 'chmod_dir':
$propbag->add('type', 'string');
$propbag->add('name', PLUGIN_EVENT_SPARTACUS_CHMOD_DIR);
$propbag->add('description', PLUGIN_EVENT_SPARTACUS_CHMOD_DIR_DESC);
$propbag->add('default', '');
break;
case 'chown':
$propbag->add('type', 'string');
$propbag->add('name', PLUGIN_EVENT_SPARTACUS_CHOWN);
$propbag->add('description', PLUGIN_EVENT_SPARTACUS_CHOWN_DESC);
$propbag->add('default', '');
break;
case 'mirror_xml':
$propbag->add('type', 'select');
$propbag->add('name', PLUGIN_EVENT_SPARTACUS_MIRROR_XML);
$propbag->add('description', PLUGIN_EVENT_SPARTACUS_MIRROR_DESC);
$propbag->add('select_values', $this->getMirrors('xml'));
$propbag->add('default', 0);
break;
case 'mirror_files':
$propbag->add('type', 'select');
$propbag->add('name', PLUGIN_EVENT_SPARTACUS_MIRROR_FILES);
$propbag->add('description', PLUGIN_EVENT_SPARTACUS_MIRROR_DESC);
$propbag->add('select_values', $this->getMirrors('files'));
$propbag->add('default', 0);
break;
default:
return false;
}
return true;
}
function GetChildren(&$vals, &$i) {
$children = array();
$cnt = sizeof($vals);
while (++$i < $cnt) {
// compare type
switch ($vals[$i]['type']) {
case 'cdata':
$children[] = $vals[$i]['value'];
break;
case 'complete':
$children[] = array(
'tag' => $vals[$i]['tag'],
'attributes' => $vals[$i]['attributes'],
'value' => $vals[$i]['value']
);
break;
case 'open':
$children[] = array(
'tag' => $vals[$i]['tag'],
'attributes' => $vals[$i]['attributes'],
'value' => $vals[$i]['value'],
'children' => $this->GetChildren($vals, $i)
);
break;
case 'close':
return $children;
}
}
}
// Create recursive directories; begins at serendipity plugin root folder level
function rmkdir($dir, $sub = 'plugins') {
global $serendipity;
$spaths = explode('/', $serendipity['serendipityPath'] . $sub . '/');
$paths = explode('/', $dir);
$stack = '';
foreach($paths AS $pathid => $path) {
$stack .= $path . '/';
if ($spaths[$pathid] == $path) {
continue;
}
if (!is_dir($stack) && !mkdir($stack)) {
return false;
} else {
$this->fileperm($stack, true);
}
}
return true;
}
// Apply file permission settings.
function fileperm($stack, $is_dir) {
$chmod_dir = intval($this->get_config('chmod_dir'), 8);
$chmod_files = intval($this->get_config('chmod_files'), 8);
$chown = $this->get_config('chown');
if ($is_dir && !empty($chmod_dir) && function_exists('chmod')) {
@chmod($stack, $chmod_dir); // Always ensure directory traversal.
}
if (!$is_dir && !empty($chmod_files) && function_exists('chmod')) {
@chmod($stack, $chmod_files); // Always ensure directory traversal.
}
if (!empty($chown) && function_exists('chown')) {
$own = explode('.', $chown);
if (isset($own[1])) {
@chgrp($stack, $own[1]);
}
@chown($stack, $own[0]);
}
return true;
}
function &fetchfile($url, $target, $cacheTimeout = 0, $decode_utf8 = false, $sub = 'plugins') {
static $error = false;
// Fix double URL strings.
$url = str_replace('http:/', 'http://', str_replace('//', '/', $url));
// --JAM: Get the URL's IP in the most error-free way possible
$url_parts = @parse_url($url);
$url_hostname = 'localhost';
if (is_array($url_parts)) {
$url_hostname = $url_parts['host'];
}
$url_ip = gethostbyname($url_hostname);
printf(PLUGIN_EVENT_SPARTACUS_FETCHING, '' . basename($url) . '');
echo '
';
if (file_exists($target) && filesize($target) > 0 && filemtime($target) >= (time()-$cacheTimeout)) {
$data = file_get_contents($target);
printf(PLUGIN_EVENT_SPARTACUS_FETCHED_BYTES_CACHE, strlen($data), $target);
echo '
';
} else {
require_once S9Y_PEAR_PATH . 'HTTP/Request.php';
$options = array();
serendipity_plugin_api::hook_event('backend_http_request', $options, 'spartacus');
serendipity_request_start();
$req = &new HTTP_Request($url, $options);
if (PEAR::isError($req->sendRequest()) || $req->getResponseCode() != '200') {
$resolved_url = $url . ' (at IP ' . $url_ip . ')';
printf(PLUGIN_EVENT_SPARTACUS_FETCHERROR, $resolved_url);
//--JAM: START FIREWALL DETECTION
if ($req->getResponseCode()) {
printf(PLUGIN_EVENT_SPARTACUS_REPOSITORY_ERROR, $req->getResponseCode());
}
$check_health = true;
if (function_exists('curl_init')) {
echo PLUGIN_EVENT_SPARTACUS_TRYCURL . "\n";
$curl_handle=curl_init();
curl_setopt($curl_handle, CURLOPT_URL, $url);
curl_setopt($curl_handle, CURLOPT_HEADER, 0);
$data = curl_exec($curl_handle);
curl_close($curl_handle);
if ($curl_result) {
$check_health = false;
} else {
echo PLUGIN_EVENT_SPARTACUS_CURLFAIL . "\n";
}
}
}
if ($check_health) {
echo PLUGIN_EVENT_SPARTACUS_HEALTHCHECK . "\n";
/*--JAM: Useful for later, when we have a health monitor for SPARTACUS
$propbag = new serendipity_property_bag;
$this->introspect($propbag);
$health_url = 'http://spartacus.s9y.org/spartacus_health.php?version=' . $propbag->get('version');
*/
$health_url = $url;
$matches = array();
preg_match('#http://[^/]*/#', $url, $matches);
if ($matches[0]) {
$health_url = $matches[0];
}
$health_options = $options;
serendipity_plugin_api::hook_event('backend_http_request', $health_options, 'spartacus_health');
$health_req = &new HTTP_Request($health_url, $health_options);
$health_result = $health_req->sendRequest();
if (PEAR::isError($health_result))
{
$fp = @fsockopen('www.google.com', 80, $errno, $errstr);
if (!$fp) {
printf(PLUGIN_EVENT_SPARTACUS_HEALTHBLOCKED, $errno, $errstr);
} else {
echo PLUGIN_EVENT_SPARTACUS_HEALTHDOWN;
printf(PLUGIN_EVENT_SPARTACUS_HEALTHLINK, $health_url);
fclose($fp);
}
} else if ($health_req->getResponseCode() != '200') {
printf(PLUGIN_EVENT_SPARTACUS_HEALTHERROR, $health_req->getResponseCode());
printf(PLUGIN_EVENT_SPARTACUS_HEALTHLINK, $health_url);
} else {
//--JAM: Parse response and display it.
}
//--JAM: END FIREWALL DETECTION
if (file_exists($target) && filesize($target) > 0) {
$data = file_get_contents($target);
printf(PLUGIN_EVENT_SPARTACUS_FETCHED_BYTES_CACHE, strlen($data), $target);
echo '
';
}
} else {
// Fetch file
if (!$data) {
$data = $req->getResponseBody();
}
printf(PLUGIN_EVENT_SPARTACUS_FETCHED_BYTES_URL, strlen($data), $target);
echo '
';
$tdir = dirname($target);
if (!is_dir($tdir) && !$this->rmkdir($tdir, $sub)) {
printf(FILE_WRITE_ERROR, $tdir);
echo '
';
return $error;
}
$fp = @fopen($target, 'w');
if (!$fp) {
printf(FILE_WRITE_ERROR, $target);
echo '
';
return $error;
}
if ($decode_utf8) {
$data = str_replace('', '', $data);
$this->decode($data, true);
}
fwrite($fp, $data);
fclose($fp);
$this->fileperm($target, false);
echo PLUGIN_EVENT_SPARTACUS_FETCHED_DONE;
echo '
';
$this->purgeCache = true;
}
serendipity_request_end();
}
return $data;
}
function decode(&$data, $force = false) {
// xml_parser_* functions to recoding from ISO-8859-1/UTF-8
if ($force === false && (LANG_CHARSET == 'ISO-8859-1' || LANG_CHARSET == 'UTF-8')) {
return true;
}
switch (strtolower(LANG_CHARSET)) {
case 'utf-8':
// The XML file is UTF-8 format. No changes needed.
break;
case 'iso-8859-1':
$data = utf8_decode($data);
break;
default:
if (function_exists('iconv')) {
$data = iconv('UTF-8', LANG_CHARSET, $data);
} elseif (function_exists('recode')) {
$data = recode('utf-8..' . LANG_CHARSET, $data);
}
break;
}
}
function &fetchOnline($type, $no_cache = false) {
global $serendipity;
switch($type) {
// Sanitize to not fetch other URLs
default:
case 'event':
$url_type = 'event';
$i18n = true;
break;
case 'sidebar':
$url_type = 'sidebar';
$i18n = true;
break;
case 'template':
$url_type = 'template';
$i18n = false;
break;
}
if (!$i18n) {
$lang = '';
} elseif (isset($serendipity['languages'][$serendipity['lang']])) {
$lang = '_' . $serendipity['lang'];
} else {
$lang = '_en';
}
$mirrors = $this->getMirrors('xml', true);
$mirror = $mirrors[$this->get_config('mirror_xml', 0)];
$url = $mirror . '/package_' . $url_type . $lang . '.xml';
$cacheTimeout = 60*60*12; // XML file is cached for half a day
$target = $serendipity['serendipityPath'] . PATH_SMARTY_COMPILE . '/package_' . $url_type . $lang . '.xml';
$xml = $this->fetchfile($url, $target, $cacheTimeout, true);
echo '
';
$new_crc = md5($xml);
$last_crc = $this->get_config('last_crc_' . $url_type);
if (!$no_cache && !$this->purgeCache && $last_crc == $new_crc) {
$out = 'cached';
return $out;
}
// XML functions
$xml_string = '';
if (preg_match('@(<\?xml.+\?>)@imsU', $xml, $xml_head)) {
$xml_string = $xml_head[1];
}
$encoding = 'UTF-8';
if (preg_match('@encoding="([^"]+)"@', $xml_string, $xml_encoding)) {
$encoding = $xml_encoding[1];
}
preg_match_all('@(