add('name', PLUGIN_EVENT_AUTOUPDATE_NAME); $propbag->add('description', PLUGIN_EVENT_AUTOUPDATE_DESC); $propbag->add('stackable', false); $propbag->add('author', 'onli, Ian'); $propbag->add('version', '0.8'); $propbag->add('requirements', array( 'serendipity' => '0.8', 'php' => '5.1' )); $propbag->add('event_hooks', array('plugin_dashboard_updater' => true, 'backend_sidebar_entries_event_display_update' => true)); $propbag->add('groups', array('BACKEND_FEATURES')); $this->dependencies = array('serendipity_event_dashboard' => 'keep'); } function generate_content(&$title) { $title = $this->title; } function install() { global $serendipity; if (!$serendipity['serendipityUserlevel'] >= USERLEVEL_ADMIN) { return false; } } /*function introspect_config_item($name, &$propbag) { }*/ /** * flush progress or error messages */ function show_message($message='', $pname='', $next='') { if(!empty($pname)) { // Total processes $total = 3; // fake processing loop for($i=1; $i<=$total; $i++){ // Calculate the percentation $percent = intval($i/$total * 100)."%"; // Javascript for updating the progress bar and information echo ' '; // Send output to browser immediately flush(); // Sleep one second so we can see the delay sleep(1); } $wait = strstr($pname, 'Function') ? ' Please wait ... Processing: '.$next.' ...' : ''; // no tags allowed here // Tell user that the process is completed echo ''; } echo "$message\n"; $levels = ob_get_level(); for ($i=0; $i<$levels; $i++) { ob_end_flush(); } flush(); } function event_hook($event, &$bag, &$eventData, $addData = null) { global $serendipity; $hooks = &$bag->get('event_hooks'); if (isset($hooks[$event])) { switch($event) { case 'plugin_dashboard_updater': $eventData = '
'; return true; break; case 'backend_sidebar_entries_event_display_update': if (! (serendipity_checkPermission('siteConfiguration') || serendipity_checkPermission('blogConfiguration'))) { return; } @ini_set('max_execution_time', 210); // 180 + (21+9 gui happenings) #@ini_set('memory_limit', '-1'); // extending memory_limit may be prevented by suhosin extension. /* As long scripts are not running within safe_mode they are free to change the memory_limit to whatever value they want. Suhosin changes this fact and disallows setting the memory_limit to a value greater than the one the script started with, when this option is left at 0. A value greater than 0 means that Suhosin will disallows scripts setting the memory_limit to a value above this configured hard limit. This is for example usefull if you want to run the script normaly with a limit of 16M but image processing scripts may raise it to 20M. Edit /etc/php5/conf.d/suhosin.ini and add e.g. suhosin.memory_limit = 512M ... */ $self_info = sprintf(USER_SELF_INFO, htmlspecialchars($serendipity['serendipityUser']), $serendipity['permissionLevels'][$serendipity['serendipityUserlevel']]); $lang_char = LANG_CHARSET; $ad_suite = SERENDIPITY_ADMIN_SUITE; $css_upd = file_get_contents(dirname(__FILE__) . '/upgrade.min.css'); $nv = htmlspecialchars($_REQUEST['serendipity']['newVersion']); // reduce to POST only? $logmsg = ''; echo << Startpage - {$ad_suite}

Serendipity Auto-Upgrade Processor

EOS; $this->show_message('

Download, verify, check, unzip, copy, remove temporary stuff for Serendipity Update: ' . $_REQUEST['serendipity']['newVersion'] . ' may take a little while...
Please don\'t get nervous and do not close this page while in progress!


'); $this->show_message('

Please note: If this page ever stops with an error message during procession, you can normally just RELOAD your browser [by keyboard shortcut] to get another run. This does not do any harm to a continued upgrade.

'); $this->show_message('

PHP max execution time set to 210 seconds

'); $start = microtime(true); if(false === ($update = $this->fetchUpdate($nv))) { $this->close_page(true); } usleep(1); $time = microtime(true) - $start; $logmsg .= $lmsg = sprintf("In %0.4d seconds run fcn fetchUpdate()...\n", $time); // print in readable format 1.2345 $this->show_message('

'.$lmsg.'

', 'Function fetch update', 'verify the update pack'); if (! empty($update)) { $start = microtime(true); if ($this->verifyUpdate($update, $nv)) { usleep(1); $time = microtime(true) - $start; $logmsg .= $lmsg = sprintf("In %0.4d seconds run fcn verifyUpdate()...\n", $time); // print in readable format 1.2345 $this->show_message('

'.$lmsg.'

', 'Function verify update', 'checking write permissions'); $start = microtime(true); if ($this->checkWritePermissions($update)) { usleep(1); $time = microtime(true) - $start; $logmsg .= $lmsg = sprintf("In %0.4d seconds run fcn checkWritePermissions()...\n", $time); // print in readable format 1.2345 $this->show_message('

'.$lmsg.'

', 'Function check write permissions', 'unpacking the update'); $start = microtime(true); $unpacked = $this->unpackUpdate($nv); usleep(1); $time = microtime(true) - $start; $logmsg .= $lmsg = sprintf("In %0.4d seconds run fcn unpackUpdate()...\n", $time); // print in readable format 1.2345 $this->show_message('

'.$lmsg.'

', 'Function unpack update', 'checking integrity'); if ($unpacked) { $start = microtime(true); if ($this->checkIntegrity($nv)) { usleep(1); $time = microtime(true) - $start; $logmsg .= $lmsg = sprintf("In %0.4d seconds run fcn checkIntegrity()...\n", $time); // print in readable format 1.2345 $this->show_message('

'.$lmsg.'

', 'Function check integrity', 'finally copy update'); $start = microtime(true); $copied = $this->copyUpdate($nv); usleep(1); $time = microtime(true) - $start; $logmsg .= $lmsg = sprintf("In %0.4d seconds run fcn copyUpdate()...\n", $time); // print in readable format 1.2345 $this->show_message('

'.$lmsg.'

', 'Function copy update', 'cleaning up temporary directory'); if ($copied) { $start = microtime(true); if (true === $this->cleanTemplatesC($nv, true) ) { $this->show_message('

Cleanup download temp done!

'); } usleep(1); $time = microtime(true) - $start; $logmsg .= $lmsg = sprintf("In %0.4d seconds run fcn cleanTemplatesC()...\n", $time); // print in readable format 1.2345 $this->show_message('

'.$lmsg.'

', 'Function cleanup templates_c', 'finish processing unit'); sleep(2); echo ''; sleep(2); $this->show_message('

click to start Serendipity Installer here!

'); sleep(1); $this->doUpdate();//$logmsg } else { $this->show_message('

Copying the files for the update failed

'); } } else { $this->showChecksumErrors($nv); echo '
'; } } else { $this->show_message('

Unpacking the update failed

'); if (true === $this->cleanTemplatesC($nv, false) ) { $this->show_message('

Cleaning up the failed unpack directory!

'); } $this->show_message('

Please reload this page by F5 to have another try upgrading your Blog successfully!

'); } } else { $this->showNotWriteable($update); echo '
'; } } } $this->close_page(true); return true; break; default: return false; } } else { return false; } } /** * fetch the zip file from server * @param string version * @return mixed updatepath/bool */ function fetchUpdate($version) { global $serendipity; $url = (string)"http://prdownloads.sourceforge.net/php-blog/serendipity-$version.zip?download"; $update = (string)$serendipity ['serendipityPath'] . 'templates_c/' . "serendipity-$version.zip"; // do we already have it? $done = !file_exists($update) ? @copy($url, $update ) : true; if (! $done) { //try it again with curl if copy was forbidden if (function_exists('curl_init')) { $out = @fopen($file, 'wb'); $ch = @curl_init(); @curl_setopt($ch, CURLOPT_FILE, $out); @curl_setopt($ch, CURLOPT_HEADER, 0); @curl_setopt($ch, CURLOPT_URL, $update); $success = @curl_exec($ch); if ( !$success ) { $this->show_message('

Downloading update failed

'); return false; } } } $this->show_message('

Fetch download to templates_c done

'); return $update; } /** * compare the md5 of downloaded archive with the md5 posted on the downloadpage * @param string updatePath * @param string version * @return boolean */ function verifyUpdate($update, $version) { $url = (string)"http://prdownloads.sourceforge.net/php-blog/serendipity-$version.zip?download"; $updatePage = (string)$this->getPage("http://www.s9y.org/12.html"); $downloadLink = substr($updatePage, strpos($updatePage, $url), 200); $found = array(); // grep the checksum preg_match("/\(MD5: (.*)\)/", $downloadLink, $found); $checksum = $found[1]; $check = md5_file($update); if ($check == $checksum) { return true; } else { $this->show_message('

Error. Could not verify the update.

'); return false; } } /** * get file content of updatePage * @param string url * @return page content */ function getPage($url) { $page = file_get_contents($url); if ( empty($page) ) { //try it again with curl if fopen was forbidden if (function_exists('curl_init')) { $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_TIMEOUT, "20"); $page = curl_exec($ch); curl_close($ch); } } return $page; } /** * unpack the update to templates_c * @param string version * @return boolean */ function unpackUpdate($version) { global $serendipity; $update = (string)$serendipity['serendipityPath'] . 'templates_c/' . "serendipity-$version.zip"; $updateDir = (string)$serendipity['serendipityPath'] . 'templates_c/' . "serendipity-$version/"; // do we already have it? if (is_dir($updateDir) && is_file($updateDir . '/serendipity/README.markdown') && is_file($updateDir . '/serendipity/checksums.inc.php')) { return true; } $zip = new ZipArchive; if ($zip->open($update) === true) { // 1.get all filenames apart from the root 'serendipity' $i=1; $files = array(); $name = $zip->getNameIndex($i); while (!empty($name)) { $files[] = $name; $name = $zip->getNameIndex($i); $i+=1; } // 2.extraxt all files to temp $zip->extractTo($updateDir); $this->show_message('

Extracting the zip in templates_c done

'); $zip->close(); } else { return false; } return true; } /** * copy the update from templates_c over the existing files * @param string version * @return boolean */ function copyUpdate($version) { global $serendipity; $update = (string)$serendipity['serendipityPath'] . 'templates_c/' . "serendipity-$version.zip"; $updateDir = (string)$serendipity['serendipityPath'] . 'templates_c/' . "serendipity-$version/"; $zip = new ZipArchive; if ($zip->open($update) === true) { // 1.get all filenames apart from the root 'serendipity' $i=1; $files = array(); $name = $zip->getNameIndex($i); while ( !empty($name) ) { $files[] = $name; $name = $zip->getNameIndex($i); $i+=1; } $zip->close(); // 2. copy them over foreach ($files as $file) { $target = $serendipity['serendipityPath'] . substr($file, 12); if (is_dir($updateDir .$file)) { if (!file_exists($target)) { $success = mkdir($target); } else { $success = true; } } else { $success = @copy($updateDir . $file, $target); } if ( !$success ) { $this->show_message('

Error copying file to '.$target.'

'); return false; } } } else { return false; } return true; } /** * check write permissions * @param string updatePath * @return boolean */ function checkWritePermissions($update) { global $serendipity; $zip = new ZipArchive; if ($zip->open($update) === true) { $i=0; $files = array(); $name = $zip->getNameIndex($i); while (!empty($name)) { $files[] = $name; $name = $zip->getNameIndex($i); $i+=1; } foreach ($files as $file) { $target = $serendipity['serendipityPath'] . substr($file, 12); if ( (! is_writable($target)) && file_exists($target) ) { return false; } } } return true; } /** * show not writable * @param string updatePath * @return error */ function showNotWriteable($update) { global $serendipity; $zip = new ZipArchive; if ($zip->open($update) === true) { $i=0; $files = array(); $name = $zip->getNameIndex($i); while (!empty($name)) { $files[] = $name; $name = $zip->getNameIndex($i); $i+=1; } $notWritable = array(); foreach ($files as $file) { $target = $serendipity['serendipityPath'] . substr($file, 12); if ((! is_writable($target)) && file_exists($target) ) { $notWriteable[] = $target; } } } ob_start(); echo '

Unpacking the update failed, because following files were not writeable:

'; echo "
    "; foreach ($notWriteable as $file) { echo "
  • $file
  • "; } echo "
"; $write_error = ob_get_contents(); ob_end_clean(); $this->show_message($write_error); } /** * checks updates checksum file array with updates realfiles * @param string version * @return boolean */ function checkIntegrity($version) { global $serendipity; $updateDir = (string)$serendipity['serendipityPath'] . 'templates_c/' . "serendipity-$version/"; $checksumFile = (string)$updateDir . "serendipity/checksums.inc.php"; include_once $checksumFile; $checksums = $serendipity['checksums_' . $version]; foreach ($checksums as $file => $checksum) { $check = serendipity_FTPChecksum($updateDir . "serendipity/" . $file); if ($checksum != $check) { return false; } } return true; } /** * checks updates checksum file array with updates realfiles * @param string version * @return error */ function showChecksumErrors($version) { global $serendipity; $updateDir = (string)$serendipity['serendipityPath'] . 'templates_c/' . "serendipity-$version/"; $checksumFile = (string)$updateDir . "serendipity/checksums.inc.php"; include_once $checksumFile; $checksums = $serendipity['checksums_' . $version]; $errors = array(); foreach ($checksums as $file => $checksum) { $check = serendipity_FTPChecksum($updateDir . "serendipity/" . $file); if ($checksum != $check) { $errors[] = $updateDir . "serendipity/" . $file; } } ob_start(); echo '

Updating failed, because the integrity-test for the following files failed:

'; echo "
    "; foreach ($errors as $file) { echo "
  • $file
  • "; } echo "
"; $integrity_error = ob_get_contents(); ob_end_clean(); $this->show_message($integrity_error); } /** * close the autoupdate progress page * @param bool 007 title ;-) */ function close_page($terminate = false) { echo <<
EOS; if($terminate) die(); } /** * visit the rootpage to manually start the update installer process * @param string update messages * @return refresh page */ function doUpdate() { global $serendipity; $msg = "Autoupdate successfully done!\nWe now refresh to Serendipity Installer!\n"; $this->show_message('

Autoupdate successfully done - refresh to Serendipity Installer

', 'Autoupdate'); $this->close_page(); // this is working for me.... is it for you? if(die("\n")) { return; } else { if(!headers_sent()) { if(header('Location: http://' . $_SERVER['HTTP_HOST'] . $serendipity['serendipityHTTPPath'])) exit; } else { echo ''."\n"; echo ''; exit; } } } /** * empty a directory using the Standard PHP Library (SPL) iterator * @param string directory */ function empty_dir($dir) { $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir), RecursiveIteratorIterator::CHILD_FIRST); foreach ($iterator as $file) { if ($file->isFile()) { unlink($file->__toString()); } else { @rmdir($file->__toString()); } } } /** * delete all cache-files in cache templates_c to prevent display-errors after update * @param string version * @return boolean */ function cleanTemplatesC($version, $finish) { global $serendipity; $zip = (string)$serendipity['serendipityPath'] . 'templates_c/' . "serendipity-$version.zip"; $zipDir = (string)$serendipity['serendipityPath'] . 'templates_c/' . "serendipity-$version"; // leave rm zip untouched here as not causing any errors #unlink($zip);// if(unlink($zip)) { else error note? #$this->show_message('

Removing the zip file in templates_c done

'); // As trying to remove a directory that php is still using, we use open/closedir($handle) to be sure if( $handle = opendir($zipDir) ) { $this->empty_dir($zipDir); $this->show_message('

Removing all files in '.$zipDir.' done

'); closedir($handle); } if(rmdir($zipDir)) { $this->show_message('

Removing the empty directory: '.$zipDir.' done

'); } else { $this->show_message('

Removing the empty directory: '.$zipDir.' failed!

'); } // We clear all compiles smarty template files in templates_c which only leaves the page we are on: /serendipity/templates/default/admin/index.tpl if ($finish) { // We have to reduce this call() = all tpl files, to clear the blogs template only, to not have the following automated recompile, force the servers memory // to get exhausted, when using serendipity_event_gravatar plugin, which can eat up some MB... if(method_exists($serendipity['smarty'], 'clearCompiledTemplate')) { // SMARTY 3 if($serendipity['smarty']->clearCompiledTemplate(null, $serendipity['template'])) { return true; } } if(method_exists($serendipity['smarty'], 'clear_compiled_tpl')) { // SMARTY 2 if($serendipity['smarty']->clear_compiled_tpl(null, $serendipity['template'])) { return true; } } } } /** * debug * @param string msg */ function debugMsg($msg) { global $serendipity; $this->debug_fp = @fopen ( $serendipity ['serendipityPath'] . 'templates_c/autoupdate.log', 'a' ); if (! $this->debug_fp) { return false; } if (empty ( $msg )) { fwrite ( $this->debug_fp, "failure \n" ); } else { fwrite ( $this->debug_fp, print_r ( $msg, true ) ); } fclose ( $this->debug_fp ); } } /* vim: set sts=4 ts=4 expandtab : */