|
<?php |
|
/** |
|
* Copyright (c) 2020 Eclipse Foundation. |
|
* |
|
* This program and the accompanying materials are made |
|
* available under the terms of the Eclipse Public License 2.0 |
|
* which is available at https://www.eclipse.org/legal/epl-2.0/ |
|
* |
|
* Contributors: |
|
* Eric Poirier (Eclipse Foundation) - initial API and implementation |
|
* |
|
* SPDX-License-Identifier: EPL-2.0 |
|
*/ |
|
|
|
require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/app.class.php"); |
|
require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/session.class.php"); |
|
require_once("/home/data/httpd/eclipse-php-classes/system/ldapconnection.class.php"); |
|
|
|
class DownloadDirectory { |
|
|
|
/** |
|
* App object |
|
*/ |
|
private $App; |
|
|
|
/** |
|
* Processing paths |
|
*/ |
|
private $processing_paths = array(); |
|
|
|
/** |
|
* |
|
* The directory to work with |
|
*/ |
|
private $basedir = ""; |
|
|
|
/** |
|
* Person ID |
|
*/ |
|
private $person_id = ""; |
|
|
|
/** |
|
* LDAP Group |
|
*/ |
|
private $ldap_group = NULL; |
|
|
|
/** |
|
* User is committer |
|
*/ |
|
private $user_is_committer = NULL; |
|
|
|
private $request_uri = ""; |
|
|
|
private $oomph_size_index_base = "https://download.eclipse.org/oomph/archive/"; |
|
private $oomph_index_html = ""; |
|
|
|
public function __construct() { |
|
$this->App = new App(); |
|
|
|
} |
|
|
|
/** |
|
* Get current path |
|
* |
|
* @param string $file |
|
* |
|
* @return string |
|
*/ |
|
private function _getPath($file) { |
|
if (empty($file)) { |
|
return ""; |
|
} |
|
|
|
$file = $this->App->checkPlain($file); |
|
$current_dir_path = $this->_getUri(); |
|
|
|
// Remove the last directory from the path |
|
$current_dir_path_array = explode('/', $current_dir_path); |
|
$count = count($current_dir_path_array); |
|
unset($current_dir_path_array[$count - 1]); |
|
|
|
// Scan through the parent's current directory to find out |
|
// the proper way to write the current directory. |
|
$parent_path = implode('/', $current_dir_path_array); |
|
$parent_path = rtrim($parent_path, '/') . "/"; |
|
|
|
// Validate if we're dealing with an existing file or directory |
|
$file_path = $parent_path . $file; |
|
if (is_file($this->getCurrentDirectory() . $file) || is_dir($this->getCurrentDirectory() . $file)) { |
|
return $file_path; |
|
} |
|
|
|
return ""; |
|
} |
|
|
|
/** |
|
* Get the output HTML of a file |
|
* |
|
* @return string |
|
*/ |
|
private function _getFileOutput($file) { |
|
|
|
if (empty($file)) { |
|
return ""; |
|
} |
|
|
|
$path = $this->_getPath($file); |
|
$processing_paths = $this->_getProcessingRequests(); |
|
|
|
$input_disabled = ''; |
|
$suffix_text = ''; |
|
|
|
# Get date/time |
|
$file_datetime = ""; |
|
$file_size = ""; |
|
if (file_exists($this->basedir . $file)) { |
|
$file_datetime = date("Y-m-d H:i", filemtime($this->basedir . $file)); |
|
$file_size = $this->human_filesize(filesize($this->basedir . $file), 1); |
|
} |
|
|
|
|
|
if (array_key_exists($path, $processing_paths)) { |
|
$action = 'archived'; |
|
if ($this->_isArchiveDomain()) { |
|
$action = 'deleted'; |
|
} |
|
$suffix_text = '<span class="small">(This file is being ' . $action . ')</span>'; |
|
|
|
# examine the token (return value from the handler |
|
if(!empty($processing_paths[$path])) { |
|
$suffix_text = '<span class="small">(File cannot be ' . $action . ': ' . $processing_paths[$path] . '. Please contact webmaster.)</span>'; |
|
} |
|
$input_disabled = 'disabled="disabled"'; |
|
} |
|
$icon = "<i class='fa fa-file-text-o' aria-hidden='true'></i>"; |
|
|
|
# build download link. Send to mirrors if it's a binary archive and it's not on archive.e.o. |
|
$link = "<a href='" . $path . "'> " . $file . "</a>"; |
|
if (!$this->_isArchiveDomain() && preg_match('/(zip|jar|xz|gz|tar|iso)$/', $file)) { |
|
$link = "<a href='https://www.eclipse.org/downloads/download.php?file=" . $path . "'>" . $file . "</a>"; |
|
} |
|
$checkbox = ""; |
|
if (!empty($path) && $this->_userIsCommitterOnProject()) { |
|
$checkbox = '<input ' . $input_disabled . ' type="checkbox" name="paths_to_archive[]" value="'. $path .'" />'; |
|
} |
|
return '<tr><td>' . $checkbox . '</td><td>' . $icon . ' ' . $link . '</td><td style="text-align: right; padding-right:15px;"> ' . $file_size . ' </td><td>' . $file_datetime . '</td><td>' . $suffix_text . '</td></tr>'; |
|
} |
|
|
|
/** |
|
* Get the output HTML of a folder |
|
* |
|
* @return string |
|
*/ |
|
private function _getFolderOutput($directory) { |
|
|
|
if($directory === ".") { |
|
return ""; |
|
} |
|
|
|
$path = $this->_getPath($directory); |
|
$processing_paths = $this->_getProcessingRequests(); |
|
|
|
$input_disabled = ''; |
|
$suffix_text = ''; |
|
|
|
# Get date/time |
|
$file_datetime = ""; |
|
if (file_exists($this->basedir . $directory) && $directory !== "..") { |
|
$file_datetime = date("Y-m-d H:i", filemtime($this->basedir . $directory)); |
|
} |
|
$size = $this->_getOomphDirectorySize($directory); |
|
|
|
if (array_key_exists($path, $processing_paths)) { |
|
$action = 'archived'; |
|
if ($this->_isArchiveDomain()) { |
|
$action = 'deleted'; |
|
} |
|
$suffix_text = '<span class="small">(This folder is being ' . $action . ')</span>'; |
|
|
|
# examine the token (return value from the handler |
|
if(!empty($processing_paths[$path])) { |
|
$suffix_text = '<span class="small">(Folder cannot be ' . $action . ': ' . $processing_paths[$path] . '. Please contact webmaster.)</span>'; |
|
} |
|
$input_disabled = 'disabled="disabled"'; |
|
} |
|
$icon = "<i class='fa fa-folder' aria-hidden='true'></i>"; |
|
$link = "<a href='" . $path . "'> " . $directory . "</a> "; |
|
|
|
$checkbox = ""; |
|
if (!empty($path) && $directory !== ".." && $this->_userIsCommitterOnProject()) { |
|
$checkbox = '<input ' . $input_disabled . ' type="checkbox" name="paths_to_archive[]" value="'. $path .'">'; |
|
} |
|
|
|
return '<tr><td>' . $checkbox . '</td><td>' . $icon . ' ' . $link . '</td><td style="text-align: right; padding-right:15px;"> ' . $size . ' </td><td>' . $file_datetime . '</td><td>' . $suffix_text . '</td></tr>'; |
|
} |
|
|
|
/** |
|
* Get Person ID from Session |
|
* |
|
* @return string |
|
*/ |
|
private function _getPersonID() { |
|
if (empty($this->person_id)) { |
|
$Session = new Session(); |
|
$Friend = $Session->getFriend(); |
|
$this->person_id = $Friend->getUID(); |
|
} |
|
return $this->person_id; |
|
} |
|
|
|
/** |
|
* Get all the processing requests from account_requests table |
|
* |
|
* @return array |
|
*/ |
|
private function _getProcessingRequests() { |
|
|
|
if (!empty($this->processing_paths)) { |
|
return $this->processing_paths; |
|
} |
|
|
|
$action = "DOWNLOAD_ARCHIVE"; |
|
if ($this->_isArchiveDomain()) { |
|
$action = "DOWNLOAD_DELETE"; |
|
} |
|
|
|
$sql = "SELECT password as Path, token |
|
FROM account_requests |
|
WHERE fname = " . $this->App->returnQuotedString($this->App->sqlSanitize($action)) . " |
|
AND lname = " . $this->App->returnQuotedString($this->App->sqlSanitize($action)); |
|
$result = $this->App->eclipse_sql($sql); |
|
|
|
if (empty($result)) { |
|
$this->processing_paths; |
|
} |
|
|
|
while($myrow = mysql_fetch_array($result)) { |
|
$this->processing_paths[$myrow['Path']] = $myrow['token']; |
|
} |
|
return $this->processing_paths; |
|
} |
|
|
|
/** |
|
* Get uri |
|
* |
|
* @return string |
|
*/ |
|
private function _getUri() { |
|
if (empty($this->request_uri)) { |
|
# Support adding /listing to a directory that contains an index file |
|
# See: errors/404.php |
|
$this->request_uri = preg_replace('#listing$#', '', $_SERVER['REQUEST_URI']); |
|
} |
|
return str_replace("?d", "", $this->request_uri); |
|
} |
|
|
|
|
|
/** |
|
* Get the url of the current directory |
|
* |
|
* @return string |
|
*/ |
|
public function getCurrentDirectory() { |
|
return $_SERVER['DOCUMENT_ROOT'] . rawurldecode($this->_getUri()); |
|
} |
|
|
|
/** |
|
* Get the project ID based on the group owner of the folder |
|
* |
|
* @return string |
|
*/ |
|
private function _getProjectID() { |
|
$group = $this->getLdapGroupByGid(filegroup($this->basedir)); |
|
|
|
if (empty($group)) { |
|
return ""; |
|
} |
|
return $group; |
|
} |
|
|
|
/** |
|
* Insert into account_requests table |
|
* |
|
* @return bool |
|
*/ |
|
private function _insertRequest() { |
|
if (empty($_POST['paths_to_archive'])) { |
|
return FALSE; |
|
} |
|
|
|
$action = "DOWNLOAD_ARCHIVE"; |
|
if ($this->_isArchiveDomain()) { |
|
$action = "DOWNLOAD_DELETE"; |
|
} |
|
|
|
foreach ($_POST['paths_to_archive'] as $path) { |
|
$sql = "SELECT |
|
email |
|
FROM account_requests |
|
WHERE email = " . $this->App->returnQuotedString($this->App->sqlSanitize($this->_getPersonID())) . " |
|
AND password = ". $this->App->returnQuotedString($this->App->sqlSanitize($path)); |
|
$result = $this->App->eclipse_sql($sql); |
|
while ($row = mysql_fetch_array($result)) { |
|
if (!empty($row['email'])) { |
|
return FALSE; |
|
} |
|
} |
|
|
|
$sql = "INSERT INTO account_requests ( |
|
email, |
|
new_email, |
|
fname, |
|
lname, |
|
password, |
|
ip, |
|
req_when, |
|
token |
|
) |
|
VALUES ( |
|
" . $this->App->returnQuotedString($this->App->sqlSanitize($this->_getPersonID())) . ", |
|
NULL, |
|
" . $this->App->returnQuotedString($this->App->sqlSanitize($action)) . ", |
|
" . $this->App->returnQuotedString($this->App->sqlSanitize($action)) . ", |
|
" . $this->App->returnQuotedString($this->App->sqlSanitize($path)) . ", |
|
" . $this->App->returnQuotedString($this->App->sqlSanitize($this->App->getRemoteIPAddress())) . ", |
|
NOW(), |
|
NULL |
|
)"; |
|
$this->App->eclipse_sql($sql); |
|
} |
|
} |
|
|
|
/** |
|
* Check if the current domain is archive.eclipse.org |
|
* |
|
* @return bool |
|
*/ |
|
private function _isArchiveDomain() { |
|
if (!empty($_SERVER['HTTP_HOST']) && strpos($_SERVER['HTTP_HOST'], 'archive.eclipse.org') !== FALSE) { |
|
return TRUE; |
|
} |
|
return FALSE; |
|
} |
|
|
|
/** |
|
* Check if the user is a committer on a specific project |
|
* |
|
* @return bool |
|
*/ |
|
private function _userIsCommitterOnProject() { |
|
|
|
if (!is_null($this->user_is_committer)) { |
|
return $this->user_is_committer; |
|
} |
|
|
|
$sql = "SELECT count(1) as count |
|
FROM PeopleProjects |
|
WHERE PersonID = " . $this->App->returnQuotedString($this->App->sqlSanitize($this->_getPersonID())) . " |
|
AND ProjectID = " . $this->App->returnQuotedString($this->App->sqlSanitize($this->_getProjectID())) . " |
|
AND Relation = " . $this->App->returnQuotedString("CM") . " |
|
AND (InactiveDate IS NULL OR InactiveDate = '0000-00-00')"; |
|
$result = $this->App->foundation_sql($sql); |
|
$this->user_is_committer = FALSE; |
|
while($myrow = mysql_fetch_array($result)) { |
|
if ($myrow['count']) { |
|
$this->user_is_committer = TRUE; |
|
} |
|
} |
|
|
|
return $this->user_is_committer; |
|
} |
|
|
|
|
|
|
|
/** |
|
* Get the Form ouput |
|
* |
|
* @return string |
|
*/ |
|
public function getFormOutput($files, $dirs, $basedir=null) { |
|
|
|
if($basedir === null) { |
|
$basedir = $this->getCurrentDirectory(); |
|
} |
|
$this->basedir = $basedir; |
|
|
|
if(preg_match('#/localsite/(.*).org/(.*)#', $basedir, $matches)) { |
|
$site = $matches[1]; |
|
$basepath = "/" . $matches[2]; |
|
$this->_getOomphDirectoryHTML($site, $basepath); |
|
} |
|
|
|
$output = ""; |
|
$html_checkboxes = array(); |
|
foreach ($dirs as $directory) { |
|
if($directory === ".") { |
|
continue; |
|
} |
|
|
|
if ($directory === ".." || $this->_userIsCommitterOnProject() === FALSE) { |
|
$output .= $this->_getFolderOutput($directory); |
|
continue; |
|
} |
|
|
|
$html_checkboxes[] = $this->_getFolderOutput($directory); |
|
} |
|
|
|
foreach ($files as $file) { |
|
if ($this->_userIsCommitterOnProject() === FALSE) { |
|
$output .= $this->_getFileOutput($file); |
|
} |
|
$html_checkboxes[] = $this->_getFileOutput($file); |
|
} |
|
|
|
if ($this->_userIsCommitterOnProject() === FALSE) { |
|
return "<table class='.table' style='width:100%; font-family: monospace; font-size: medium;'>" . $output . "</table>"; |
|
} |
|
|
|
// Now that we know that the current user is a committer for this project, |
|
// we can safely insert a new request on page reload |
|
$this->_insertRequest(); |
|
|
|
$output .= '<form class="downloads-directory" method="post" action="/errors/filehandler.php">'; |
|
$output .= "<table class='.table' style='width:100%; font-family: monospace;'>" . implode("", $html_checkboxes) . "</table>"; |
|
|
|
$button_text = 'Move selected to archive.eclipse.org'; |
|
$button_class = 'btn-primary'; |
|
if ($this->_isArchiveDomain()) { |
|
$button_text = 'Delete selected permanently'; |
|
$button_class = 'btn-danger'; |
|
} |
|
|
|
$output .= '<ul class="list-inline">'; |
|
$output .= '<li><input id="deletesubmit" class="btn btn-xs ' . $button_class . '" type="submit" value="' . $button_text . '" /></li>'; |
|
if (!$this->_isArchiveDomain()) { |
|
$App = new App(); |
|
$archive_url = str_replace("download.eclipse.org","archive.eclipse.org", $App->getCurrentURL()); |
|
$output .= '<li><a class="btn btn-success btn-xs" href="'. $archive_url .'">Go to Archives</a></li>'; |
|
} |
|
$output .= '</ul>'; |
|
$output .= "</form>"; |
|
if (!$this->_isArchiveDomain()) { |
|
$output .= "<p><b>PLEASE NOTE:</b> You can safely move content, even entire p2 repositories, to Archives. Links will continue to work.</p>"; |
|
} |
|
$output .= "<span class='small'>File and folder operations make take several seconds to complete. Refresh the page to get current status.</span>"; |
|
return $output; |
|
} |
|
|
|
/** |
|
* Get the LDAP Group name, by gid |
|
* |
|
* @return string |
|
*/ |
|
public function getLdapGroupByGid($gid) { |
|
if (!is_null($this->ldap_group)) { |
|
return $this->ldap_group; |
|
} |
|
$LDAPConnection = new LDAPConnection(); |
|
$this->ldap_group = $LDAPConnection->getGroupByGid($gid); |
|
return $this->ldap_group; |
|
} |
|
|
|
public function human_filesize($bytes, $decimals = 2) { |
|
$sz = ' kMGTP'; |
|
$factor = floor((strlen($bytes) - 1) / 3); |
|
if($factor == 0) { |
|
$decimals = 0; |
|
} |
|
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[$factor]; |
|
} |
|
|
|
|
|
/** |
|
* |
|
* @param String $_site download.eclipse or archive.eclipse |
|
* @param String $_base /path/to/the/dir relative to the base directory containing downloads |
|
*/ |
|
private function _getOomphDirectoryHTML($_site, $_base) { |
|
$this->oomph_index_html = file_get_contents($this->oomph_size_index_base . $_site . $_base); |
|
} |
|
|
|
/** |
|
* |
|
* @param String $dirname the directory name to look up, no slashes. |
|
* @return String |
|
*/ |
|
private function _getOomphDirectorySize($dirname) { |
|
if(!empty($this->oomph_index_html)) { |
|
# <tr><td><a id='2020-12' href='2020-12/index.html'>2020-12</a></td><td>474.3M</td><td>17.15%</td><td>2021-03-30</td></tr> |
|
if(preg_match('#<a id=\'' . $dirname . '\' .*</td><td.*>(.*)</td><td>.*</td><td>.*</td></tr>#', $this->oomph_index_html, $matches)) { |
|
return $matches[1]; |
|
} |
|
} |
|
} |
|
} |