View source
<?php
class hackedProject {
var $name = '';
var $project_info = array();
var $remote_files_downloader;
var $remote_files;
var $local_files;
var $project_type = '';
var $existing_version = '';
var $result = array();
var $project_identified = FALSE;
var $remote_downloaded = FALSE;
var $remote_hashed = FALSE;
var $local_hashed = FALSE;
function hackedProject($name) {
$this->name = $name;
$this->remote_files_downloader = new hackedProjectWebFilesDownloader($this);
}
function title() {
$this
->identify_project();
return $this->project_info['title'];
}
function identify_project() {
if (!empty($this->project_identified)) {
return;
}
$available = update_get_available(TRUE);
$data = update_calculate_project_data($available);
foreach ($data as $key => $project) {
if ($key == $this->name) {
$this->project_info = $project;
if (module_exists('cvs_deploy')) {
foreach ($project['includes'] as $name => $title) {
if (is_dir(drupal_get_path($project['project_type'], $name) . '/CVS')) {
$this->remote_files_downloader = new hackedProjectWebCVSDownloader($this, drupal_get_filename($project['project_type'], $name));
break;
}
}
}
$this->project_identified = TRUE;
$this->existing_version = $this->project_info['existing_version'];
$this->project_type = $this->project_info['project_type'];
break;
}
}
}
function download_remote_project() {
if (!empty($this->remote_downloaded)) {
return;
}
$this
->identify_project();
$this->remote_files_downloader
->download();
$this->remote_downloaded = TRUE;
}
function hash_remote_project() {
if (!empty($this->remote_hashed)) {
return;
}
$this
->download_remote_project();
$base_path = $this->remote_files_downloader
->get_final_destination();
$this->remote_files = hackedFileGroup::fromDirectory($base_path);
$this->remote_files
->compute_hashes();
$this->remote_hashed = TRUE;
}
function locate_local_project() {
$this
->hash_remote_project();
if (!is_array($this->project_info['includes']) || !count($this->project_info['includes'])) {
return FALSE;
}
if ($this->project_type != 'core') {
$includes = array_keys($this->project_info['includes']);
$include = array_shift($includes);
$include_type = $this->project_info['project_type'];
}
else {
$include = 'system';
$include_type = 'module';
}
$path = drupal_get_path($include_type, $include);
$temp = '';
foreach ($this->remote_files->files as $file) {
if (preg_match('@(^|.*/)' . $include . '.info$@', $file)) {
$temp = $file;
break;
}
}
$slash_count = substr_count($temp, '/');
$back_track = str_repeat('/..', $slash_count);
return realpath($path . $back_track);
}
function hash_local_project() {
if (!empty($this->local_hashed)) {
return;
}
$location = $this
->locate_local_project();
$this->local_files = hackedFileGroup::fromList($location, $this->remote_files->files);
$this->local_files
->compute_hashes();
$this->local_hashed = TRUE;
}
function compute_differences() {
$this
->hash_remote_project();
$this
->hash_local_project();
$results = array(
'same' => array(),
'different' => array(),
'missing' => array(),
'access_denied' => array(),
);
foreach ($this->remote_files->files as $file) {
if ($this->remote_files->files_hashes[$file] == $this->local_files->files_hashes[$file]) {
$results['same'][] = $file;
}
elseif (!$this->local_files
->file_exists($file)) {
$results['missing'][] = $file;
}
elseif (!$this->local_files
->is_readable($file)) {
$results['access_denied'][] = $file;
}
else {
$results['different'][] = $file;
}
}
$this->result = $results;
}
function compute_report() {
$this
->compute_differences();
$report = array(
'project_name' => $this->name,
'status' => HACKED_STATUS_UNCHECKED,
'counts' => array(
'same' => count($this->result['same']),
'different' => count($this->result['different']),
'missing' => count($this->result['missing']),
'access_denied' => count($this->result['access_denied']),
),
'title' => $this->project_info['title'],
'link' => $this->project_info['link'],
'name' => $this->project_info['name'],
'existing_version' => $this->project_info['existing_version'],
'install_type' => $this->project_info['install_type'],
'datestamp' => $this->project_info['datestamp'],
'project_type' => $this->project_info['project_type'],
'includes' => $this->project_info['includes'],
);
if ($report['counts']['access_denied'] > 0) {
$report['status'] = HACKED_STATUS_PERMISSION_DENIED;
}
elseif ($report['counts']['missing'] > 0) {
$report['status'] = HACKED_STATUS_HACKED;
}
elseif ($report['counts']['different'] > 0) {
$report['status'] = HACKED_STATUS_HACKED;
}
elseif ($report['counts']['same'] > 0) {
$report['status'] = HACKED_STATUS_UNHACKED;
}
return $report;
}
function compute_details() {
$report = $this
->compute_report();
$report['files'] = array();
$states = array(
'access_denied' => HACKED_STATUS_PERMISSION_DENIED,
'missing' => HACKED_STATUS_DELETED,
'different' => HACKED_STATUS_HACKED,
'same' => HACKED_STATUS_UNHACKED,
);
foreach ($states as $state => $status) {
foreach ($this->result[$state] as $file) {
$report['files'][$file] = $status;
$report['diffable'][$file] = $this
->file_is_diffable($file);
}
}
return $report;
}
function file_is_diffable($file) {
$this
->hash_remote_project();
$this
->hash_local_project();
return $this->remote_files
->is_not_binary($file) && $this->local_files
->is_not_binary($file);
}
function file_get_location($storage = 'local', $file) {
switch ($storage) {
case 'remote':
$this
->download_remote_project();
return $this->remote_files
->file_get_location($file);
case 'local':
$this
->hash_local_project();
return $this->local_files
->file_get_location($file);
}
return FALSE;
}
}
class hackedProjectWebDownloader {
var $project;
function hackedProjectWebDownloader(&$project) {
$this->project = $project;
}
function get_temp_directory($namespace = NULL) {
if (is_null($namespace)) {
$namespace = get_class($this);
}
$segments = array(
file_directory_temp(),
'hacked-cache-' . get_current_user(),
$namespace,
);
$dir = implode('/', array_filter($segments));
if (!file_check_directory($dir, FILE_CREATE_DIRECTORY) && !mkdir($dir, 0775, TRUE)) {
watchdog('hacked', 'Failed to create temp directory: %dir', array(
'%dir' => $dir,
), WATCHDOG_ERROR);
return FALSE;
}
return $dir;
}
function get_destination() {
$type = $this->project->project_type;
$name = $this->project->name;
$version = $this->project->existing_version;
$dir = $this
->get_temp_directory() . "/{$type}/{$name}";
if (!file_check_directory($dir, FILE_CREATE_DIRECTORY) && !mkdir($dir, 0775, TRUE)) {
watchdog('hacked', 'Failed to create temp directory: %dir', array(
'%dir' => $dir,
), WATCHDOG_ERROR);
return FALSE;
}
return "{$dir}/{$version}";
}
function get_final_destination() {
$dir = $this
->get_destination();
$name = $this->project->name;
$version = $this->project->existing_version;
$type = $this->project->project_type;
if ($type != 'core') {
$module_dir = $dir . "/{$name}";
}
else {
$module_dir = $dir . '/' . $name . '-' . $version;
}
return $module_dir;
}
function download() {
}
function remove_dir($path) {
if (is_file($path) || is_link($path)) {
unlink($path);
}
elseif (is_dir($path)) {
$d = dir($path);
while (($entry = $d
->read()) !== FALSE) {
if ($entry == '.' || $entry == '..') {
continue;
}
$entry_path = $path . '/' . $entry;
$this
->remove_dir($entry_path);
}
$d
->close();
rmdir($path);
}
else {
watchdog('hacked', 'Unknown file type(%path) stat: %stat ', array(
'%path' => $path,
'%stat' => print_r(stat($path), 1),
), WATCHDOG_ERROR);
}
}
}
class hackedProjectWebFilesDownloader extends hackedProjectWebDownloader {
function download_link() {
$this_release = $this->project->project_info['releases'][$this->project->existing_version];
return $this_release['download_link'];
}
function download() {
$dir = $this
->get_destination();
$release_url = $this
->download_link();
if (file_exists($dir)) {
return $dir;
}
$request = drupal_http_request($release_url);
if ($request->code == 200) {
if (!file_check_directory($dir, FILE_CREATE_DIRECTORY) && !mkdir($dir, 0775, TRUE)) {
watchdog('hacked', 'Failed to create temp directory: %dir', array(
'%dir' => $dir,
), WATCHDOG_ERROR);
return FALSE;
}
$project_path = file_create_path($dir . '/' . basename($release_url));
file_save_data($request->data, $project_path);
shell_exec("cd {$dir}; tar -zxf " . basename($project_path));
file_delete($project_path);
return TRUE;
}
return FALSE;
}
}
class hackedProjectWebCVSDownloader extends hackedProjectWebDownloader {
var $base_filename = '';
function hackedProjectWebCVSDownloader(&$project, $base_filename) {
parent::hackedProjectWebDownloader($project);
$this->base_filename = $base_filename;
}
function download_link() {
$info = array();
$pinfo =& $this->project->project_info;
if ($pinfo['project_type'] == 'core') {
$info['cvsroot'] = ':pserver:anonymous:anonymous@cvs.drupal.org:/cvs/drupal';
$info['module'] = 'drupal';
$info['checkout_folder'] = $pinfo['name'] . '-' . $pinfo['info']['version'];
$info['tag'] = $this
->cvs_deploy_get_tag($this->base_filename);
}
else {
$info['cvsroot'] = ':pserver:anonymous:anonymous@cvs.drupal.org:/cvs/drupal-contrib';
$info['module'] = 'contributions/' . $pinfo['project_type'] . 's/' . $pinfo['name'];
$info['checkout_folder'] = $pinfo['name'];
$info['tag'] = $this
->cvs_deploy_get_tag($this->base_filename);
}
return $info;
}
function cvs_deploy_get_tag($file) {
$version = '';
static $available = array();
$match = array();
if (empty($version)) {
$cvs_dir = dirname($file) . '/CVS';
if (is_dir($cvs_dir)) {
$tag = '';
if (file_exists($cvs_dir . '/Tag')) {
$tag_file = trim(file_get_contents($cvs_dir . '/Tag'));
if ($tag_file) {
$tag = preg_replace('@^(T|N)@', '', $tag_file);
}
}
$version = $tag;
}
}
elseif (preg_match('/\\$' . 'Name: (.*?)\\$/', $version, $match)) {
$version = trim($match[1]);
}
if (empty($version)) {
$version = 'HEAD';
}
return $version;
}
function download() {
$dir = $this
->get_destination();
$release_info = $this
->download_link();
if (file_exists($dir)) {
if ($this
->is_cvs_tag($release_info['tag'])) {
return $dir;
}
else {
$this
->remove_dir($dir);
}
}
if (hacked_cvs_checkout($release_info['cvsroot'], $release_info['module'], $dir, $release_info['checkout_folder'], $release_info['tag'])) {
return $dir;
}
return FALSE;
}
function get_destination() {
$type = $this->project->project_type;
$name = $this->project->name;
$info = $this
->download_link();
$version = $this->project->existing_version . '-' . $info['tag'];
$dir = $this
->get_temp_directory() . "/{$type}/{$name}";
if (!file_check_directory($dir, FILE_CREATE_DIRECTORY) && !mkdir($dir, 0775, TRUE)) {
watchdog('hacked', 'Failed to create temp directory: %dir', array(
'%dir' => $dir,
), WATCHDOG_ERROR);
return FALSE;
}
return "{$dir}/{$version}";
}
function is_cvs_tag($tag) {
$valid_tags = '@^DRUPAL-[567]--(\\d+)-(\\d+)(-[A-Z0-9]+)?@';
return (bool) preg_match($valid_tags, $tag);
}
}
class hackedFileGroup {
var $base_path = '';
var $files = array();
var $files_hashes = array();
var $file_mtimes = array();
var $hasher;
function hackedFileGroup($base_path) {
$this->base_path = $base_path;
$this->hasher = hacked_get_file_hasher();
}
static function fromDirectory($path) {
$filegroup = new hackedFileGroup($path);
$filegroup
->scan_base_path();
return $filegroup;
}
static function fromList($path, $files) {
$filegroup = new hackedFileGroup($path);
$filegroup->files = $files;
return $filegroup;
}
function scan_base_path() {
$files = hacked_file_scan_directory($this->base_path, '/.*/', array(
'.',
'..',
'CVS',
'.svn',
'.git',
));
foreach ($files as $file) {
$filename = str_replace($this->base_path . '/', '', $file->filename);
$this->files[] = $filename;
}
}
function compute_hashes() {
foreach ($this->files as $filename) {
$this->files_hashes[$filename] = $this->hasher
->hash($this->base_path . '/' . $filename);
}
}
function is_readable($file) {
return is_readable($this->base_path . '/' . $file);
}
function file_exists($file) {
return file_exists($this->base_path . '/' . $file);
}
function is_not_binary($file) {
return is_readable($this->base_path . '/' . $file) && !hacked_file_is_binary($this->base_path . '/' . $file);
}
function file_get_location($file) {
return $this->base_path . '/' . $file;
}
}
class hackedFileHasher {
function hash($filename) {
if (file_exists($filename)) {
if ($hash = $this
->cache_get($filename)) {
return $hash;
}
else {
$hash = $this
->perform_hash($filename);
$this
->cache_set($filename, $hash);
return $hash;
}
}
}
function cache_set($filename, $hash) {
cache_set($this
->cache_key($filename), $hash, HACKED_CACHE_TABLE, strtotime('+7 days'));
}
function cache_get($filename) {
$cache = cache_get($this
->cache_key($filename), HACKED_CACHE_TABLE);
if (!empty($cache->data)) {
return $cache->data;
}
}
function perform_hash($filename) {
return '';
}
function cache_key($filename) {
$key = array(
'filename' => $filename,
'mtime' => filemtime($filename),
'class_name' => get_class($this),
);
return sha1(serialize($key));
}
}
class hackedFileIgnoreEndingsHasher extends hackedFileHasher {
function perform_hash($filename) {
if (!hacked_file_is_binary($filename)) {
$file = file($filename, FILE_IGNORE_NEW_LINES);
return sha1(serialize($file));
}
else {
return sha1_file($filename);
}
}
}
class hackedFileIncludeEndingsHasher extends hackedFileHasher {
function perform_hash($filename) {
return sha1_file($filename);
}
}