summaryrefslogblamecommitdiffstats
path: root/modules-available/minilinux/page.inc.php
blob: 8004f1ab0b0599a3c5484e233c2964cb545cb3bb (plain) (tree)
1
2
3
4
5
6
7
8
9

     

                                 
 


                                         
 




                                                                





                                                                       

                                                           



                                                        
                                               

                                                                                                           
         
 

                                     


                                                               
                                                                                                                                                        
                                                                          
                                                               
                                         
                                                                               
                                                         




                                                                                                                                       



                                                                                                                             




                                                                              
                                                      
                                                                            
                                                                                                                                  
                                 
                         
                                       
                                                                


                                                                                                                                                       
                                                                                 
                                                       
                                                               
                                                




                                                                                              
                                                                                
                                 
                                                                 
                         













                                                                                                                    

                                                                        
                 
         


                                   


                                                               
                                                    

                                                 
                 

         
                                                                                 
         
                                                                       
                                                                                   

                                                                                        
                                                                                                                                                         
                                                                                                                                        
                                                                                                                    



                                                                  
                                                                                      


                                                                                


                                                                                  
                 
                                                                                             

         
                                             






                                                                       

                                                                                                   










                                                                                                                   
                                                                              
                                               
                                      




                                                                              
                                 


                                                                                     
                                                       
                                 









                                                                                                
                                                               
                                                                                                         
                                 
                         

                                                                         

                                                               

                                                                                                            
                                 

                                                                                                
                         
                 
                                                                                                  

                                                                          
                                                                             






                                                      
                                    
 
                                                                                        





                                                                                 

                                                       
                              







                                                                                                    
                         







                                                  
                                                                          
                                              
                                                                           
                                        
                                                                     
                                             
                                                                          
                                   
                                                                











                                                                     
                                     


                                                                       
                                                    














                                                                                                                       
                               
                 







                                                                       
                                                                                                               

                                                                                                      
                                                                                                                






                                                                                   
                                                                                                               

                                               




                                                               
                 
         
 

                                     
                                                 










                                                                                                                       
                                                                    

         
 
<?php

class Page_MiniLinux extends Page
{

	protected function doPreprocess()
	{
		User::load();

		if (!User::isLoggedIn()) {
			Message::addError('main.no-permission');
			Util::redirect('?do=Main');
		}

		if (Request::isPost()) {
			$show = Request::post('show', false, 'string');
			if ($show === 'delete') {
				$this->deleteVersion();
			} elseif ($show === 'updatesources') {
				$this->updateSources();
			} elseif ($show === 'setdefault') {
				$this->setDefault();
			}
			Util::redirect('?do=minilinux');
		}

		User::assertPermission('view');
		Dashboard::addSubmenu('?do=minilinux', Dictionary::translate('menu-versions'));
		Dashboard::addSubmenu('?do=minilinux&show=sources', Dictionary::translate('menu-sources'));
	}

	protected function doRender()
	{
		$show = Request::get('show', 'list', 'string');
		if ($show === 'list') {
			// List branches and versions
			$branches = Database::queryAll('SELECT sourceid, branchid, title, color, description FROM minilinux_branch ORDER BY title ASC');
			$versions = MiniLinux::queryAllVersionsByBranch();
			$usage = MiniLinux::getBootMenuUsage();
			$sourceList = [];
			// Group by branch for detailed listing, add usage info
			foreach ($branches as &$branch) {
				// Little hack: We abuse the title for ordering, so if the second char is a space, assume the first one
				// is just for sort order and remove it.
				if ($branch['title'][1] === ' ') {
					$branch['title'] = substr($branch['title'], 2);
				}
				$bid = 'div-' . str_replace('/', '-', $branch['branchid']);
				if (!isset($sourceList[$branch['sourceid']])) {
					$sourceList[$branch['sourceid']] = ['sourceid' => $branch['sourceid'], 'list' => []];
				}
				$sourceList[$branch['sourceid']]['list'][] = [
					'title' => $branch['title'],
					'color' => $branch['color'],
					'bid' => $bid
				];
				$branch['bid'] = $bid;
				if (isset($versions[$branch['branchid']])) {
					$branch['versionlist'] = $this->renderVersionList($versions[$branch['branchid']], $usage);
				}
			}
			unset($branch);
			$sourceList = array_values($sourceList);
		} elseif ($show === 'sources') {
			// List sources
			$res = Database::simpleQuery('SELECT sourceid, title, url, lastupdate, pubkey FROM minilinux_source ORDER BY title, sourceid');
			$sourceViewData = ['list' => [], 'show_refresh' => true];
			$tooOld = strtotime('-7 days');
			$showRefresh = strtotime('-5 minutes');
			foreach ($res as $row) {
				$row['lastupdate_s'] = Util::prettyTime($row['lastupdate']);
				if ($row['lastupdate'] != 0 && $row['lastupdate'] < $tooOld) {
					$row['update_class'] = 'text-danger';
				}
				if ($row['lastupdate'] > $showRefresh) {
					$sourceViewData['show_refresh'] = false;
				}
				$sourceViewData['list'][] = $row;
			}
		}
		// Output
		Render::addTemplate('page-minilinux', [
			'default' => Property::get(MiniLinux::PROPERTY_DEFAULT_BOOT),
			'sources' => $sourceList ?? null,
		]);
		// Warning
		if (!MiniLinux::updateCurrentBootSetting()) {
			Message::addError('default-not-installed', Property::get(MiniLinux::PROPERTY_DEFAULT_BOOT));
		}
		if (isset($branches)) {
			Render::addTemplate('branches', ['branches' => $branches]);
		} elseif (isset($sourceViewData)) {
			Render::addTemplate('sources', $sourceViewData);
		} else {
			Message::addError('main.invalid-action', $show);
		}
	}
	
	protected function doAjax()
	{
		User::load();
		$show = Request::post('show', false, 'string');
		if ($show === 'version') {
			$this->ajaxVersionDetails();
		} elseif ($show === 'download') {
			$this->ajaxDownload();
		}
	}

	private function renderVersionList(array $versions, array $usage): string
	{
		$def = Property::get(MiniLinux::PROPERTY_DEFAULT_BOOT);
		//$eff = Property::get(MiniLinux::PROPERTY_DEFAULT_BOOT_EFFECTIVE);
		foreach ($versions as &$version) {
			$version['dateline_s'] = Util::prettyTime($version['dateline']);
			$version['orphan'] = ($version['orphan'] > 0 && $version['installed'] == MiniLinux::INSTALL_MISSING) || ($version['orphan'] > 1);
			$version['downloading'] = $version['taskid'] && Taskmanager::isRunning(Taskmanager::status($version['taskid']));
			if ($version['installed'] != MiniLinux::INSTALL_MISSING && $version['versionid'] !== $def) {
				$version['showsetdefault'] = true;
			}
			if ($version['versionid'] === $def) {
				$version['isdefault'] = true;
				if (!$version['installed'] != MiniLinux::INSTALL_OK) {
					$version['default_class'] = 'bg-danger';
				}
			}
			if (isset($usage[$version['versionid']])) {
				$version['usage'] = $usage[$version['versionid']];
			}
		}
		return Render::parse('versionlist', ['versions' => array_values($versions)]);
	}

	private function ajaxVersionDetails()
	{
		User::assertPermission('view');
		$verify = Request::post('verify', false, 'bool');
		$versionid = Request::post('version', false, 'string');
		if ($versionid === false) {
			die('What!');
		}
		$ver = Database::queryFirst('SELECT versionid, description, taskid, data, installed
				FROM minilinux_version WHERE versionid = :versionid',
			['versionid' => $versionid]);
		if ($ver === false) {
			die('No such version');
		}
		$versionid = $ver['versionid']; // Just to be sure -- should be safe for building a path either way
		$data = json_decode($ver['data'], true);
		if (!is_array($data)) {
			die('Corrupted data');
		}
		$data['versionid'] = $versionid;
		$data['dltask'] = MiniLinux::validateDownloadTask($versionid, $ver['taskid']);
		$data['verify_button'] = !$verify && $data['dltask'] === null;
		if (is_array($data['files'])) {
			$valid = true;
			$sort = [];
			foreach ($data['files'] as &$file) {
				if (empty($file['name'])) {
					$sort[] = 'zzz' . implode(',', $file);
					continue;
				}
				$sort[] = $file['name'];
				$s = $this->getFileState($versionid, $file, $verify);
				if ($s !== self::FILE_OK) {
					$valid = false;
				}
				if ($s !== self::FILE_MISSING) {
					$data['delete_button'] = true;
				}
				$file['state'] = $this->fileStateToString($s);
				if (isset($file['size'])) {
					$file['size_s'] = Util::readableFileSize($file['size']);
				}
				if (isset($file['mtime'])) {
					$file['mtime_s'] = Util::prettyTime($file['mtime']);
				}
				if ($data['dltask'] !== null) {
					$file['fileid'] = MiniLinux::fileToId($versionid, $file['name']);
				}
			}
			unset($file);
			array_multisort($sort, SORT_ASC, $data['files']);
			if (!$valid) {
				$data['verify_button'] = false;
				if ($ver['installed'] != MiniLinux::INSTALL_MISSING) {
					MiniLinux::setInstalledState($versionid, MiniLinux::INSTALL_BROKEN);
				}
			} elseif ($ver['installed'] != MiniLinux::INSTALL_OK && $verify) {
				MiniLinux::setInstalledState($versionid, MiniLinux::INSTALL_OK);
			}
		}
		if ($data['dltask'] !== null || $ver['installed'] != MiniLinux::INSTALL_MISSING) {
			MiniLinux::checkStage4($data, $data['s4_errors']);
		}
		$data['changelog'] = Util::markup($ver['description'] ?? '');
		echo Render::parse('filelist', $data);
	}

	const FILE_OK = 0;
	const FILE_MISSING = 1;
	const FILE_SIZE_MISMATCH = 2;
	const FILE_CHECKSUM_BAD = 3;
	const FILE_NOT_READABLE = 4;

	private function getFileState(string $versionid, array $file, bool $verify): int
	{
		$path = CONFIG_HTTP_DIR . '/' . $versionid . '/' . $file['name'];
		if (!is_file($path))
			return self::FILE_MISSING;
		if (isset($file['size']) && filesize($path) != $file['size'])
			return self::FILE_SIZE_MISMATCH;
		if (!is_readable($path))
			return self::FILE_NOT_READABLE;
		if ($verify) {
			foreach (['sha512', 'sha384', 'sha256', 'sha224', 'sha1', 'md5'] as $algo) {
				if (isset($file[$algo])) {
					$calced = hash_file($algo, $path);
					if ($calced === false)
						continue; // Algo not supported?
					if ($calced !== $file['md5'])
						return self::FILE_CHECKSUM_BAD;
				}
			}
		}
		return self::FILE_OK;
	}

	private function fileStateToString($state)
	{
		switch ($state) {
		case self::FILE_CHECKSUM_BAD:
			return Dictionary::translate('file-checksum-bad');
		case self::FILE_SIZE_MISMATCH:
			return Dictionary::translate('file-size-mismatch');
		case self::FILE_MISSING:
			return Dictionary::translate('file-missing');
		case self::FILE_NOT_READABLE:
			return Dictionary::translate('file-not-readable');
		case self::FILE_OK:
			return Dictionary::translate('file-ok');
		}
		return '???';
	}

	private function ajaxDownload()
	{
		User::assertPermission('update');
		$version = Request::post('version', false, 'string');
		if ($version === false) {
			die('No version');
		}
		$task = MiniLinux::downloadVersion($version);
		if ($task === null) {
			Message::addError('no-such-version', $version);
			Message::renderList();
		} else {
			$this->ajaxVersionDetails();
		}
	}

	private function deleteVersion()
	{
		User::assertPermission('delete');
		$versionid = Request::post('version', false, 'string');
		if ($versionid === false) {
			Message::addError('main.parameter-missing', 'versionid');
			return;
		}
		$version = Database::queryFirst('SELECT versionid FROM minilinux_version WHERE versionid = :versionid',
			['versionid' => $versionid]);
		if ($version === false) {
			Message::addError('no-such-version');
			return;
		}
		$path = CONFIG_HTTP_DIR . '/' . $version['versionid'];
		$task = Taskmanager::submit('DeleteDirectory', [
			'path' => $path,
			'recursive' => true,
		]);
		if ($task !== false) {
			$task = Taskmanager::waitComplete($task, 2500);
			if (Taskmanager::isFailed($task)) {
				MiniLinux::setInstalledState($version['versionid'], MiniLinux::INSTALL_BROKEN);
				Message::addError('delete-error', $versionid, $task['data']['error']);
			} else {
				MiniLinux::setInstalledState($version['versionid'], MiniLinux::INSTALL_MISSING);
				Message::addSuccess('version-deleted', $versionid);
			}
		}
	}

	private function updateSources()
	{
		User::assertPermission('view'); // As it doesn't really change anything, accept view permission
		$ret = MiniLinux::updateList();
		if ($ret > 0) {
			for ($i = 0; $i < 6; ++$i) {
				sleep(1);
				if (!Trigger::checkCallbacks())
					break;
			}
		}
	}

	private function setDefault()
	{
		User::assertPermission('update');
		$versionid = Request::post('version', false, 'string');
		if ($versionid === false) {
			Message::addError('main.parameter-missing', 'versionid');
			return;
		}
		$version = Database::queryFirst('SELECT versionid FROM minilinux_version WHERE versionid = :versionid',
			['versionid' => $versionid]);
		if ($version === false) {
			Message::addError('no-such-version');
			return;
		}
		MiniLinux::setDefaultVersion($version['versionid']);
	}

}