? drush-ishacked.patch ? commands/core/.core.drush.inc.swp Index: commands/core/core.drush.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/drush/commands/core/core.drush.inc,v retrieving revision 1.30 diff -u -p -r1.30 core.drush.inc --- commands/core/core.drush.inc 2 Jun 2009 23:48:31 -0000 1.30 +++ commands/core/core.drush.inc 3 Aug 2009 14:19:41 -0000 @@ -100,6 +100,21 @@ function core_drush_command() { 'code' => 'PHP code', ), ); + $items['iscorehacked'] = array( + 'description' => 'Check and see if core is hacked, and save a few kittens.', + ); + $items['ishacked'] = array( + 'description' => 'Check and see if given contrib module is hacked.', + 'examples' => array( + 'drush ishacked content' => 'Checks if CCK has been altered.', + ), + 'arguments' => array( + 'module' => 'Module name', + ), + ); + $items['find_dead_kittens'] = array( + 'description' => 'Alias for iscorehacked' + ); return $items; } @@ -210,6 +225,10 @@ function core_drush_help($section) { return dt("Sync the entire drupal directory or a subdirectory to a using ssh. Excludes .svn directories. Useful for pushing copies of your tree to a staging server, or retrieving a files directory from a remote site. Local paths should be specified relative to Drupal root."); case 'drush:eval': return dt("Run arbitrary PHP code in the context of Drupal"); + case 'drush:iscorehacked': + return dt("Check current site core against stock drupal core to see if it has been modified."); + case 'drush:ishacked': + return dt("Check current site contrib module against stock drupal contrib to see if it has been modified."); case 'error:DRUSH_DRUPAL_DB_ERROR' : $message = dt("Drush was not able to start (bootstrap) the Drupal database.\n"); $message .= dt("Hint: This error often occurs when Drush is trying to bootstrap a site that has not been installed or does not have a configured database.\n"); @@ -439,3 +458,211 @@ function drush_core_watchdog_delete($typ function drush_core_eval($command) { eval($command . ';'); } + +function drush_core_find_dead_kittens() { + return drush_core_iscorehacked(); +} + +function drush_core_ishacked($module) { + $tempdir = preg_replace("%//$%", "/", sys_get_temp_dir() . "/"); + $destination = $tempdir . $module .'-ISHACKED/'; + mkdir($destination); // TODO if exists don't. + _ishacked_fetch_project($destination, $module); + $drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT'); + $source = $drupal_root .'/' . $release['path']; + //_ishacked_check_files($original, $destination . $module); + +} + +function _ishacked_fetch_project($destination, $module) { + // Get the list of installed stuff. + include_once drupal_get_path('module', 'update') .'/update.compare.inc'; + $projects = update_get_projects(); + $project = $projects[$module]; + $name = $project['name']; + $url = 'http://updates.drupal.org/release-history'."/$name/". $project['info']['core']; + + if ($xml = @simplexml_load_file($url)) { + if ($error = $xml->xpath('/error')) { + drush_set_error('DRUSH_PM_COULD_NOT_LOAD_UPDATE_FILE', $error[0]); + } + else { + // Try to get the specified release. + if ($project['info']['version']) { + $xpath = "/project/releases/release[status='published'][version='" . $project['info']['version'] . "']"; + $releases = $xml->xpath($xpath); + if (!empty($releases)) { + $release = (array)$releases[0]; + } + else { + drush_die(dt("Could not locate specified project version."), 'notice'); + } + } + } + } + drush_include_engine('package_handler', drush_get_option('package-handler', 'wget')); + if (!package_handler_install_project($module, $release, $destination)) { + drush_die(dt('Could not fetch contributed module.'), 'error'); + } +} + +function _ishacked_check_files($original, $installed) { + // Open the uncompressed directory. + $destination .= "/$module"; + + $fp = opendir($destination); + $hackedfiles = array(); + $dirstack = array(); + while (($filename = readdir($fp)) !== FALSE) { + _drush_core_ishacked_process($drupal_root, $destination, $filename, $dirstack, $hackedfiles); + } + if (count($hackedfiles)) { + $drush_msg = 'Module is likely hacked! The following files did not match:'."\n". implode("\n", $hackedfiles); + foreach ($hackedfiles as $hackedfile) { + $src = escapeshellarg($destination . '/' . $hackedfile); + $target = escapeshellarg($drupal_root .'/'. $hackedfile); + $output = `diff -Nau $src $target`; + $drush_msg .= $output; + } + drush_die($drush_msg); + } +} + +function _drush_core_ishacked_process($drupal_root, $destination, $filename, &$dirstack, &$hackedfiles) { + if ($filename == '.' || $filename == '..' || $filename == '.htaccess' || $filename == 'default.settings.php') { + // skip + return; + } + $dirs = implode('/', $dirstack); + $fullpath = preg_replace('%//$%', '/', $destination . '/' . $dirs .'/') . $filename; + $sitefullpath = preg_replace('%//$%', '/', $drupal_root . '/' . $dirs . '/') . $filename; + + if (is_dir($fullpath)) { + array_push($dirstack, $filename); + $fp = opendir($fullpath); + while (($filename = readdir($fp)) !== FALSE) { + _drush_core_ishacked_process($drupal_root, $destination, $filename, $dirstack, $hackedfiles); + } + array_pop($dirstack); + } else { + $coresum = md5_file($fullpath); + $rootsum = md5_file($sitefullpath); + if ($coresum != $rootsum) { + $hackedfiles[] = preg_replace("%^/%", "", $dirs . '/' . $filename); + } + } +} + +function drush_core_iscorehacked() { + $command = drush_get_command(); + $tempdir = preg_replace("%//$%", "/", sys_get_temp_dir() . "/"); + $destination = $tempdir .'DRUPAL-ISCOREHACKED'; + mkdir($destination); + $drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT'); + + drush_include_engine('package_handler', drush_get_option('package-handler', 'wget')); + $requestdata = pm_parse_project_version(array('drupal-'. VERSION)); + $release = NULL; + foreach ($requestdata as $package) { + $project = $package['name']; + $url = 'http://updates.drupal.org/release-history'."/$project/". $package['drupal_version']; + if ($xml = @simplexml_load_file($url)) { + if ($error = $xml->xpath('/error')) { + drush_set_error('DRUSH_PM_COULD_NOT_LOAD_UPDATE_FILE', $error[0]); + } + else { + // Try to get the specified release. + if ($package['version']) { + if ($releases = $xml->xpath("/project/releases/release[status='published'][version='" . $package['version'] . "']")) { + $release = (array)$releases[0]; + } + if (empty($release)) { + drush_die(dt("Could not locate specified project version."), 'notice'); + } + } + } + } + } + if (!package_handler_install_project('drupal', $release, $destination)) { + drush_die(dt('could not fetch drupal core')); + } + + // uncompress the tarball + $destination .= '/drupal-'. VERSION; + + $fp = opendir($destination); + $hackedfiles = array(); + $dirstack = array(); + while (($filename = readdir($fp)) !== FALSE) { + _drush_core_iscorehacked_process($drupal_root, $destination, $filename, $dirstack, $hackedfiles); + } + if (count($hackedfiles)) { + $drush_msg = 'Core is likely hacked! The following files did not match:'."\n". implode("\n", $hackedfiles); + foreach ($hackedfiles as $hackedfile) { + $src = escapeshellarg($destination . '/' . $hackedfile); + $target = escapeshellarg($drupal_root .'/'. $hackedfile); + $output = `diff -Nau $src $target`; + $drush_msg .= $output; + } + drush_die($drush_msg); + } +} + +function _drush_core_iscorehacked_process($drupal_root, $destination, $filename, &$dirstack, &$hackedfiles) { + if ($filename == '.' || $filename == '..' || $filename == '.htaccess' || $filename == 'default.settings.php') { + // skip + return; + } + $dirs = implode('/', $dirstack); + $fullpath = preg_replace('%//$%', '/', $destination . '/' . $dirs .'/') . $filename; + $sitefullpath = preg_replace('%//$%', '/', $drupal_root . '/' . $dirs . '/') . $filename; + + if (is_dir($fullpath)) { + array_push($dirstack, $filename); + $fp = opendir($fullpath); + while (($filename = readdir($fp)) !== FALSE) { + _drush_core_iscorehacked_process($drupal_root, $destination, $filename, $dirstack, $hackedfiles); + } + array_pop($dirstack); + } else { + $coresum = md5_file($fullpath); + $rootsum = md5_file($sitefullpath); + if ($coresum != $rootsum) { + $hackedfiles[] = preg_replace("%^/%", "", $dirs . '/' . $filename); + } + } +} + + +// support sys_get_temp_dir for php 4 +if (!function_exists('sys_get_temp_dir')) { + // Based on http://www.phpit.net/ + // article/creating-zip-tar-archives-dynamically-php/2/ + function sys_get_temp_dir() { + // Try to get from environment variable + if (!empty($_ENV['TMP'])) { + return realpath($_ENV['TMP']); + } + else if (!empty($_ENV['TMPDIR'])) { + return realpath($_ENV['TMPDIR']); + } + else if (!empty($_ENV['TEMP'])) { + return realpath( $_ENV['TEMP']); + } + // Detect by creating a temporary file + else { + // Try to use system's temporary directory + // as random name shouldn't exist + $temp_file = tempnam( md5(uniqid(rand(), TRUE)), '' ); + if ($temp_file) { + $temp_dir = realpath( dirname($temp_file) ); + unlink( $temp_file ); + return $temp_dir; + } + else { + return FALSE; + } + } + } +} +