From dbc0d9614421e064cc62aacf116ebb783c83f2f3 Mon Sep 17 00:00:00 2001
From: Jonathan Bauer
Date: Fri, 1 Apr 2016 16:50:13 +0200
Subject: [merge] merging c3sl / fr - initial commit
---
modules/adduser.inc.php | 60 -
modules/adduser/config.json | 4 +
modules/adduser/module.inc.php | 60 +
modules/adduser/templates/page-adduser.html | 28 +
modules/backup.inc.php | 134 -
modules/backup/config.json | 4 +
modules/backup/module.inc.php | 164 +
modules/backup/templates/_page.html | 41 +
modules/backup/templates/restore.html | 62 +
modules/baseconfig.inc.php | 127 -
modules/baseconfig/config.json | 4 +
modules/baseconfig/module.inc.php | 127 +
modules/baseconfig/templates/_page.html | 186 +
modules/citymanagement/config.json | 5 +
modules/citymanagement/module.inc.php | 81 +
.../templates/page-citymanagement.html | 77 +
modules/dozmod.inc.php | 252 -
modules/dozmod/config.json | 4 +
modules/dozmod/module.inc.php | 252 +
modules/dozmod/templates/images-delete.html | 57 +
modules/dozmod/templates/mailconfig.html | 91 +
modules/dozmod/templates/orglist.html | 51 +
modules/dozmod/templates/userlist.html | 62 +
modules/eventlog.inc.php | 70 -
modules/eventlog/config.json | 4 +
modules/eventlog/module.inc.php | 70 +
modules/eventlog/templates/_page.html | 41 +
modules/imgmanagement/config.json | 5 +
modules/imgmanagement/module.inc.php | 76 +
.../templates/page-imgmanagement.html | 46 +
modules/internetaccess.inc.php | 51 -
modules/internetaccess/config.json | 4 +
modules/internetaccess/module.inc.php | 51 +
modules/internetaccess/templates/_page.html | 40 +
modules/internetaccess/templates/restart.html | 22 +
modules/locations.inc.php | 348 -
modules/locations/config.json | 4 +
modules/locations/module.inc.php | 348 +
modules/locations/templates/location-subnets.html | 73 +
modules/locations/templates/locations.html | 96 +
modules/locations/templates/subnets.html | 35 +
modules/main.inc.php | 54 -
modules/main/config.json | 4 +
modules/main/module.inc.php | 64 +
modules/main/templates/dialog-generic.html | 13 +
modules/main/templates/footer.html | 2 +
modules/main/templates/main-menu.html | 51 +
modules/main/templates/main-menu.html.topnavbar | 92 +
modules/main/templates/messagebox-error.html | 1 +
modules/main/templates/messagebox-info.html | 1 +
modules/main/templates/messagebox-success.html | 1 +
modules/main/templates/messagebox-warning.html | 1 +
modules/main/templates/page-login.html | 11 +
modules/main/templates/page-main-guest.html | 15 +
modules/main/templates/page-main.html | 36 +
modules/main/templates/page-minilinux.html | 14 +
modules/main/templates/page-news.html | 57 +
modules/main/templates/page-syslog.html | 58 +
modules/main/templates/page-vmstore.html | 111 +
modules/main/templates/pagenav.html | 16 +
modules/main/templates/tm-callback-trigger.html | 15 +
modules/minilinux.inc.php | 128 -
modules/minilinux/config.json | 4 +
modules/minilinux/module.inc.php | 128 +
modules/minilinux/templates/download.html | 1 +
modules/minilinux/templates/filelist.html | 77 +
modules/minilinux/templates/page-minilinux.html | 28 +
modules/news.inc.php | 167 -
modules/news/config.json | 4 +
modules/news/module.inc.php | 167 +
modules/news/templates/page-news.html | 57 +
modules/serversetup.inc.php | 141 -
modules/serversetup/config.json | 4 +
modules/serversetup/module.inc.php | 186 +
modules/serversetup/templates/ipaddress.html | 34 +
modules/serversetup/templates/ipxe.html | 149 +
modules/serversetup/templates/ipxe_update.html | 20 +
modules/session.inc.php | 38 -
modules/session/config.json | 4 +
modules/session/module.inc.php | 36 +
modules/session/templates/page-login.html | 11 +
modules/statistics.inc.php | 785 -
modules/statistics/config.json | 5 +
modules/statistics/module.inc.php | 792 +
modules/statistics/templates/clientlist.html | 45 +
modules/statistics/templates/cpumodels.html | 51 +
modules/statistics/templates/id44.html | 48 +
modules/statistics/templates/kvmstate.html | 47 +
modules/statistics/templates/machine-hdds.html | 67 +
modules/statistics/templates/machine-main.html | 124 +
modules/statistics/templates/machine-notes.html | 17 +
modules/statistics/templates/machine-usage.html | 51 +
modules/statistics/templates/memory.html | 47 +
modules/statistics/templates/newclients.html | 44 +
modules/statistics/templates/summary.html | 33 +
modules/statistics/templates/syslog.html | 43 +
modules/support/config.json | 4 +
modules/support/module.inc.php | 77 +
modules/support/templates/go-pear.phar | 99492 +++++++++++++++++++
modules/support/templates/page-faq.html | 58 +
modules/support/templates/page-support.html | 67 +
modules/sysconfig.inc.php | 391 -
modules/sysconfig/addconfig.inc.php | 4 +-
modules/sysconfig/addmodule.inc.php | 31 +-
modules/sysconfig/addmodule_adauth.inc.php | 108 +-
modules/sysconfig/addmodule_branding.inc.php | 4 +-
modules/sysconfig/addmodule_custommodule.inc.php | 4 +-
modules/sysconfig/addmodule_ldapauth.inc.php | 92 +-
modules/sysconfig/addmodule_sshconfig.inc.php | 2 +-
modules/sysconfig/config.json | 4 +
modules/sysconfig/module.inc.php | 396 +
modules/sysconfig/templates/_page.html | 227 +
modules/sysconfig/templates/ad-finish.html | 29 +
modules/sysconfig/templates/ad-selfsearch.html | 112 +
modules/sysconfig/templates/ad-start.html | 121 +
.../templates/ad_ldap-checkconnection.html | 91 +
.../templates/ad_ldap-checkcredentials.html | 67 +
modules/sysconfig/templates/branding-check.html | 26 +
modules/sysconfig/templates/branding-start.html | 25 +
modules/sysconfig/templates/cfg-finish.html | 12 +
modules/sysconfig/templates/cfg-start.html | 39 +
.../sysconfig/templates/config-module-list.html | 17 +
modules/sysconfig/templates/custom-filelist.html | 16 +
modules/sysconfig/templates/custom-fileselect.html | 31 +
modules/sysconfig/templates/custom-upload.html | 18 +
modules/sysconfig/templates/ldap-finish.html | 29 +
modules/sysconfig/templates/ldap-start.html | 101 +
modules/sysconfig/templates/sshconfig-start.html | 27 +
modules/sysconfig/templates/start.html | 12 +
modules/sysconfignew/config.json | 4 +
modules/sysconfignew/module.inc.php | 113 +
modules/sysconfignew/templates/_pagenew.html | 190 +
modules/sysconfignew/templates/module-editor.html | 269 +
modules/syslog.inc.php | 94 -
modules/syslog/config.json | 4 +
modules/syslog/module.inc.php | 94 +
modules/syslog/templates/page-syslog.html | 58 +
modules/systemstatus.inc.php | 361 -
modules/systemstatus/config.json | 4 +
modules/systemstatus/module.inc.php | 361 +
modules/systemstatus/templates/_page.html | 129 +
modules/systemstatus/templates/addresses.html | 8 +
modules/systemstatus/templates/diskstat.html | 63 +
modules/systemstatus/templates/services.html | 6 +
modules/systemstatus/templates/systeminfo.html | 115 +
modules/translation.inc.php | 491 -
modules/translation/config.json | 4 +
modules/translation/module.inc.php | 597 +
modules/translation/templates/_page.html | 17 +
modules/translation/templates/edit.html | 71 +
modules/translation/templates/module-list.html | 32 +
modules/translation/templates/template-list.html | 32 +
modules/usermanagement/config.json | 5 +
modules/usermanagement/module.inc.php | 109 +
.../usermanagement/templates/user-management.html | 127 +
modules/vmstore.inc.php | 63 -
modules/vmstore/config.json | 4 +
modules/vmstore/module.inc.php | 63 +
modules/vmstore/templates/mount.html | 25 +
modules/vmstore/templates/page-vmstore.html | 111 +
modules/webinterface.inc.php | 85 -
modules/webinterface/config.json | 4 +
modules/webinterface/module.inc.php | 85 +
modules/webinterface/templates/httpd-restart.html | 6 +
modules/webinterface/templates/https.html | 60 +
modules/webinterface/templates/passwords.html | 25 +
166 files changed, 109095 insertions(+), 4045 deletions(-)
delete mode 100644 modules/adduser.inc.php
create mode 100644 modules/adduser/config.json
create mode 100644 modules/adduser/module.inc.php
create mode 100644 modules/adduser/templates/page-adduser.html
delete mode 100644 modules/backup.inc.php
create mode 100644 modules/backup/config.json
create mode 100644 modules/backup/module.inc.php
create mode 100644 modules/backup/templates/_page.html
create mode 100644 modules/backup/templates/restore.html
delete mode 100644 modules/baseconfig.inc.php
create mode 100644 modules/baseconfig/config.json
create mode 100644 modules/baseconfig/module.inc.php
create mode 100644 modules/baseconfig/templates/_page.html
create mode 100644 modules/citymanagement/config.json
create mode 100644 modules/citymanagement/module.inc.php
create mode 100644 modules/citymanagement/templates/page-citymanagement.html
delete mode 100644 modules/dozmod.inc.php
create mode 100644 modules/dozmod/config.json
create mode 100644 modules/dozmod/module.inc.php
create mode 100644 modules/dozmod/templates/images-delete.html
create mode 100644 modules/dozmod/templates/mailconfig.html
create mode 100644 modules/dozmod/templates/orglist.html
create mode 100644 modules/dozmod/templates/userlist.html
delete mode 100644 modules/eventlog.inc.php
create mode 100644 modules/eventlog/config.json
create mode 100644 modules/eventlog/module.inc.php
create mode 100644 modules/eventlog/templates/_page.html
create mode 100644 modules/imgmanagement/config.json
create mode 100644 modules/imgmanagement/module.inc.php
create mode 100644 modules/imgmanagement/templates/page-imgmanagement.html
delete mode 100644 modules/internetaccess.inc.php
create mode 100644 modules/internetaccess/config.json
create mode 100644 modules/internetaccess/module.inc.php
create mode 100644 modules/internetaccess/templates/_page.html
create mode 100644 modules/internetaccess/templates/restart.html
delete mode 100644 modules/locations.inc.php
create mode 100644 modules/locations/config.json
create mode 100644 modules/locations/module.inc.php
create mode 100644 modules/locations/templates/location-subnets.html
create mode 100644 modules/locations/templates/locations.html
create mode 100644 modules/locations/templates/subnets.html
delete mode 100644 modules/main.inc.php
create mode 100644 modules/main/config.json
create mode 100644 modules/main/module.inc.php
create mode 100644 modules/main/templates/dialog-generic.html
create mode 100644 modules/main/templates/footer.html
create mode 100644 modules/main/templates/main-menu.html
create mode 100644 modules/main/templates/main-menu.html.topnavbar
create mode 100644 modules/main/templates/messagebox-error.html
create mode 100644 modules/main/templates/messagebox-info.html
create mode 100644 modules/main/templates/messagebox-success.html
create mode 100644 modules/main/templates/messagebox-warning.html
create mode 100644 modules/main/templates/page-login.html
create mode 100644 modules/main/templates/page-main-guest.html
create mode 100644 modules/main/templates/page-main.html
create mode 100644 modules/main/templates/page-minilinux.html
create mode 100644 modules/main/templates/page-news.html
create mode 100644 modules/main/templates/page-syslog.html
create mode 100644 modules/main/templates/page-vmstore.html
create mode 100644 modules/main/templates/pagenav.html
create mode 100644 modules/main/templates/tm-callback-trigger.html
delete mode 100644 modules/minilinux.inc.php
create mode 100644 modules/minilinux/config.json
create mode 100644 modules/minilinux/module.inc.php
create mode 100644 modules/minilinux/templates/download.html
create mode 100644 modules/minilinux/templates/filelist.html
create mode 100644 modules/minilinux/templates/page-minilinux.html
delete mode 100644 modules/news.inc.php
create mode 100644 modules/news/config.json
create mode 100644 modules/news/module.inc.php
create mode 100644 modules/news/templates/page-news.html
delete mode 100644 modules/serversetup.inc.php
create mode 100644 modules/serversetup/config.json
create mode 100644 modules/serversetup/module.inc.php
create mode 100644 modules/serversetup/templates/ipaddress.html
create mode 100644 modules/serversetup/templates/ipxe.html
create mode 100644 modules/serversetup/templates/ipxe_update.html
delete mode 100644 modules/session.inc.php
create mode 100644 modules/session/config.json
create mode 100644 modules/session/module.inc.php
create mode 100644 modules/session/templates/page-login.html
delete mode 100644 modules/statistics.inc.php
create mode 100644 modules/statistics/config.json
create mode 100644 modules/statistics/module.inc.php
create mode 100644 modules/statistics/templates/clientlist.html
create mode 100644 modules/statistics/templates/cpumodels.html
create mode 100644 modules/statistics/templates/id44.html
create mode 100644 modules/statistics/templates/kvmstate.html
create mode 100644 modules/statistics/templates/machine-hdds.html
create mode 100644 modules/statistics/templates/machine-main.html
create mode 100644 modules/statistics/templates/machine-notes.html
create mode 100644 modules/statistics/templates/machine-usage.html
create mode 100644 modules/statistics/templates/memory.html
create mode 100644 modules/statistics/templates/newclients.html
create mode 100644 modules/statistics/templates/summary.html
create mode 100644 modules/statistics/templates/syslog.html
create mode 100644 modules/support/config.json
create mode 100644 modules/support/module.inc.php
create mode 100644 modules/support/templates/go-pear.phar
create mode 100644 modules/support/templates/page-faq.html
create mode 100644 modules/support/templates/page-support.html
delete mode 100644 modules/sysconfig.inc.php
create mode 100644 modules/sysconfig/config.json
create mode 100644 modules/sysconfig/module.inc.php
create mode 100644 modules/sysconfig/templates/_page.html
create mode 100644 modules/sysconfig/templates/ad-finish.html
create mode 100644 modules/sysconfig/templates/ad-selfsearch.html
create mode 100644 modules/sysconfig/templates/ad-start.html
create mode 100644 modules/sysconfig/templates/ad_ldap-checkconnection.html
create mode 100644 modules/sysconfig/templates/ad_ldap-checkcredentials.html
create mode 100644 modules/sysconfig/templates/branding-check.html
create mode 100644 modules/sysconfig/templates/branding-start.html
create mode 100644 modules/sysconfig/templates/cfg-finish.html
create mode 100644 modules/sysconfig/templates/cfg-start.html
create mode 100644 modules/sysconfig/templates/config-module-list.html
create mode 100644 modules/sysconfig/templates/custom-filelist.html
create mode 100644 modules/sysconfig/templates/custom-fileselect.html
create mode 100644 modules/sysconfig/templates/custom-upload.html
create mode 100644 modules/sysconfig/templates/ldap-finish.html
create mode 100644 modules/sysconfig/templates/ldap-start.html
create mode 100644 modules/sysconfig/templates/sshconfig-start.html
create mode 100644 modules/sysconfig/templates/start.html
create mode 100644 modules/sysconfignew/config.json
create mode 100644 modules/sysconfignew/module.inc.php
create mode 100644 modules/sysconfignew/templates/_pagenew.html
create mode 100644 modules/sysconfignew/templates/module-editor.html
delete mode 100644 modules/syslog.inc.php
create mode 100644 modules/syslog/config.json
create mode 100644 modules/syslog/module.inc.php
create mode 100644 modules/syslog/templates/page-syslog.html
delete mode 100644 modules/systemstatus.inc.php
create mode 100644 modules/systemstatus/config.json
create mode 100644 modules/systemstatus/module.inc.php
create mode 100644 modules/systemstatus/templates/_page.html
create mode 100644 modules/systemstatus/templates/addresses.html
create mode 100644 modules/systemstatus/templates/diskstat.html
create mode 100644 modules/systemstatus/templates/services.html
create mode 100644 modules/systemstatus/templates/systeminfo.html
delete mode 100644 modules/translation.inc.php
create mode 100644 modules/translation/config.json
create mode 100644 modules/translation/module.inc.php
create mode 100644 modules/translation/templates/_page.html
create mode 100644 modules/translation/templates/edit.html
create mode 100644 modules/translation/templates/module-list.html
create mode 100644 modules/translation/templates/template-list.html
create mode 100644 modules/usermanagement/config.json
create mode 100644 modules/usermanagement/module.inc.php
create mode 100644 modules/usermanagement/templates/user-management.html
delete mode 100644 modules/vmstore.inc.php
create mode 100644 modules/vmstore/config.json
create mode 100644 modules/vmstore/module.inc.php
create mode 100644 modules/vmstore/templates/mount.html
create mode 100644 modules/vmstore/templates/page-vmstore.html
delete mode 100644 modules/webinterface.inc.php
create mode 100644 modules/webinterface/config.json
create mode 100644 modules/webinterface/module.inc.php
create mode 100644 modules/webinterface/templates/httpd-restart.html
create mode 100644 modules/webinterface/templates/https.html
create mode 100644 modules/webinterface/templates/passwords.html
(limited to 'modules')
diff --git a/modules/adduser.inc.php b/modules/adduser.inc.php
deleted file mode 100644
index c236cb6f..00000000
--- a/modules/adduser.inc.php
+++ /dev/null
@@ -1,60 +0,0 @@
- $_POST['user'],
- 'pass' => Crypto::hash6($_POST['pass1']),
- 'fullname' => $_POST['fullname'],
- 'phone' => $_POST['phone'],
- 'email' => $_POST['email'],
- );
- if (Database::exec('INSERT INTO user SET login = :user, passwd = :pass, fullname = :fullname, phone = :phone, email = :email', $data) != 1) {
- Util::traceError('Could not create new user in DB');
- }
- // Make it superadmin if first user. This method sucks as it's a race condition but hey...
- $ret = Database::queryFirst('SELECT Count(*) AS num FROM user');
- if ($ret !== false && $ret['num'] == 1) {
- Database::exec('UPDATE user SET permissions = 1');
- EventLog::clear();
- EventLog::info('Created first user ' . $_POST['user']);
- } else {
- EventLog::info(User::getName() . ' created user ' . $_POST['user']);
- }
- Message::addInfo('adduser-success');
- Util::redirect('?do=Session&action=login');
- }
- }
- }
-
- protected function doRender()
- {
- // No user was added, check if current user is allowed to add a new user
- // Currently you can only add users if there is no user yet. :)
- if (!User::hasPermission('superadmin') && Database::queryFirst('SELECT userid FROM user LIMIT 1') !== false) {
- Message::addError('adduser-disabled');
- } else {
-
- Render::setTitle(Dictionary::translate('lang_createUser'));
- Render::addTemplate('page-adduser', $_POST);
- }
- }
-
-}
diff --git a/modules/adduser/config.json b/modules/adduser/config.json
new file mode 100644
index 00000000..d5da4cc8
--- /dev/null
+++ b/modules/adduser/config.json
@@ -0,0 +1,4 @@
+{
+ "category":"hidden",
+ "enabled":"true"
+}
diff --git a/modules/adduser/module.inc.php b/modules/adduser/module.inc.php
new file mode 100644
index 00000000..c236cb6f
--- /dev/null
+++ b/modules/adduser/module.inc.php
@@ -0,0 +1,60 @@
+ $_POST['user'],
+ 'pass' => Crypto::hash6($_POST['pass1']),
+ 'fullname' => $_POST['fullname'],
+ 'phone' => $_POST['phone'],
+ 'email' => $_POST['email'],
+ );
+ if (Database::exec('INSERT INTO user SET login = :user, passwd = :pass, fullname = :fullname, phone = :phone, email = :email', $data) != 1) {
+ Util::traceError('Could not create new user in DB');
+ }
+ // Make it superadmin if first user. This method sucks as it's a race condition but hey...
+ $ret = Database::queryFirst('SELECT Count(*) AS num FROM user');
+ if ($ret !== false && $ret['num'] == 1) {
+ Database::exec('UPDATE user SET permissions = 1');
+ EventLog::clear();
+ EventLog::info('Created first user ' . $_POST['user']);
+ } else {
+ EventLog::info(User::getName() . ' created user ' . $_POST['user']);
+ }
+ Message::addInfo('adduser-success');
+ Util::redirect('?do=Session&action=login');
+ }
+ }
+ }
+
+ protected function doRender()
+ {
+ // No user was added, check if current user is allowed to add a new user
+ // Currently you can only add users if there is no user yet. :)
+ if (!User::hasPermission('superadmin') && Database::queryFirst('SELECT userid FROM user LIMIT 1') !== false) {
+ Message::addError('adduser-disabled');
+ } else {
+
+ Render::setTitle(Dictionary::translate('lang_createUser'));
+ Render::addTemplate('page-adduser', $_POST);
+ }
+ }
+
+}
diff --git a/modules/adduser/templates/page-adduser.html b/modules/adduser/templates/page-adduser.html
new file mode 100644
index 00000000..0b097890
--- /dev/null
+++ b/modules/adduser/templates/page-adduser.html
@@ -0,0 +1,28 @@
+
\ No newline at end of file
diff --git a/modules/backup.inc.php b/modules/backup.inc.php
deleted file mode 100644
index 24352a9a..00000000
--- a/modules/backup.inc.php
+++ /dev/null
@@ -1,134 +0,0 @@
-action = Request::post('action');
- if ($this->action === 'backup') {
- $this->backup();
- } elseif ($this->action === 'restore') {
- $this->restore();
- }
- }
-
- protected function doRender()
- {
- Render::setTitle(Dictionary::translate('lang_titleBackup'));
- if ($this->action === 'restore') {
- Render::addTemplate('backup/restore', $this->templateData);
- } else {
- Render::addScriptBottom('fileselect');
- Render::addTemplate('backup/_page');
- }
- }
-
- private function backup()
- {
- $task = Taskmanager::submit('BackupRestore', array('mode' => 'backup'));
- if (!isset($task['id'])) {
- Message::addError('backup-failed');
- Util::redirect('?do=Backup');
- }
- $task = Taskmanager::waitComplete($task, 30000);
- if (!Taskmanager::isFinished($task) || !isset($task['data']['backupFile'])) {
- Taskmanager::addErrorMessage($task);
- Util::redirect('?do=Backup');
- }
- while ((@ob_get_level()) > 0)
- @ob_end_clean();
- $fh = @fopen($task['data']['backupFile'], 'rb');
- if ($fh === false) {
- Message::addError('error-read', $task['data']['backupFile']);
- Util::redirect('?do=Backup');
- }
- Header('Content-Type: application/octet-stream', true);
- Header('Content-Disposition: attachment; filename=' . 'satellite-backup_v' . Database::getExpectedSchemaVersion() . '_' . date('Y.m.d-H.i.s') . '.tgz');
- Header('Content-Length: ' . @filesize($task['data']['backupFile']));
- while (!feof($fh)) {
- $data = fread($fh, 16000);
- if ($data === false) {
- EventLog::failure('Could not stream system backup to browser - backup corrupted!');
- die("\r\n\nDOWNLOAD INTERRUPTED!\n");
- }
- echo $data;
- @ob_flush();
- @flush();
- }
- @fclose($fh);
- @unlink($task['data']['backupFile']);
- die();
- }
-
- private function restore()
- {
- if (!isset($_FILES['backupfile'])) {
- Message::addError('missing-file');
- Util::redirect('?do=Backup');
- }
- if ($_FILES['backupfile']['error'] != UPLOAD_ERR_OK) {
- Message::addError('upload-failed', Util::uploadErrorString($_FILES['backupfile']['error']));
- Util::redirect('?do=Backup');
- }
- $tempfile = '/tmp/bwlp-' . mt_rand(1, 100000) . '-' . crc32($_SERVER['REMOTE_ADDR']) . '.tgz';
- if (!move_uploaded_file($_FILES['backupfile']['tmp_name'], $tempfile)) {
- Message::addError('error-write', $tempfile);
- Util::redirect('?do=Backup');
- }
- // Got uploaded file, now shut down all the daemons etc.
- $parent = Trigger::stopDaemons(null, $this->templateData);
- // Unmount store
- $task = Taskmanager::submit('MountVmStore', array(
- 'address' => 'null',
- 'type' => 'images',
- 'parentTask' => $parent,
- 'failOnParentFail' => false
- ));
- if (isset($task['id'])) {
- $this->templateData['mountid'] = $task['id'];
- $parent = $task['id'];
- }
- EventLog::info('Creating backup, v' . Database::getExpectedSchemaVersion() . ' on ' . Property::getServerIp());
- // Finally run restore
- $task = Taskmanager::submit('BackupRestore', array(
- 'mode' => 'restore',
- 'backupFile' => $tempfile,
- 'parentTask' => $parent,
- 'failOnParentFail' => false,
- 'restoreOpenslx' => Request::post('restore_openslx', 'off') === 'on',
- 'restoreDozmod' => Request::post('restore_dozmod', 'off') === 'on',
- ));
- if (isset($task['id'])) {
- $this->templateData['restoreid'] = $task['id'];
- $parent = $task['id'];
- TaskmanagerCallback::addCallback($task, 'dbRestored');
- }
- // Wait a bit
- $task = Taskmanager::submit('SleepTask', array(
- 'seconds' => 3,
- 'parentTask' => $parent,
- 'failOnParentFail' => false
- ));
- if (isset($task['id']))
- $parent = $task['id'];
- // Reboot
- $task = Taskmanager::submit('Reboot', array(
- 'parentTask' => $parent,
- 'failOnParentFail' => false
- ));
- // Leave this comment so the i18n scanner finds it:
- // Message::addSuccess('restore-done');
- if (isset($task['id']))
- $this->templateData['rebootid'] = $task['id'];
- }
-
-}
diff --git a/modules/backup/config.json b/modules/backup/config.json
new file mode 100644
index 00000000..4e7fa5fb
--- /dev/null
+++ b/modules/backup/config.json
@@ -0,0 +1,4 @@
+{
+ "category":"content",
+ "enabled":"true"
+}
diff --git a/modules/backup/module.inc.php b/modules/backup/module.inc.php
new file mode 100644
index 00000000..4095f875
--- /dev/null
+++ b/modules/backup/module.inc.php
@@ -0,0 +1,164 @@
+action = Request::post('action');
+ if ($this->action === 'backup') {
+ $this->backup();
+ } elseif ($this->action === 'restore') {
+ $this->restore();
+ }
+ }
+
+ protected function doRender()
+ {
+ Render::setTitle(Dictionary::translate('lang_titleBackup'));
+ if ($this->action === 'restore') {
+ Render::addTemplate('restore', $this->templateData);
+ } else {
+ Render::addScriptBottom('fileselect');
+ Render::addTemplate('_page');
+ }
+ }
+
+ private function backup()
+ {
+ $task = Taskmanager::submit('BackupRestore', array('mode' => 'backup'));
+ if (!isset($task['id'])) {
+ Message::addError('backup-failed');
+ Util::redirect('?do=Backup');
+ }
+ $task = Taskmanager::waitComplete($task, 30000);
+ if (!Taskmanager::isFinished($task) || !isset($task['data']['backupFile'])) {
+ Taskmanager::addErrorMessage($task);
+ Util::redirect('?do=Backup');
+ }
+ while ((@ob_get_level()) > 0)
+ @ob_end_clean();
+ $fh = @fopen($task['data']['backupFile'], 'rb');
+ if ($fh === false) {
+ Message::addError('error-read', $task['data']['backupFile']);
+ Util::redirect('?do=Backup');
+ }
+ Header('Content-Type: application/octet-stream', true);
+ Header('Content-Disposition: attachment; filename=' . 'satellite-backup_v' . Database::getExpectedSchemaVersion() . '_' . date('Y.m.d-H.i.s') . '.tgz');
+ Header('Content-Length: ' . @filesize($task['data']['backupFile']));
+ while (!feof($fh)) {
+ $data = fread($fh, 16000);
+ if ($data === false) {
+ EventLog::failure('Could not stream system backup to browser - backup corrupted!');
+ die("\r\n\nDOWNLOAD INTERRUPTED!\n");
+ }
+ echo $data;
+ @ob_flush();
+ @flush();
+ }
+ @fclose($fh);
+ @unlink($task['data']['backupFile']);
+ die();
+ }
+
+ private function restore()
+ {
+ if (!isset($_FILES['backupfile'])) {
+ Message::addError('missing-file');
+ Util::redirect('?do=Backup');
+ }
+ if ($_FILES['backupfile']['error'] != UPLOAD_ERR_OK) {
+ Message::addError('upload-failed', Util::uploadErrorString($_FILES['backupfile']['error']));
+ Util::redirect('?do=Backup');
+ }
+ $tempfile = '/tmp/bwlp-' . mt_rand(1, 100000) . '-' . crc32($_SERVER['REMOTE_ADDR']) . '.tgz';
+ if (!move_uploaded_file($_FILES['backupfile']['tmp_name'], $tempfile)) {
+ Message::addError('error-write', $tempfile);
+ Util::redirect('?do=Backup');
+ }
+ // Got uploaded file, now shut down all the daemons etc.
+ $parent = Trigger::stopDaemons(null, $this->templateData);
+ // Unmount store
+ $task = Taskmanager::submit('MountVmStore', array(
+ 'address' => 'null',
+ 'type' => 'images',
+ 'parentTask' => $parent,
+ 'failOnParentFail' => false
+ ));
+ if (isset($task['id'])) {
+ $this->templateData['mountid'] = $task['id'];
+ $parent = $task['id'];
+ }
+ EventLog::info('Creating backup, v' . Database::getExpectedSchemaVersion() . ' on ' . Property::getServerIp());
+ // Finally run restore
+ $task = Taskmanager::submit('BackupRestore', array(
+ 'mode' => 'restore',
+ 'backupFile' => $tempfile,
+ 'parentTask' => $parent,
+ 'failOnParentFail' => false,
+ 'restoreOpenslx' => Request::post('restore_openslx', 'off') === 'on',
+ 'restoreDozmod' => Request::post('restore_dozmod', 'off') === 'on',
+ ));
+ if (isset($task['id'])) {
+ $this->templateData['restoreid'] = $task['id'];
+ $parent = $task['id'];
+ TaskmanagerCallback::addCallback($task, 'dbRestored');
+ }
+ // Wait a bit
+ $task = Taskmanager::submit('SleepTask', array(
+ 'seconds' => 3,
+ 'parentTask' => $parent,
+ 'failOnParentFail' => false
+ ));
+ if (isset($task['id']))
+ $parent = $task['id'];
+ // Reboot
+ $task = Taskmanager::submit('Reboot', array(
+ 'parentTask' => $parent,
+ 'failOnParentFail' => false
+ ));
+ // Leave this comment so the i18n scanner finds it:
+ // Message::addSuccess('restore-done');
+ if (isset($task['id']))
+ $this->templateData['rebootid'] = $task['id'];
+ }
+ private function stopDaemons($parent)
+ {
+ $task = Taskmanager::submit('SyncdaemonLauncher', array(
+ 'operation' => 'stop',
+ 'parentTask' => $parent,
+ 'failOnParentFail' => false
+ ));
+ if (isset($task['id'])) {
+ $this->templateData['syncid'] = $task['id'];
+ $parent = $task['id'];
+ }
+ $task = Taskmanager::submit('DozmodLauncher', array(
+ 'operation' => 'stop',
+ 'parentTask' => $parent,
+ 'failOnParentFail' => false
+ ));
+ if (isset($task['id'])) {
+ $this->templateData['dmsdid'] = $task['id'];
+ $parent = $task['id'];
+ }
+ $task = Taskmanager::submit('LdadpLauncher', array(
+ 'ids' => array(),
+ 'parentTask' => $parent,
+ 'failOnParentFail' => false
+ ));
+ if (isset($task['id'])) {
+ $this->templateData['ldadpid'] = $task['id'];
+ $parent = $task['id'];
+ }
+ return $parent;
+ }
+}
diff --git a/modules/backup/templates/_page.html b/modules/backup/templates/_page.html
new file mode 100644
index 00000000..47b5a174
--- /dev/null
+++ b/modules/backup/templates/_page.html
@@ -0,0 +1,41 @@
+{{lang_backupRestore}}
+
+
+
+
\ No newline at end of file
diff --git a/modules/backup/templates/restore.html b/modules/backup/templates/restore.html
new file mode 100644
index 00000000..4494a993
--- /dev/null
+++ b/modules/backup/templates/restore.html
@@ -0,0 +1,62 @@
+
+
{{lang_backup}}
+
+
+
{{lang_stopping}} syncdaemon
+
{{lang_stopping}} dmsd
+
{{lang_stopping}} ldadp
+
{{lang_stopping}} vmstore
+
{{lang_restoreConfig}}
+
{{lang_reboot}}
+
+
+ {{lang_restoreFailed}}
+
+
+
+
+
+
+
+
diff --git a/modules/baseconfig.inc.php b/modules/baseconfig.inc.php
deleted file mode 100644
index 152e3372..00000000
--- a/modules/baseconfig.inc.php
+++ /dev/null
@@ -1,127 +0,0 @@
-qry_extra[] = array(
- 'name' => 'distroid',
- 'value' => (int)$_REQUEST['distroid'],
- 'table' => 'setting_distro',
- );
- if (isset($_REQUEST['poolid'])) {
- $this->qry_extra[] = array(
- 'name' => 'poolid',
- 'value' => (int)$_REQUEST['poolid'],
- 'table' => 'setting_pool',
- );
- }
- }
-
- if (isset($_POST['setting']) && is_array($_POST['setting'])) {
- if (User::hasPermission('superadmin')) {
- // Build variables for specific sub-settings
- $qry_insert = '';
- $qry_values = '';
- foreach ($this->qry_extra as $item) {
- $qry_insert = ', ' . $item['name'];
- $qry_values = ', :' . $item['name'];
- }
- // Load all existing config options to validate input
- $res = Database::simpleQuery('SELECT setting, validator FROM setting');
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- $key = $row['setting'];
- $validator = $row['validator'];
- $displayValue = (isset($_POST['setting'][$key]) ? $_POST['setting'][$key] : '');
- // Validate data first!
- $mangledValue = Validator::validate($validator, $displayValue);
- if ($mangledValue === false) {
- Message::addWarning('value-invalid', $key, $displayValue);
- continue;
- }
- // Now put into DB
- Database::exec("INSERT INTO setting_global (setting, value, displayvalue $qry_insert)
- VALUES (:key, :value, :displayvalue $qry_values)
- ON DUPLICATE KEY UPDATE value = :value, displayvalue = :displayvalue",
- $this->qry_extra + array(
- 'key' => $key,
- 'value' => $mangledValue,
- 'displayvalue' => $displayValue
- )
- );
- }
- Message::addSuccess('settings-updated');
- Util::redirect('?do=BaseConfig');
- }
- }
- }
-
- protected function doRender()
- {
- if (!User::hasPermission('superadmin')) {
- Message::addError('no-permission');
- Util::redirect('?do=Main');
- }
- // Build left joins for specific settings
- $joins = '';
- foreach ($this->qry_extra as $item) {
- $joins .= " LEFT JOIN {$item['table']} ";
- }
- // List global config option
- $settings = array();
- $res = Database::simpleQuery('SELECT cat_setting.catid, setting.setting, setting.defaultvalue, setting.permissions, setting.validator, tbl.displayvalue
- FROM setting
- INNER JOIN cat_setting USING (catid)
- LEFT JOIN setting_global AS tbl USING (setting)
- ORDER BY cat_setting.sortval ASC, setting.setting ASC');
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- $row['description'] = Util::markup(Dictionary::translate('settings/setting', $row['setting']));
- if (is_null($row['displayvalue'])) $row['displayvalue'] = $row['defaultvalue'];
- $row['item'] = $this->makeInput($row['validator'], $row['setting'], $row['displayvalue']);
- $settings[$row['catid']]['settings'][] = $row;
- $settings[$row['catid']]['category_name'] = Dictionary::translate('settings/cat_setting', 'cat_' . $row['catid']);
- }
-
- Render::addTemplate('baseconfig/_page', array(
- 'categories' => array_values($settings)
- ));
- }
-
- /**
- * Create html snippet for setting, based on given validator
- * @param type $validator
- * @return boolean
- */
- private function makeInput($validator, $setting, $current)
- {
- $parts = explode(':', $validator, 2);
- if ($parts[0] === 'list') {
- $items = explode('|', $parts[1]);
- $ret = '';
- foreach ($items as $item) {
- if ($item === $current) {
- $ret .= '' . $item . ' ';
- } else {
- $ret .= '' . $item . ' ';
- }
- }
- return $ret . ' ';
- }
- // Password field guessing
- if (stripos($validator, 'password') !== false) {
- $type = Property::getPasswordFieldType();
- } else {
- $type = 'text';
- }
- // Fallback: single line input
- return ' ';
- }
-
-}
diff --git a/modules/baseconfig/config.json b/modules/baseconfig/config.json
new file mode 100644
index 00000000..93209f62
--- /dev/null
+++ b/modules/baseconfig/config.json
@@ -0,0 +1,4 @@
+{
+ "category":"settings",
+ "enabled":"true"
+}
diff --git a/modules/baseconfig/module.inc.php b/modules/baseconfig/module.inc.php
new file mode 100644
index 00000000..3d187d30
--- /dev/null
+++ b/modules/baseconfig/module.inc.php
@@ -0,0 +1,127 @@
+qry_extra[] = array(
+ 'name' => 'distroid',
+ 'value' => (int)$_REQUEST['distroid'],
+ 'table' => 'setting_distro',
+ );
+ if (isset($_REQUEST['poolid'])) {
+ $this->qry_extra[] = array(
+ 'name' => 'poolid',
+ 'value' => (int)$_REQUEST['poolid'],
+ 'table' => 'setting_pool',
+ );
+ }
+ }
+
+ if (isset($_POST['setting']) && is_array($_POST['setting'])) {
+ if (User::hasPermission('superadmin')) {
+ // Build variables for specific sub-settings
+ $qry_insert = '';
+ $qry_values = '';
+ foreach ($this->qry_extra as $item) {
+ $qry_insert = ', ' . $item['name'];
+ $qry_values = ', :' . $item['name'];
+ }
+ // Load all existing config options to validate input
+ $res = Database::simpleQuery('SELECT setting, validator FROM setting');
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $key = $row['setting'];
+ $validator = $row['validator'];
+ $displayValue = (isset($_POST['setting'][$key]) ? $_POST['setting'][$key] : '');
+ // Validate data first!
+ $mangledValue = Validator::validate($validator, $displayValue);
+ if ($mangledValue === false) {
+ Message::addWarning('value-invalid', $key, $displayValue);
+ continue;
+ }
+ // Now put into DB
+ Database::exec("INSERT INTO setting_global (setting, value, displayvalue $qry_insert)
+ VALUES (:key, :value, :displayvalue $qry_values)
+ ON DUPLICATE KEY UPDATE value = :value, displayvalue = :displayvalue",
+ $this->qry_extra + array(
+ 'key' => $key,
+ 'value' => $mangledValue,
+ 'displayvalue' => $displayValue
+ )
+ );
+ }
+ Message::addSuccess('settings-updated');
+ Util::redirect('?do=BaseConfig');
+ }
+ }
+ }
+
+ protected function doRender()
+ {
+ if (!User::hasPermission('superadmin')) {
+ Message::addError('no-permission');
+ Util::redirect('?do=Main');
+ }
+ // Build left joins for specific settings
+ $joins = '';
+ foreach ($this->qry_extra as $item) {
+ $joins .= " LEFT JOIN {$item['table']} ";
+ }
+ // List global config option
+ $settings = array();
+ $res = Database::simpleQuery('SELECT cat_setting.catid, setting.setting, setting.defaultvalue, setting.permissions, setting.validator, tbl.displayvalue
+ FROM setting
+ INNER JOIN cat_setting USING (catid)
+ LEFT JOIN setting_global AS tbl USING (setting)
+ ORDER BY cat_setting.sortval ASC, setting.setting ASC');
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $row['description'] = Util::markup(Dictionary::translate('settings/setting', $row['setting']));
+ if (is_null($row['displayvalue'])) $row['displayvalue'] = $row['defaultvalue'];
+ $row['item'] = $this->makeInput($row['validator'], $row['setting'], $row['displayvalue']);
+ $settings[$row['catid']]['settings'][] = $row;
+ $settings[$row['catid']]['category_name'] = Dictionary::translate('settings/cat_setting', 'cat_' . $row['catid']);
+ $settings[$row['catid']]['category_id'] = $row['catid'];
+ }
+ Render::addTemplate('_page', array(
+ 'categories' => array_values($settings)
+ ));
+ }
+
+ /**
+ * Create html snippet for setting, based on given validator
+ * @param type $validator
+ * @return boolean
+ */
+ private function makeInput($validator, $setting, $current)
+ {
+ $parts = explode(':', $validator, 2);
+ if ($parts[0] === 'list') {
+ $items = explode('|', $parts[1]);
+ $ret = '';
+ foreach ($items as $item) {
+ if ($item === $current) {
+ $ret .= '' . $item . ' ';
+ } else {
+ $ret .= '' . $item . ' ';
+ }
+ }
+ return $ret . ' ';
+ }
+ // Password field guessing
+ if (stripos($validator, 'password') !== false) {
+ $type = Property::getPasswordFieldType();
+ } else {
+ $type = 'text';
+ }
+ // Fallback: single line input
+ return ' ';
+ }
+
+}
diff --git a/modules/baseconfig/templates/_page.html b/modules/baseconfig/templates/_page.html
new file mode 100644
index 00000000..c72f0a5e
--- /dev/null
+++ b/modules/baseconfig/templates/_page.html
@@ -0,0 +1,186 @@
+{{lang_basicConfiguration}}
+{{lang_clientRelatedConfig}}
+
+
+
+
+
+
+
+
+
diff --git a/modules/citymanagement/config.json b/modules/citymanagement/config.json
new file mode 100644
index 00000000..3cd4afd3
--- /dev/null
+++ b/modules/citymanagement/config.json
@@ -0,0 +1,5 @@
+{
+ "category":"hidden",
+ "enabled":"false",
+ "permission":"0"
+}
diff --git a/modules/citymanagement/module.inc.php b/modules/citymanagement/module.inc.php
new file mode 100644
index 00000000..acc30bf9
--- /dev/null
+++ b/modules/citymanagement/module.inc.php
@@ -0,0 +1,81 @@
+page = $p;
+ else
+ $this->page = 1;
+ switch(Request::post('action')){
+ case "edit":
+ $this->edit(Request::post('cityid'),Request::post('name'));
+ break;
+ case "create":
+ $this->create(Request::post('name'));
+ break;
+ case "delete":
+ $this->delete(Request::post('cityid'));
+ break;
+ }
+
+
+ if (!User::hasPermission('superadmin')) {
+ Message::addError('no-permission');
+ Util::redirect('?do=Main');
+ }
+
+ }
+
+ protected function doRender()
+ {
+ // load every city
+ $cities = array();
+ $res = Database::simpleQuery("SELECT cityid, name FROM cities ORDER BY cityid DESC");
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $cities[] = array(
+ 'id' => $row['cityid'],
+ 'name' => $row['name'],
+ );
+ }
+
+ $pag = new Pagination($cities,$this->page);
+
+ Render::addTemplate('page-citymanagement', array(
+ 'cities' => $pag->getItems(),
+ 'pages' => $pag->getPagination()
+ ));
+ }
+
+ private function edit($cityid, $newname){
+ $data = array (
+ 'cityid' => $cityid,
+ 'name' => $newname,
+ );
+ Database::exec ( 'UPDATE cities SET name = :name WHERE cityid = :cityid', $data );
+ Message::addSuccess('update-city');
+ }
+
+ private function create($name){
+ $data = array (
+ 'name' => $name,
+ );
+ Database::exec('INSERT INTO cities(name) VALUES( :name )',$data);
+ Message::addSuccess('add-city');
+ }
+
+ private function delete($cityid){
+ $data = array (
+ 'cityid' => $cityid
+ );
+ Database::exec ( 'DELETE FROM cities WHERE cityid = :cityid', $data );
+ Message::addSuccess('delete-city');
+ }
+}
diff --git a/modules/citymanagement/templates/page-citymanagement.html b/modules/citymanagement/templates/page-citymanagement.html
new file mode 100644
index 00000000..6d7750cf
--- /dev/null
+++ b/modules/citymanagement/templates/page-citymanagement.html
@@ -0,0 +1,77 @@
+
+
+
+
+ {{lang_cityInfo}}
+
+
+
+
+
+{{#cities}}
+
+{{/cities}}
diff --git a/modules/dozmod.inc.php b/modules/dozmod.inc.php
deleted file mode 100644
index 98cacd59..00000000
--- a/modules/dozmod.inc.php
+++ /dev/null
@@ -1,252 +0,0 @@
-mailHandler();
- }
- if ($action === 'delimages') {
- $result = $this->handleDeleteImages();
- if (!empty($result)) {
- Message::addInfo('delete-images', $result);
- }
- Util::redirect('?do=DozMod');
- }
- }
-
- protected function doRender()
- {
- $this->listDeletePendingImages();
- // Mail config
- $conf = Database::queryFirst('SELECT value FROM sat.configuration WHERE parameter = :param', array('param' => 'mailconfig'));
- if ($conf != null) {
- $conf = @json_decode($conf['value'], true);
- if (is_array($conf)) {
- $conf['set_' . $conf['ssl']] = 'selected="selected"';
- }
- }
- Render::addTemplate('dozmod/mailconfig', $conf);
- // User list for making people admin
- $this->listUsers();
- $this->listOrganizations();
- }
-
- private function listDeletePendingImages()
- {
- $res = Database::simpleQuery("SELECT b.displayname,"
- . " own.firstname, own.lastname, own.email,"
- . " v.imageversionid, v.createtime, v.filesize, v.deletestate,"
- . " lat.expiretime AS latexptime, lat.deletestate AS latdelstate"
- . " FROM sat.imageversion v"
- . " INNER JOIN sat.imagebase b ON (b.imagebaseid = v.imagebaseid)"
- . " INNER JOIN sat.user own ON (b.ownerid = own.userid)"
- . " LEFT JOIN sat.imageversion lat ON (b.latestversionid = lat.imageversionid)"
- . " WHERE v.deletestate <> 'KEEP'"
- . " ORDER BY b.displayname ASC, v.createtime ASC");
- $NOW = time();
- $rows = array();
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- if ($row['latexptime'] > $NOW && $row['latdelstate'] === 'KEEP') {
- $row['hasNewerClass'] = 'glyphicon-ok green';
- } else {
- $row['hasNewerClass'] = 'glyphicon-remove red';
- }
- if ($row['deletestate'] === 'WANT_DELETE') {
- $row['name_extra_class'] = 'slx-strike';
- }
- $row['version'] = date('d.m.Y H:i:s', $row['createtime']);
- $row['filesize'] = Util::readableFileSize($row['filesize']);
- $rows[] = $row;
- }
- if (empty($rows))
- return;
- Render::addTemplate('dozmod/images-delete', array('images' => $rows));
- }
-
- private function cleanMailArray()
- {
- $keys = array('host', 'port', 'ssl', 'senderAddress', 'replyTo', 'username', 'password', 'serverName');
- $data = array();
- foreach ($keys as $key) {
- $data[$key] = Request::post($key, '');
- settype($data[$key], 'string');
- if (is_numeric($data[$key])) {
- settype($data[$key], 'int');
- }
- }
- return $data;
- }
-
- protected function doAjax()
- {
- if (!User::hasPermission('superadmin'))
- return;
-
- $action = Request::post('action');
- if ($action === 'mail') {
- $this->handleTestMail();
- } elseif ($action === 'setmail' || $action === 'setsu' || $action == 'setlogin') {
- $this->setUserOption($action);
- } elseif ($action === 'setorglogin') {
- $this->setOrgOption($action);
- } elseif ($action === 'delimages') {
- die($this->handleDeleteImages());
- }
- }
-
- private function handleDeleteImages()
- {
- $images = Request::post('images', false);
- if (is_array($images)) {
- foreach ($images as $image => $val) {
- if (strtolower($val) !== 'on')
- continue;
- Database::exec("UPDATE sat.imageversion SET deletestate = 'WANT_DELETE'"
- . " WHERE deletestate = 'SHOULD_DELETE' AND imageversionid = :imageversionid", array(
- 'imageversionid' => $image
- ));
- }
- if (!empty($images)) {
- $ret = Download::asStringPost('http://127.0.0.1:9080/do/delete-images', false, 2, $code);
- if ($code == 999) {
- $ret .= "\nConnection to DMSD failed.";
- }
- return $ret;
- }
- }
- return false;
- }
-
- private function handleTestMail()
- {
- $do = Request::post('button');
- if ($do === 'test') {
- // Prepare array
- $data = $this->cleanMailArray();
- Header('Content-Type: text/plain; charset=utf-8');
- $data['recipient'] = Request::post('recipient', '');
- if (!preg_match('/.+@.+\..+/', $data['recipient'])) {
- $result = 'No recipient given!';
- } else {
- $result = Download::asStringPost('http://127.0.0.1:9080/do/mailtest', $data, 2, $code);
- }
- die($result);
- }
- }
-
- private function mailHandler()
- {
- // Check action
- $do = Request::post('button');
- if ($do === 'save') {
- // Prepare array
- $data = $this->cleanMailArray();
- $data = json_encode($data);
- Database::exec('INSERT INTO sat.configuration (parameter, value)'
- . ' VALUES (:param, :value)'
- . ' ON DUPLICATE KEY UPDATE value = VALUES(value)', array(
- 'param' => 'mailconfig',
- 'value' => $data
- ));
- Message::addSuccess('mail-config-saved');
- }
- Util::redirect('?do=DozMod');
- }
-
- private function listUsers()
- {
- $res = Database::simpleQuery('SELECT userid, firstname, lastname, email, lastlogin, user.canlogin, issuperuser, emailnotifications,'
- . ' organization.displayname AS orgname FROM sat.user'
- . ' LEFT JOIN sat.organization USING (organizationid)'
- . ' ORDER BY lastname ASC, firstname ASC');
- $rows = array();
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- $row['canlogin'] = $this->checked($row['canlogin']);
- $row['issuperuser'] = $this->checked($row['issuperuser']);
- $row['emailnotifications'] = $this->checked($row['emailnotifications']);
- $row['lastlogin'] = date('d.m.Y', $row['lastlogin']);
- $rows[] = $row;
- }
- Render::addTemplate('dozmod/userlist', array('users' => $rows));
- }
-
- private function listOrganizations()
- {
- $res = Database::simpleQuery('SELECT organizationid, displayname, canlogin FROM sat.organization'
- . ' ORDER BY displayname ASC');
- $rows = array();
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- $row['canlogin'] = $this->checked($row['canlogin']);
- $rows[] = $row;
- }
- Render::addTemplate('dozmod/orglist', array('organizations' => $rows));
- }
-
- private function checked($val)
- {
- if ($val)
- return 'checked="checked"';
- return '';
- }
-
- private function setUserOption($option)
- {
- $val = (string) Request::post('value', '-');
- if ($val !== '1' && $val !== '0')
- die('Nein');
- if ($option === 'setmail') {
- $field = 'emailnotifications';
- } elseif ($option === 'setsu') {
- $field = 'issuperuser';
- } elseif ($option === 'setlogin') {
- $field = 'canlogin';
- } else {
- die('Unknown');
- }
- $user = (string) Request::post('userid', '?');
- $ret = Database::exec("UPDATE sat.user SET $field = :onoff WHERE userid = :userid", array(
- 'userid' => $user,
- 'onoff' => $val
- ));
- error_log("Setting $field to $val for $user - affected: $ret");
- if ($ret === false)
- die('Error');
- if ($ret == 0)
- die(1 - $val);
- die($val);
- }
-
- private function setOrgOption($option)
- {
- $val = (string) Request::post('value', '-');
- if ($val !== '1' && $val !== '0')
- die('Nein');
- if ($option === 'setorglogin') {
- $field = 'canlogin';
- } else {
- die('Unknown');
- }
- $ret = Database::exec("UPDATE sat.organization SET $field = :onoff WHERE organizationid = :organizationid", array(
- 'organizationid' => (string) Request::post('organizationid', ''),
- 'onoff' => $val
- ));
- if ($ret === false)
- die('Error');
- if ($ret === 0)
- die(1 - $val);
- die($val);
- }
-
-}
diff --git a/modules/dozmod/config.json b/modules/dozmod/config.json
new file mode 100644
index 00000000..4e7fa5fb
--- /dev/null
+++ b/modules/dozmod/config.json
@@ -0,0 +1,4 @@
+{
+ "category":"content",
+ "enabled":"true"
+}
diff --git a/modules/dozmod/module.inc.php b/modules/dozmod/module.inc.php
new file mode 100644
index 00000000..f98d8952
--- /dev/null
+++ b/modules/dozmod/module.inc.php
@@ -0,0 +1,252 @@
+mailHandler();
+ }
+ if ($action === 'delimages') {
+ $result = $this->handleDeleteImages();
+ if (!empty($result)) {
+ Message::addInfo('delete-images', $result);
+ }
+ Util::redirect('?do=DozMod');
+ }
+ }
+
+ protected function doRender()
+ {
+ $this->listDeletePendingImages();
+ // Mail config
+ $conf = Database::queryFirst('SELECT value FROM sat.configuration WHERE parameter = :param', array('param' => 'mailconfig'));
+ if ($conf != null) {
+ $conf = @json_decode($conf['value'], true);
+ if (is_array($conf)) {
+ $conf['set_' . $conf['ssl']] = 'selected="selected"';
+ }
+ }
+ Render::addTemplate('mailconfig', $conf);
+ // User list for making people admin
+ $this->listUsers();
+ $this->listOrganizations();
+ }
+
+ private function listDeletePendingImages()
+ {
+ $res = Database::simpleQuery("SELECT b.displayname,"
+ . " own.firstname, own.lastname, own.email,"
+ . " v.imageversionid, v.createtime, v.filesize, v.deletestate,"
+ . " lat.expiretime AS latexptime, lat.deletestate AS latdelstate"
+ . " FROM sat.imageversion v"
+ . " INNER JOIN sat.imagebase b ON (b.imagebaseid = v.imagebaseid)"
+ . " INNER JOIN sat.user own ON (b.ownerid = own.userid)"
+ . " LEFT JOIN sat.imageversion lat ON (b.latestversionid = lat.imageversionid)"
+ . " WHERE v.deletestate <> 'KEEP'"
+ . " ORDER BY b.displayname ASC, v.createtime ASC");
+ $NOW = time();
+ $rows = array();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ if ($row['latexptime'] > $NOW && $row['latdelstate'] === 'KEEP') {
+ $row['hasNewerClass'] = 'glyphicon-ok green';
+ } else {
+ $row['hasNewerClass'] = 'glyphicon-remove red';
+ }
+ if ($row['deletestate'] === 'WANT_DELETE') {
+ $row['name_extra_class'] = 'slx-strike';
+ }
+ $row['version'] = date('d.m.Y H:i:s', $row['createtime']);
+ $row['filesize'] = Util::readableFileSize($row['filesize']);
+ $rows[] = $row;
+ }
+ if (empty($rows))
+ return;
+ Render::addTemplate('images-delete', array('images' => $rows));
+ }
+
+ private function cleanMailArray()
+ {
+ $keys = array('host', 'port', 'ssl', 'senderAddress', 'replyTo', 'username', 'password', 'serverName');
+ $data = array();
+ foreach ($keys as $key) {
+ $data[$key] = Request::post($key, '');
+ settype($data[$key], 'string');
+ if (is_numeric($data[$key])) {
+ settype($data[$key], 'int');
+ }
+ }
+ return $data;
+ }
+
+ protected function doAjax()
+ {
+ if (!User::hasPermission('superadmin'))
+ return;
+
+ $action = Request::post('action');
+ if ($action === 'mail') {
+ $this->handleTestMail();
+ } elseif ($action === 'setmail' || $action === 'setsu' || $action == 'setlogin') {
+ $this->setUserOption($action);
+ } elseif ($action === 'setorglogin') {
+ $this->setOrgOption($action);
+ } elseif ($action === 'delimages') {
+ die($this->handleDeleteImages());
+ }
+ }
+
+ private function handleDeleteImages()
+ {
+ $images = Request::post('images', false);
+ if (is_array($images)) {
+ foreach ($images as $image => $val) {
+ if (strtolower($val) !== 'on')
+ continue;
+ Database::exec("UPDATE sat.imageversion SET deletestate = 'WANT_DELETE'"
+ . " WHERE deletestate = 'SHOULD_DELETE' AND imageversionid = :imageversionid", array(
+ 'imageversionid' => $image
+ ));
+ }
+ if (!empty($images)) {
+ $ret = Download::asStringPost('http://127.0.0.1:9080/do/delete-images', false, 2, $code);
+ if ($code == 999) {
+ $ret .= "\nConnection to DMSD failed.";
+ }
+ return $ret;
+ }
+ }
+ return false;
+ }
+
+ private function handleTestMail()
+ {
+ $do = Request::post('button');
+ if ($do === 'test') {
+ // Prepare array
+ $data = $this->cleanMailArray();
+ Header('Content-Type: text/plain; charset=utf-8');
+ $data['recipient'] = Request::post('recipient', '');
+ if (!preg_match('/.+@.+\..+/', $data['recipient'])) {
+ $result = 'No recipient given!';
+ } else {
+ $result = Download::asStringPost('http://127.0.0.1:9080/do/mailtest', $data, 2, $code);
+ }
+ die($result);
+ }
+ }
+
+ private function mailHandler()
+ {
+ // Check action
+ $do = Request::post('button');
+ if ($do === 'save') {
+ // Prepare array
+ $data = $this->cleanMailArray();
+ $data = json_encode($data);
+ Database::exec('INSERT INTO sat.configuration (parameter, value)'
+ . ' VALUES (:param, :value)'
+ . ' ON DUPLICATE KEY UPDATE value = VALUES(value)', array(
+ 'param' => 'mailconfig',
+ 'value' => $data
+ ));
+ Message::addSuccess('mail-config-saved');
+ }
+ Util::redirect('?do=DozMod');
+ }
+
+ private function listUsers()
+ {
+ $res = Database::simpleQuery('SELECT userid, firstname, lastname, email, lastlogin, user.canlogin, issuperuser, emailnotifications,'
+ . ' organization.displayname AS orgname FROM sat.user'
+ . ' LEFT JOIN sat.organization USING (organizationid)'
+ . ' ORDER BY lastname ASC, firstname ASC');
+ $rows = array();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $row['canlogin'] = $this->checked($row['canlogin']);
+ $row['issuperuser'] = $this->checked($row['issuperuser']);
+ $row['emailnotifications'] = $this->checked($row['emailnotifications']);
+ $row['lastlogin'] = date('d.m.Y', $row['lastlogin']);
+ $rows[] = $row;
+ }
+ Render::addTemplate('userlist', array('users' => $rows));
+ }
+
+ private function listOrganizations()
+ {
+ $res = Database::simpleQuery('SELECT organizationid, displayname, canlogin FROM sat.organization'
+ . ' ORDER BY displayname ASC');
+ $rows = array();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $row['canlogin'] = $this->checked($row['canlogin']);
+ $rows[] = $row;
+ }
+ Render::addTemplate('orglist', array('organizations' => $rows));
+ }
+
+ private function checked($val)
+ {
+ if ($val)
+ return 'checked="checked"';
+ return '';
+ }
+
+ private function setUserOption($option)
+ {
+ $val = (string) Request::post('value', '-');
+ if ($val !== '1' && $val !== '0')
+ die('Nein');
+ if ($option === 'setmail') {
+ $field = 'emailnotifications';
+ } elseif ($option === 'setsu') {
+ $field = 'issuperuser';
+ } elseif ($option === 'setlogin') {
+ $field = 'canlogin';
+ } else {
+ die('Unknown');
+ }
+ $user = (string) Request::post('userid', '?');
+ $ret = Database::exec("UPDATE sat.user SET $field = :onoff WHERE userid = :userid", array(
+ 'userid' => $user,
+ 'onoff' => $val
+ ));
+ error_log("Setting $field to $val for $user - affected: $ret");
+ if ($ret === false)
+ die('Error');
+ if ($ret == 0)
+ die(1 - $val);
+ die($val);
+ }
+
+ private function setOrgOption($option)
+ {
+ $val = (string) Request::post('value', '-');
+ if ($val !== '1' && $val !== '0')
+ die('Nein');
+ if ($option === 'setorglogin') {
+ $field = 'canlogin';
+ } else {
+ die('Unknown');
+ }
+ $ret = Database::exec("UPDATE sat.organization SET $field = :onoff WHERE organizationid = :organizationid", array(
+ 'organizationid' => (string) Request::post('organizationid', ''),
+ 'onoff' => $val
+ ));
+ if ($ret === false)
+ die('Error');
+ if ($ret === 0)
+ die(1 - $val);
+ die($val);
+ }
+
+}
diff --git a/modules/dozmod/templates/images-delete.html b/modules/dozmod/templates/images-delete.html
new file mode 100644
index 00000000..c4cbfd34
--- /dev/null
+++ b/modules/dozmod/templates/images-delete.html
@@ -0,0 +1,57 @@
+{{lang_heading}}
+
+
+
+ {{lang_subHeading}}
+
+
+
{{lang_description}}
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/dozmod/templates/mailconfig.html b/modules/dozmod/templates/mailconfig.html
new file mode 100644
index 00000000..b19776c0
--- /dev/null
+++ b/modules/dozmod/templates/mailconfig.html
@@ -0,0 +1,91 @@
+{{lang_mailConfigHeadline}}
+
+
+
+ {{lang_mailConfig}}
+
+
+
{{lang_mailDescription}}
+
[BETA] Diese Funktionalität ist neu. Wir bitten um Nachsicht, falls es Situationen gibt, in denen zu viele
+ oder zu wenige Nachrichten verschickt werden.
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/dozmod/templates/orglist.html b/modules/dozmod/templates/orglist.html
new file mode 100644
index 00000000..d325cc4d
--- /dev/null
+++ b/modules/dozmod/templates/orglist.html
@@ -0,0 +1,51 @@
+{{lang_organizationList}}
+
+
+
+ {{lang_organizationListHeader}}
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/dozmod/templates/userlist.html b/modules/dozmod/templates/userlist.html
new file mode 100644
index 00000000..a76eae5e
--- /dev/null
+++ b/modules/dozmod/templates/userlist.html
@@ -0,0 +1,62 @@
+{{lang_userList}}
+
+
+
+ {{lang_userListHeader}}
+
+
+
{{lang_userListDescription}}
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/eventlog.inc.php b/modules/eventlog.inc.php
deleted file mode 100644
index a070f5a2..00000000
--- a/modules/eventlog.inc.php
+++ /dev/null
@@ -1,70 +0,0 @@
-exec();
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- $day = date('d.m.Y', $row['dateline']);
- if ($day === $today) {
- $day = Dictionary::translate('today');
- } elseif ($day === $yesterday) {
- $day = Dictionary::translate('yesterday');
- }
- $row['date'] = $day . date(' H:i', $row['dateline']);
- $row['icon'] = $this->typeToIcon($row['logtypeid']);
- $row['color'] = $this->typeToColor($row['logtypeid']);
- $lines[] = $row;
- }
-
- $paginate->render('eventlog/_page', array(
- 'list' => $lines
- ));
- }
-
- private function typeToIcon($type)
- {
- switch ($type) {
- case 'info':
- return 'ok';
- case 'warning':
- return 'exclamation-sign';
- case 'failure':
- return 'remove';
- default:
- return 'question-sign';
- }
- }
-
- private function typeToColor($type)
- {
- switch ($type) {
- case 'info':
- return '';
- case 'warning':
- return 'orange';
- case 'failure':
- return 'red';
- default:
- return '';
- }
- }
-
-}
diff --git a/modules/eventlog/config.json b/modules/eventlog/config.json
new file mode 100644
index 00000000..d42dc3e3
--- /dev/null
+++ b/modules/eventlog/config.json
@@ -0,0 +1,4 @@
+{
+ "category":"status",
+ "enabled":"true"
+}
diff --git a/modules/eventlog/module.inc.php b/modules/eventlog/module.inc.php
new file mode 100644
index 00000000..7cfc8a55
--- /dev/null
+++ b/modules/eventlog/module.inc.php
@@ -0,0 +1,70 @@
+exec();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $day = date('d.m.Y', $row['dateline']);
+ if ($day === $today) {
+ $day = Dictionary::translate('today');
+ } elseif ($day === $yesterday) {
+ $day = Dictionary::translate('yesterday');
+ }
+ $row['date'] = $day . date(' H:i', $row['dateline']);
+ $row['icon'] = $this->typeToIcon($row['logtypeid']);
+ $row['color'] = $this->typeToColor($row['logtypeid']);
+ $lines[] = $row;
+ }
+
+ $paginate->render('_page', array(
+ 'list' => $lines
+ ));
+ }
+
+ private function typeToIcon($type)
+ {
+ switch ($type) {
+ case 'info':
+ return 'ok';
+ case 'warning':
+ return 'exclamation-sign';
+ case 'failure':
+ return 'remove';
+ default:
+ return 'question-sign';
+ }
+ }
+
+ private function typeToColor($type)
+ {
+ switch ($type) {
+ case 'info':
+ return '';
+ case 'warning':
+ return 'orange';
+ case 'failure':
+ return 'red';
+ default:
+ return '';
+ }
+ }
+
+}
diff --git a/modules/eventlog/templates/_page.html b/modules/eventlog/templates/_page.html
new file mode 100644
index 00000000..2e657805
--- /dev/null
+++ b/modules/eventlog/templates/_page.html
@@ -0,0 +1,41 @@
+{{lang_eventLog}}
+{{{pagenav}}}
+
+
+
+ {{lang_when}}
+ {{lang_event}}
+ {{lang_details}}
+
+
+ {{#list}}
+
+
+ {{date}}
+ {{description}}
+ {{#extra}}
+ »
+
+ {{/extra}}
+
+ {{/list}}
+
+
+{{{pagenav}}}
+
+
diff --git a/modules/imgmanagement/config.json b/modules/imgmanagement/config.json
new file mode 100644
index 00000000..49daa91a
--- /dev/null
+++ b/modules/imgmanagement/config.json
@@ -0,0 +1,5 @@
+{
+ "category":"hidden",
+ "enabled":"false",
+ "permission":"1"
+}
diff --git a/modules/imgmanagement/module.inc.php b/modules/imgmanagement/module.inc.php
new file mode 100644
index 00000000..27844e23
--- /dev/null
+++ b/modules/imgmanagement/module.inc.php
@@ -0,0 +1,76 @@
+baselocation = '/var/images/';
+ $this->images = array();
+
+ error_reporting(E_ALL);
+ ini_set('display_errors','on');
+
+ Session::get('token');
+
+ }
+
+ protected function doRender()
+ {
+ /*get city of user !!!!NOT TESTED!!!!
+
+ $data=array( 'id'= User.getId());
+ $res = Database::exec("SELECT cityid FROM user WHERE userid=:id",$data);
+ $cityid = $res->fetch(PDO::FETCH_ASSOC);
+ $res = Database::exec("SELECT name FROM cities WHERE cityid=:cityid",$cityid);
+ $city = $res->fetch(PDO::FETCH_ASSOC);
+ $location = $baselocation . $city;
+
+
+ verify type of vars (string concatenation and more)
+ !!!!NOT TESTED!!!!
+ */
+
+ error_reporting(E_ALL);
+ ini_set('display_errors','on');
+ //Search images on location specified
+ $location = $this->baselocation . 'curitiba/*';
+ //Gets the configuration of each image
+ $config = substr($location,0,-1).'config.json';
+ $imgsactive = json_decode(file_get_contents($config),true);
+ $images = glob($location, GLOB_ONLYDIR);
+ $actives = array();
+ $deactives= array();
+ foreach($images as &$imgname){
+ $imgname= substr($imgname, strlen($location)-1);
+ //fill associative array (img->active[true/false])
+ $this->images[$imgname] = isset($imgsactive[$imgname])?$imgsactive[$imgname] : false;
+ if($this->images[$imgname]){
+ array_push($actives, array('name' => $imgname));
+ }else{
+ array_push($deactives, array('name'=>$imgname));
+ }
+ }
+
+ //Save eventually new images to config.json
+ $fp = fopen($config,'w');
+ fwrite($fp,json_encode($this->images));
+ fclose($fp);
+ Render::addTemplate('page-imgmanagement', array(
+ 'deactives' => $deactives,
+ 'actives' => $actives));
+ }
+}
diff --git a/modules/imgmanagement/templates/page-imgmanagement.html b/modules/imgmanagement/templates/page-imgmanagement.html
new file mode 100644
index 00000000..795ae39f
--- /dev/null
+++ b/modules/imgmanagement/templates/page-imgmanagement.html
@@ -0,0 +1,46 @@
+
+
+ Gerenciamento de Imagens
+
+
+
+
+ Imagens em azul indicam imagens criadas pelo usuário
+
+
+
+
+
+ {{#deactives}}
+ {{name}}
+ {{/deactives}}
+
+
+
+
+
+
+
+
+ {{#actives}}
+ {{name}}
+ {{/actives}}
+
+
+
+
+
Upload de Nova Imagem
+ Gerar IPXE
+
+
+
+
+
+
diff --git a/modules/internetaccess.inc.php b/modules/internetaccess.inc.php
deleted file mode 100644
index c142285d..00000000
--- a/modules/internetaccess.inc.php
+++ /dev/null
@@ -1,51 +0,0 @@
-{{lang_internetAccess}}
+
+
diff --git a/modules/internetaccess/templates/restart.html b/modules/internetaccess/templates/restart.html
new file mode 100644
index 00000000..effe1feb
--- /dev/null
+++ b/modules/internetaccess/templates/restart.html
@@ -0,0 +1,22 @@
+
+
{{lang_serviceRestart}}
+
+
{{lang_restarting}} syncdaemon
+
{{lang_restarting}} dmsd
+
{{lang_restarting}} ldadp
+
+ {{lang_restartFailed}}
+
+
+
+
+
diff --git a/modules/locations.inc.php b/modules/locations.inc.php
deleted file mode 100644
index d4d0c85f..00000000
--- a/modules/locations.inc.php
+++ /dev/null
@@ -1,348 +0,0 @@
-action = Request::post('action');
- if ($this->action === 'updatelocation') {
- $this->updateLocation();
- } elseif ($this->action === 'addlocations') {
- $this->addLocations();
- }
- }
-
- private function addLocations()
- {
- $names = Request::post('newlocation', false);
- $parents = Request::post('newparent', false);
- if (!is_array($names) || !is_array($parents)) {
- Message::addError('empty-field');
- Util::redirect('?do=Locations');
- }
- $locs = Location::getLocations();
- $count = 0;
- foreach ($names as $idx => $name) {
- $name = trim($name);
- if (empty($name)) continue;
- $parent = isset($parents[$idx]) ? (int)$parents[$idx] : 0;
- if ($parent !== 0) {
- $ok = false;
- foreach ($locs as $loc) {
- if ($loc['locationid'] == $parent) {
- $ok = true;
- }
- }
- if (!$ok) {
- Message::addWarning('value-invalid', 'parentlocationid', $parent);
- continue;
- }
- }
- Database::exec("INSERT INTO location (parentlocationid, locationname)"
- . " VALUES (:parent, :name)", array(
- 'parent' => $parent,
- 'name' => $name
- ));
- $count++;
- }
- Message::addSuccess('added-x-entries', $count);
- Util::redirect('?do=Locations');
- }
-
- private function updateLocation()
- {
- $locationId = Request::post('locationid', false, 'integer');
- $del = Request::post('deletelocation', false, 'integer');
- if ($locationId === false) {
- Message::addError('parameter-missing', 'locationid');
- Util::redirect('?do=Locations');
- }
- $location = Database::queryFirst('SELECT locationid, parentlocationid, locationname FROM location'
- . ' WHERE locationid = :lid', array('lid' => $locationId));
- if ($location === false) {
- Message::addError('value-invalid', 'locationid', $locationId);
- Util::redirect('?do=Locations');
- }
- // Delete location?
- if ($locationId === $del) {
- $this->deleteLocation($location);
- }
- // Update subnets
- $this->updateLocationSubnets($location);
- // Insert subnets
- $this->addNewLocationSubnets($location); // TODO
- // Update location!
- $this->updateLocationData($location);
- Util::redirect('?do=Locations');
- }
-
- private function deleteLocation($location)
- {
- $locationId = (int)$location['locationid'];
- $ids = $locationId;
- if (Request::post('recursive', false) === 'on') {
- $rows = Location::queryLocations();
- $rows = Location::buildTree($rows, $locationId);
- $rows = Location::extractIds($rows);
- if (!empty($rows)) {
- $ids .= ',' . implode(',', $rows);
- }
- }
- $subs = Database::exec("DELETE FROM subnet WHERE locationid IN ($ids)");
- $locs = Database::exec("DELETE FROM location WHERE locationid IN ($ids)");
- Database::exec('UPDATE location SET parentlocationid = :newparent WHERE parentlocationid = :oldparent', array(
- 'newparent' => $location['parentlocationid'],
- 'oldparent' => $location['locationid']
- ));
- Message::addSuccess('location-deleted', $locs, $subs);
- Util::redirect('?do=Locations');
- }
-
- private function updateLocationData($location)
- {
- $locationId = (int)$location['locationid'];
- $newParent = Request::post('parentlocationid', false, 'integer');
- $newName = Request::post('locationname', false, 'string');
- if ($newName === false || preg_match('/^\s*$/', $newName)) {
- if ($newName !== false) {
- Message::addWarning('value-invalid', 'location name', $newName);
- }
- $newName = $location['locationname'];
- }
- if ($newParent === false) {
- $newParent = $location['parentlocationid'];
- } else if ($newParent !== 0) {
- $rows = Location::queryLocations();
- $all = Location::extractIds(Location::buildTree($rows));
- if (!in_array($newParent, $all) || $newParent === $locationId) {
- Message::addWarning('value-invalid', 'parent', $newParent);
- $newParent = $location['parentlocationid'];
- } else {
- $rows = Location::extractIds(Location::buildTree($rows, $locationId));
- if (in_array($newParent, $rows)) {
- Message::addWarning('value-invalid', 'parent', $newParent);
- $newParent = $location['parentlocationid'];
- }
- }
- }
- $ret = Database::exec('UPDATE location SET parentlocationid = :parent, locationname = :name'
- . ' WHERE locationid = :lid', array(
- 'lid' => $locationId,
- 'parent' => $newParent,
- 'name' => $newName
- ));
- if ($ret > 0) {
- Message::addSuccess('location-updated', $newName);
- }
- }
-
- private function updateLocationSubnets($location)
- {
- $locationId = (int)$location['locationid'];
- // Deletion first
- $dels = Request::post('deletesubnet', false);
- if (is_array($dels)) {
- $count = 0;
- $stmt = Database::prepare('DELETE FROM subnet WHERE subnetid = :id');
- foreach ($dels as $key => $value) {
- if (!is_numeric($key) || $value !== 'on') continue;
- if ($stmt->execute(array('id' => $key))) {
- $count += $stmt->rowCount();
- }
- }
- if ($count > 0) {
- Message::addInfo('subnets-deleted', $count);
- }
- }
- // Now actual updates
- // TODO: Warn on mismatch/overlap (should lie entirely in parent's subnet, not overlap with others)
- $starts = Request::post('startaddr', false);
- $ends = Request::post('endaddr', false);
- if (!is_array($starts) || !is_array($ends)) {
- return;
- }
- $count = 0;
- $stmt = Database::prepare('UPDATE subnet SET startaddr = :start, endaddr = :end'
- . ' WHERE subnetid = :id');
- foreach ($starts as $key => $start) {
- if (!isset($ends[$key]) || !is_numeric($key)) continue;
- $end = $ends[$key];
- list($startLong, $endLong) = $this->rangeToLong($start, $end);
- if ($startLong === false) {
- Message::addWarning('value-invalid', 'start addr', $start);
- }
- if ($endLong === false) {
- Message::addWarning('value-invalid', 'end addr', $start);
- }
- if ($startLong === false || $endLong === false) continue;
- if ($startLong > $endLong) {
- Message::addWarning('value-invalid', 'range', $start . ' - ' . $end);
- continue;
- }
- if ($stmt->execute(array('id' => $key, 'start' => $startLong, 'end' => $endLong))) {
- $count += $stmt->rowCount();
- }
- }
- if ($count > 0) {
- Message::addInfo('subnets-updated', $count);
- }
- }
-
- private function addNewLocationSubnets($location)
- {
- $locationId = (int)$location['locationid'];
- $starts = Request::post('newstartaddr', false);
- $ends = Request::post('newendaddr', false);
- if (!is_array($starts) || !is_array($ends)) {
- return;
- }
- $count = 0;
- $stmt = Database::prepare('INSERT INTO subnet SET startaddr = :start, endaddr = :end, locationid = :location');
- foreach ($starts as $key => $start) {
- if (!isset($ends[$key]) || !is_numeric($key)) continue;
- $end = $ends[$key];
- list($startLong, $endLong) = $this->rangeToLong($start, $end);
- if ($startLong === false) {
- Message::addWarning('value-invalid', 'new start addr', $start);
- }
- if ($endLong === false) {
- Message::addWarning('value-invalid', 'new end addr', $start);
- }
- if ($startLong === false || $endLong === false) continue;
- if ($startLong > $endLong) {
- Message::addWarning('value-invalid', 'range', $start . ' - ' . $end);
- continue;
- }
- if ($stmt->execute(array('location' => $locationId, 'start' => $startLong, 'end' => $endLong))) {
- $count += $stmt->rowCount();
- }
- }
- if ($count > 0) {
- Message::addInfo('subnets-created', $count);
- }
- }
-
- /*
- * Rendering normal pages
- */
-
- protected function doRender()
- {
- //Render::setTitle(Dictionary::translate('lang_titleBackup'));
- $getAction = Request::get('action');
- if (empty($getAction)) {
- // Until we have a main landing page?
- Util::redirect('?do=Locations&action=showlocations');
- }
- if ($getAction === 'showsubnets') {
- $res = Database::simpleQuery("SELECT subnetid, startaddr, endaddr, locationid FROM subnet");
- $rows = array();
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- $row['startaddr'] = long2ip($row['startaddr']);
- $row['endaddr'] = long2ip($row['endaddr']);
- $row['locations'] = Location::getLocations($row['locationid']);
- $rows[] = $row;
- }
- Render::addTemplate('locations/subnets', array('list' => $rows));
- } elseif ($getAction === 'showlocations') {
- $locs = Location::getLocations();
- Render::addTemplate('locations/locations', array('list' => $locs));
- }
- }
-
- /*
- * Ajax
- */
-
- protected function doAjax()
- {
- User::load();
- if (!User::isLoggedIn()) {
- die('Unauthorized');
- }
- $action = Request::any('action');
- if ($action === 'showlocation') {
- $this->ajaxShowLocation();
- }
- }
-
- private function ajaxShowLocation()
- {
- $locationId = Request::any('locationid', 0, 'integer');
- $loc = Database::queryFirst('SELECT locationid, parentlocationid, locationname FROM location WHERE locationid = :lid',
- array('lid' => $locationId));
- if ($loc === false) {
- die('Unknown locationid');
- }
- $res = Database::simpleQuery("SELECT subnetid, startaddr, endaddr FROM subnet WHERE locationid = :lid",
- array('lid' => $locationId));
- $rows = array();
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- $row['startaddr'] = long2ip($row['startaddr']);
- $row['endaddr'] = long2ip($row['endaddr']);
- $rows[] = $row;
- }
- $data = array(
- 'locationid' => $loc['locationid'],
- 'locationname' => $loc['locationname'],
- 'list' => $rows,
- 'parents' => Location::getLocations($loc['parentlocationid'], $locationId, true)
- );
- // if (moduleEnabled(DOZMOD) {
- $lectures = Database::queryFirst('SELECT Count(*) AS cnt FROM sat.lecture l '
- . ' INNER JOIN sat.lecture_x_location ll ON (l.lectureid = ll.lectureid AND ll.locationid = :lid)',
- array('lid' => $locationId));
- $data['lectures'] = $lectures['cnt'];
- // }
- // Get clients matching this location's subnet(s)
- $mres = Database::simpleQuery("SELECT lastseen, logintime FROM machine"
- . " INNER JOIN subnet ON (INET_ATON(machine.clientip) BETWEEN startaddr AND endaddr)"
- . " WHERE subnet.locationid = :lid OR machine.locationid = :lid", array('lid' => $locationId));
- $count = $online = $used = 0;
- $DL = time() - 605;
- while ($row = $mres->fetch(PDO::FETCH_ASSOC)) {
- $count++;
- if ($row['lastseen'] > $DL) {
- $online++;
- if ($row['logintime'] != 0) {
- $used++;
- }
- }
- }
- $data['machines'] = $count;
- $data['machines_online'] = $online;
- $data['machines_used'] = $used;
- $data['used_percent'] = round(100 * $used / $online);
- echo Render::parse('locations/location-subnets', $data);
- }
-
- /*
- * Helpers
- */
-
- private function rangeToLong($start, $end)
- {
- $startLong = ip2long($start);
- $endLong = ip2long($end);
- if ($startLong !== false) {
- $startLong = sprintf("%u", $startLong);
- }
- if ($endLong !== false) {
- $endLong = sprintf("%u", $endLong);
- }
- return array($startLong, $endLong);
- }
-
-}
diff --git a/modules/locations/config.json b/modules/locations/config.json
new file mode 100644
index 00000000..4e7fa5fb
--- /dev/null
+++ b/modules/locations/config.json
@@ -0,0 +1,4 @@
+{
+ "category":"content",
+ "enabled":"true"
+}
diff --git a/modules/locations/module.inc.php b/modules/locations/module.inc.php
new file mode 100644
index 00000000..60af719b
--- /dev/null
+++ b/modules/locations/module.inc.php
@@ -0,0 +1,348 @@
+action = Request::post('action');
+ if ($this->action === 'updatelocation') {
+ $this->updateLocation();
+ } elseif ($this->action === 'addlocations') {
+ $this->addLocations();
+ }
+ }
+
+ private function addLocations()
+ {
+ $names = Request::post('newlocation', false);
+ $parents = Request::post('newparent', false);
+ if (!is_array($names) || !is_array($parents)) {
+ Message::addError('empty-field');
+ Util::redirect('?do=Locations');
+ }
+ $locs = Location::getLocations();
+ $count = 0;
+ foreach ($names as $idx => $name) {
+ $name = trim($name);
+ if (empty($name)) continue;
+ $parent = isset($parents[$idx]) ? (int)$parents[$idx] : 0;
+ if ($parent !== 0) {
+ $ok = false;
+ foreach ($locs as $loc) {
+ if ($loc['locationid'] == $parent) {
+ $ok = true;
+ }
+ }
+ if (!$ok) {
+ Message::addWarning('value-invalid', 'parentlocationid', $parent);
+ continue;
+ }
+ }
+ Database::exec("INSERT INTO location (parentlocationid, locationname)"
+ . " VALUES (:parent, :name)", array(
+ 'parent' => $parent,
+ 'name' => $name
+ ));
+ $count++;
+ }
+ Message::addSuccess('added-x-entries', $count);
+ Util::redirect('?do=Locations');
+ }
+
+ private function updateLocation()
+ {
+ $locationId = Request::post('locationid', false, 'integer');
+ $del = Request::post('deletelocation', false, 'integer');
+ if ($locationId === false) {
+ Message::addError('parameter-missing', 'locationid');
+ Util::redirect('?do=Locations');
+ }
+ $location = Database::queryFirst('SELECT locationid, parentlocationid, locationname FROM location'
+ . ' WHERE locationid = :lid', array('lid' => $locationId));
+ if ($location === false) {
+ Message::addError('value-invalid', 'locationid', $locationId);
+ Util::redirect('?do=Locations');
+ }
+ // Delete location?
+ if ($locationId === $del) {
+ $this->deleteLocation($location);
+ }
+ // Update subnets
+ $this->updateLocationSubnets($location);
+ // Insert subnets
+ $this->addNewLocationSubnets($location); // TODO
+ // Update location!
+ $this->updateLocationData($location);
+ Util::redirect('?do=Locations');
+ }
+
+ private function deleteLocation($location)
+ {
+ $locationId = (int)$location['locationid'];
+ $ids = $locationId;
+ if (Request::post('recursive', false) === 'on') {
+ $rows = Location::queryLocations();
+ $rows = Location::buildTree($rows, $locationId);
+ $rows = Location::extractIds($rows);
+ if (!empty($rows)) {
+ $ids .= ',' . implode(',', $rows);
+ }
+ }
+ $subs = Database::exec("DELETE FROM subnet WHERE locationid IN ($ids)");
+ $locs = Database::exec("DELETE FROM location WHERE locationid IN ($ids)");
+ Database::exec('UPDATE location SET parentlocationid = :newparent WHERE parentlocationid = :oldparent', array(
+ 'newparent' => $location['parentlocationid'],
+ 'oldparent' => $location['locationid']
+ ));
+ Message::addSuccess('location-deleted', $locs, $subs);
+ Util::redirect('?do=Locations');
+ }
+
+ private function updateLocationData($location)
+ {
+ $locationId = (int)$location['locationid'];
+ $newParent = Request::post('parentlocationid', false, 'integer');
+ $newName = Request::post('locationname', false, 'string');
+ if ($newName === false || preg_match('/^\s*$/', $newName)) {
+ if ($newName !== false) {
+ Message::addWarning('value-invalid', 'location name', $newName);
+ }
+ $newName = $location['locationname'];
+ }
+ if ($newParent === false) {
+ $newParent = $location['parentlocationid'];
+ } else if ($newParent !== 0) {
+ $rows = Location::queryLocations();
+ $all = Location::extractIds(Location::buildTree($rows));
+ if (!in_array($newParent, $all) || $newParent === $locationId) {
+ Message::addWarning('value-invalid', 'parent', $newParent);
+ $newParent = $location['parentlocationid'];
+ } else {
+ $rows = Location::extractIds(Location::buildTree($rows, $locationId));
+ if (in_array($newParent, $rows)) {
+ Message::addWarning('value-invalid', 'parent', $newParent);
+ $newParent = $location['parentlocationid'];
+ }
+ }
+ }
+ $ret = Database::exec('UPDATE location SET parentlocationid = :parent, locationname = :name'
+ . ' WHERE locationid = :lid', array(
+ 'lid' => $locationId,
+ 'parent' => $newParent,
+ 'name' => $newName
+ ));
+ if ($ret > 0) {
+ Message::addSuccess('location-updated', $newName);
+ }
+ }
+
+ private function updateLocationSubnets($location)
+ {
+ $locationId = (int)$location['locationid'];
+ // Deletion first
+ $dels = Request::post('deletesubnet', false);
+ if (is_array($dels)) {
+ $count = 0;
+ $stmt = Database::prepare('DELETE FROM subnet WHERE subnetid = :id');
+ foreach ($dels as $key => $value) {
+ if (!is_numeric($key) || $value !== 'on') continue;
+ if ($stmt->execute(array('id' => $key))) {
+ $count += $stmt->rowCount();
+ }
+ }
+ if ($count > 0) {
+ Message::addInfo('subnets-deleted', $count);
+ }
+ }
+ // Now actual updates
+ // TODO: Warn on mismatch/overlap (should lie entirely in parent's subnet, not overlap with others)
+ $starts = Request::post('startaddr', false);
+ $ends = Request::post('endaddr', false);
+ if (!is_array($starts) || !is_array($ends)) {
+ return;
+ }
+ $count = 0;
+ $stmt = Database::prepare('UPDATE subnet SET startaddr = :start, endaddr = :end'
+ . ' WHERE subnetid = :id');
+ foreach ($starts as $key => $start) {
+ if (!isset($ends[$key]) || !is_numeric($key)) continue;
+ $end = $ends[$key];
+ list($startLong, $endLong) = $this->rangeToLong($start, $end);
+ if ($startLong === false) {
+ Message::addWarning('value-invalid', 'start addr', $start);
+ }
+ if ($endLong === false) {
+ Message::addWarning('value-invalid', 'end addr', $start);
+ }
+ if ($startLong === false || $endLong === false) continue;
+ if ($startLong > $endLong) {
+ Message::addWarning('value-invalid', 'range', $start . ' - ' . $end);
+ continue;
+ }
+ if ($stmt->execute(array('id' => $key, 'start' => $startLong, 'end' => $endLong))) {
+ $count += $stmt->rowCount();
+ }
+ }
+ if ($count > 0) {
+ Message::addInfo('subnets-updated', $count);
+ }
+ }
+
+ private function addNewLocationSubnets($location)
+ {
+ $locationId = (int)$location['locationid'];
+ $starts = Request::post('newstartaddr', false);
+ $ends = Request::post('newendaddr', false);
+ if (!is_array($starts) || !is_array($ends)) {
+ return;
+ }
+ $count = 0;
+ $stmt = Database::prepare('INSERT INTO subnet SET startaddr = :start, endaddr = :end, locationid = :location');
+ foreach ($starts as $key => $start) {
+ if (!isset($ends[$key]) || !is_numeric($key)) continue;
+ $end = $ends[$key];
+ list($startLong, $endLong) = $this->rangeToLong($start, $end);
+ if ($startLong === false) {
+ Message::addWarning('value-invalid', 'new start addr', $start);
+ }
+ if ($endLong === false) {
+ Message::addWarning('value-invalid', 'new end addr', $start);
+ }
+ if ($startLong === false || $endLong === false) continue;
+ if ($startLong > $endLong) {
+ Message::addWarning('value-invalid', 'range', $start . ' - ' . $end);
+ continue;
+ }
+ if ($stmt->execute(array('location' => $locationId, 'start' => $startLong, 'end' => $endLong))) {
+ $count += $stmt->rowCount();
+ }
+ }
+ if ($count > 0) {
+ Message::addInfo('subnets-created', $count);
+ }
+ }
+
+ /*
+ * Rendering normal pages
+ */
+
+ protected function doRender()
+ {
+ //Render::setTitle(Dictionary::translate('lang_titleBackup'));
+ $getAction = Request::get('action');
+ if (empty($getAction)) {
+ // Until we have a main landing page?
+ Util::redirect('?do=Locations&action=showlocations');
+ }
+ if ($getAction === 'showsubnets') {
+ $res = Database::simpleQuery("SELECT subnetid, startaddr, endaddr, locationid FROM subnet");
+ $rows = array();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $row['startaddr'] = long2ip($row['startaddr']);
+ $row['endaddr'] = long2ip($row['endaddr']);
+ $row['locations'] = Location::getLocations($row['locationid']);
+ $rows[] = $row;
+ }
+ Render::addTemplate('subnets', array('list' => $rows));
+ } elseif ($getAction === 'showlocations') {
+ $locs = Location::getLocations();
+ Render::addTemplate('locations', array('list' => $locs));
+ }
+ }
+
+ /*
+ * Ajax
+ */
+
+ protected function doAjax()
+ {
+ User::load();
+ if (!User::isLoggedIn()) {
+ die('Unauthorized');
+ }
+ $action = Request::any('action');
+ if ($action === 'showlocation') {
+ $this->ajaxShowLocation();
+ }
+ }
+
+ private function ajaxShowLocation()
+ {
+ $locationId = Request::any('locationid', 0, 'integer');
+ $loc = Database::queryFirst('SELECT locationid, parentlocationid, locationname FROM location WHERE locationid = :lid',
+ array('lid' => $locationId));
+ if ($loc === false) {
+ die('Unknown locationid');
+ }
+ $res = Database::simpleQuery("SELECT subnetid, startaddr, endaddr FROM subnet WHERE locationid = :lid",
+ array('lid' => $locationId));
+ $rows = array();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $row['startaddr'] = long2ip($row['startaddr']);
+ $row['endaddr'] = long2ip($row['endaddr']);
+ $rows[] = $row;
+ }
+ $data = array(
+ 'locationid' => $loc['locationid'],
+ 'locationname' => $loc['locationname'],
+ 'list' => $rows,
+ 'parents' => Location::getLocations($loc['parentlocationid'], $locationId, true)
+ );
+ // if (moduleEnabled(DOZMOD) {
+ $lectures = Database::queryFirst('SELECT Count(*) AS cnt FROM sat.lecture l '
+ . ' INNER JOIN sat.lecture_x_location ll ON (l.lectureid = ll.lectureid AND ll.locationid = :lid)',
+ array('lid' => $locationId));
+ $data['lectures'] = $lectures['cnt'];
+ // }
+ // Get clients matching this location's subnet(s)
+ $mres = Database::simpleQuery("SELECT lastseen, logintime FROM machine"
+ . " INNER JOIN subnet ON (INET_ATON(machine.clientip) BETWEEN startaddr AND endaddr)"
+ . " WHERE subnet.locationid = :lid OR machine.locationid = :lid", array('lid' => $locationId));
+ $count = $online = $used = 0;
+ $DL = time() - 605;
+ while ($row = $mres->fetch(PDO::FETCH_ASSOC)) {
+ $count++;
+ if ($row['lastseen'] > $DL) {
+ $online++;
+ if ($row['logintime'] != 0) {
+ $used++;
+ }
+ }
+ }
+ $data['machines'] = $count;
+ $data['machines_online'] = $online;
+ $data['machines_used'] = $used;
+ $data['used_percent'] = round(100 * $used / $online);
+ echo Render::parse('location-subnets', $data);
+ }
+
+ /*
+ * Helpers
+ */
+
+ private function rangeToLong($start, $end)
+ {
+ $startLong = ip2long($start);
+ $endLong = ip2long($end);
+ if ($startLong !== false) {
+ $startLong = sprintf("%u", $startLong);
+ }
+ if ($endLong !== false) {
+ $endLong = sprintf("%u", $endLong);
+ }
+ return array($startLong, $endLong);
+ }
+
+}
diff --git a/modules/locations/templates/location-subnets.html b/modules/locations/templates/location-subnets.html
new file mode 100644
index 00000000..76b7442a
--- /dev/null
+++ b/modules/locations/templates/location-subnets.html
@@ -0,0 +1,73 @@
+
+
{{lang_locationSettings}}
+
+
+
{{lang_locationInfo}}
+
+ {{lang_referencingLectures}}: {{lectures}}
+
+
+
\ No newline at end of file
diff --git a/modules/locations/templates/locations.html b/modules/locations/templates/locations.html
new file mode 100644
index 00000000..76c8f97c
--- /dev/null
+++ b/modules/locations/templates/locations.html
@@ -0,0 +1,96 @@
+
+
+
{{lang_locationsMainHeading}}
+
+
+ #
+ {{lang_locationName}}
+
+
+ {{#list}}
+
+ {{locationid}}
+
{{locationname}}
+
+ {{lang_edit}}
+
+
+ {{/list}}
+
+
+
+
diff --git a/modules/locations/templates/subnets.html b/modules/locations/templates/subnets.html
new file mode 100644
index 00000000..2294f42b
--- /dev/null
+++ b/modules/locations/templates/subnets.html
@@ -0,0 +1,35 @@
+
+
+
{{lang_listOfSubnets}}
+
+
diff --git a/modules/main.inc.php b/modules/main.inc.php
deleted file mode 100644
index 9af1afef..00000000
--- a/modules/main.inc.php
+++ /dev/null
@@ -1,54 +0,0 @@
-sysconfig = !file_exists(CONFIG_HTTP_DIR . '/default/config.tgz');
- $this->minilinux = !file_exists(CONFIG_HTTP_DIR . '/default/kernel') || !file_exists(CONFIG_HTTP_DIR . '/default/initramfs-stage31') || !file_exists(CONFIG_HTTP_DIR . '/default/stage32.sqfs');
- $this->vmstore = !is_array(Property::getVmStoreConfig());
- $this->ipxe = !preg_match('/^\d+\.\d+\.\d+\.\d+$/', Property::getServerIp());
- Property::setNeedsSetup(($this->sysconfig || $this->minilinux || $this->vmstore || $this->ipxe) ? 1 : 0);
- $res = Database::queryFirst("SELECT Count(*) AS cnt FROM sat.imageversion WHERE deletestate = 'SHOULD_DELETE'");
- $this->delPending = isset($res['cnt']) ? $res['cnt'] : 0;
- }
- }
-
- protected function doRender()
- {
- // Render::setTitle('abc');
-
- if (!User::isLoggedIn()) {
- Render::addTemplate('page-main-guest', array(
- 'register' => (Database::queryFirst('SELECT userid FROM user LIMIT 1') === false)
- ));
- return;
- }
- // Logged in here
-
- Render::addTemplate('page-main', array(
- 'user' => User::getName(),
- 'sysconfig' => $this->sysconfig,
- 'minilinux' => $this->minilinux,
- 'vmstore' => $this->vmstore,
- 'ipxe' => $this->ipxe,
- 'delpending' => $this->delPending
- ));
- }
-
- protected function doAjax()
- {
- User::isLoggedIn();
- die('Status: DB running');
- }
-
-}
diff --git a/modules/main/config.json b/modules/main/config.json
new file mode 100644
index 00000000..d5da4cc8
--- /dev/null
+++ b/modules/main/config.json
@@ -0,0 +1,4 @@
+{
+ "category":"hidden",
+ "enabled":"true"
+}
diff --git a/modules/main/module.inc.php b/modules/main/module.inc.php
new file mode 100644
index 00000000..369d4b54
--- /dev/null
+++ b/modules/main/module.inc.php
@@ -0,0 +1,64 @@
+sysconfig = !file_exists(CONFIG_HTTP_DIR . '/default/config.tgz');
+ $this->minilinux = !file_exists(CONFIG_HTTP_DIR . '/default/kernel') || !file_exists(CONFIG_HTTP_DIR . '/default/initramfs-stage31') || !file_exists(CONFIG_HTTP_DIR . '/default/stage32.sqfs');
+ $this->vmstore = !is_array(Property::getVmStoreConfig());
+ $this->ipxe = !preg_match('/^\d+\.\d+\.\d+\.\d+$/', Property::getServerIp());
+ Property::setNeedsSetup(($this->sysconfig || $this->minilinux || $this->vmstore || $this->ipxe) ? 1 : 0);
+ $res = Database::queryFirst("SELECT Count(*) AS cnt FROM sat.imageversion WHERE deletestate = 'SHOULD_DELETE'", array(), true);
+ $this->delPending = isset($res['cnt']) ? $res['cnt'] : 0;
+ }
+ }
+
+ protected function doRender()
+ {
+ // Render::setTitle('abc');
+
+ if (!User::isLoggedIn()) {
+ Render::addTemplate('page-main-guest', array(
+ 'register' => (Database::queryFirst('SELECT userid FROM user LIMIT 1') === false)
+ ));
+ return;
+ }
+ // Logged in here
+
+ // Load news
+ $lines = array();
+ $paginate = new Paginate("SELECT newsid, dateline, title, content FROM news ORDER BY dateline DESC", 10);
+ $res = $paginate->exec();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ if(count($lines) >= 3) break;
+ $lines[] = $row;
+ }
+
+ Render::addTemplate('page-main', array(
+ 'user' => User::getName(),
+ 'sysconfig' => $this->sysconfig,
+ 'minilinux' => $this->minilinux,
+ 'vmstore' => $this->vmstore,
+ 'ipxe' => $this->ipxe,
+ 'delpending' => $this->delPending,
+ 'news' => $lines
+ ));
+ }
+
+ protected function doAjax()
+ {
+ User::isLoggedIn();
+ die('Status: DB running');
+ }
+
+}
diff --git a/modules/main/templates/dialog-generic.html b/modules/main/templates/dialog-generic.html
new file mode 100644
index 00000000..5face8ce
--- /dev/null
+++ b/modules/main/templates/dialog-generic.html
@@ -0,0 +1,13 @@
+
\ No newline at end of file
diff --git a/modules/main/templates/footer.html b/modules/main/templates/footer.html
new file mode 100644
index 00000000..8cf71a5c
--- /dev/null
+++ b/modules/main/templates/footer.html
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/modules/main/templates/main-menu.html b/modules/main/templates/main-menu.html
new file mode 100644
index 00000000..c4534c2f
--- /dev/null
+++ b/modules/main/templates/main-menu.html
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{#user}}
+
+
+
+ {{/user}}
+ {{^user}}
+ {{lang_login}}
+ {{/user}}
+
+
+
+
+
diff --git a/modules/main/templates/main-menu.html.topnavbar b/modules/main/templates/main-menu.html.topnavbar
new file mode 100644
index 00000000..8da8edd0
--- /dev/null
+++ b/modules/main/templates/main-menu.html.topnavbar
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{#user}}
+
+
+
+ {{/user}}
+ {{^user}}
+ {{lang_login}}
+ {{/user}}
+
+
+
+
+
diff --git a/modules/main/templates/messagebox-error.html b/modules/main/templates/messagebox-error.html
new file mode 100644
index 00000000..873716c9
--- /dev/null
+++ b/modules/main/templates/messagebox-error.html
@@ -0,0 +1 @@
+ {{{message}}}
diff --git a/modules/main/templates/messagebox-info.html b/modules/main/templates/messagebox-info.html
new file mode 100644
index 00000000..eb9d518a
--- /dev/null
+++ b/modules/main/templates/messagebox-info.html
@@ -0,0 +1 @@
+ {{{message}}}
diff --git a/modules/main/templates/messagebox-success.html b/modules/main/templates/messagebox-success.html
new file mode 100644
index 00000000..93674d69
--- /dev/null
+++ b/modules/main/templates/messagebox-success.html
@@ -0,0 +1 @@
+ {{{message}}}
diff --git a/modules/main/templates/messagebox-warning.html b/modules/main/templates/messagebox-warning.html
new file mode 100644
index 00000000..b02e2e8a
--- /dev/null
+++ b/modules/main/templates/messagebox-warning.html
@@ -0,0 +1 @@
+ {{{message}}}
diff --git a/modules/main/templates/page-login.html b/modules/main/templates/page-login.html
new file mode 100644
index 00000000..247e9a55
--- /dev/null
+++ b/modules/main/templates/page-login.html
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/modules/main/templates/page-main-guest.html b/modules/main/templates/page-main-guest.html
new file mode 100644
index 00000000..28b0d04c
--- /dev/null
+++ b/modules/main/templates/page-main-guest.html
@@ -0,0 +1,15 @@
+
+
+
{{lang_welcome}}
+
{{lang_introGuest}}
+ {{#register}}
+
+ {{/register}}
+
{{lang_login}} »
+
+
diff --git a/modules/main/templates/page-main.html b/modules/main/templates/page-main.html
new file mode 100644
index 00000000..39e4e74e
--- /dev/null
+++ b/modules/main/templates/page-main.html
@@ -0,0 +1,36 @@
+
+
{{lang_welcome}}, {{user}}
+
{{lang_intro}}
+
+
+
diff --git a/modules/main/templates/page-minilinux.html b/modules/main/templates/page-minilinux.html
new file mode 100644
index 00000000..dc13e6b0
--- /dev/null
+++ b/modules/main/templates/page-minilinux.html
@@ -0,0 +1,14 @@
+
+
{{lang_listObtained}}
+
+
+
\ No newline at end of file
diff --git a/modules/main/templates/page-news.html b/modules/main/templates/page-news.html
new file mode 100644
index 00000000..8e400498
--- /dev/null
+++ b/modules/main/templates/page-news.html
@@ -0,0 +1,57 @@
+
+
+ {{lang_editNews}}
+
+
+
+
+
+
+ {{lang_newsOld}}
+
+
+
+
+
+
+
+
+ {{lang_date}}
+ {{lang_title}}
+ {{lang_content}}
+
+
+
+
+ {{#list}}
+
+ {{date}}
+ {{title}}
+ {{content}}
+
+ {{lang_show}}
+ {{lang_delete}}
+
+
+ {{/list}}
+
+
+
+
+
+
diff --git a/modules/main/templates/page-syslog.html b/modules/main/templates/page-syslog.html
new file mode 100644
index 00000000..98e94291
--- /dev/null
+++ b/modules/main/templates/page-syslog.html
@@ -0,0 +1,58 @@
+{{lang_clientLog}}
+
+
+
+ {{lang_filter}}
+
+
+ {{lang_not}}
+
+
+ {{lang_go}}
+
+
+
+{{{pagenav}}}
+
+
+
+ {{lang_when}}
+ {{lang_client}}
+ {{lang_event}}
+ {{lang_details}}
+
+
+ {{#list}}
+
+
+ {{date}}
+ {{clientip}}
+ {{description}}
+ {{#extra}}
+ »
+
+ {{/extra}}
+
+ {{/list}}
+
+
+{{{pagenav}}}
+
+
+
+
diff --git a/modules/main/templates/page-vmstore.html b/modules/main/templates/page-vmstore.html
new file mode 100644
index 00000000..fe2c5225
--- /dev/null
+++ b/modules/main/templates/page-vmstore.html
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+
+
{{lang_vmLocationChoose}}
+
+
+ {{lang_intern}}
+
+
+ {{lang_noAdditionalInformation}}
+
+
+
+
+
{{lang_save}}
+
+
+
+
+
+
+
+
+
+
+ {{lang_vmLocationHelp1}}
+
+
+ {{lang_vmLocationHelp2}}
+
+
+ {{lang_vmLocationHelp3}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{lang_nfsHelp1}}
+
+
+/mnt/images 1.2.3.4(rw,no_root_squash,async)
+/mnt/images *(ro,async,nolock)
+
+
+ {{lang_nfsHelp2}}
+
+
+/mnt/images 1.2.3.4(rw,all_squash,anon_uid=1234,async)
+/mnt/images *(ro,async,nolock)
+
+
+
+
+
+
diff --git a/modules/main/templates/pagenav.html b/modules/main/templates/pagenav.html
new file mode 100644
index 00000000..93194999
--- /dev/null
+++ b/modules/main/templates/pagenav.html
@@ -0,0 +1,16 @@
+
+
\ No newline at end of file
diff --git a/modules/main/templates/tm-callback-trigger.html b/modules/main/templates/tm-callback-trigger.html
new file mode 100644
index 00000000..cd03a1fe
--- /dev/null
+++ b/modules/main/templates/tm-callback-trigger.html
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/modules/minilinux.inc.php b/modules/minilinux.inc.php
deleted file mode 100644
index 0853c0db..00000000
--- a/modules/minilinux.inc.php
+++ /dev/null
@@ -1,128 +0,0 @@
- '?do=MiniLinux&async=true&action=list'
- ));
- Render::addFooter('');
- }
-
- protected function doAjax()
- {
- $data = Property::getVersionCheckInformation();
- if (!is_array($data) || !isset($data['systems'])) {
- echo Render::parse('messagebox-error', array(
- 'message' => 'Failed to retrieve the list: ' . print_r($data, true)
- ));
- return;
- }
- $action = Request::any('action');
- $selectedVersion = (int)Request::any('version', 0);
- switch ($action) {
- case 'list':
- $count = 0;
- foreach ($data['systems'] as &$system) {
- // Get latest version, build simple array of all version numbers
- $versionNumbers = array();
- $selected = false;
- foreach ($system['versions'] as $version) {
- if (!is_numeric($version['version']) || $version['version'] < 1)
- continue;
- if ($selectedVersion === 0 && ($selected === false || $selected['version'] < $version['version']))
- $selected = $version;
- elseif ($version['version'] == $selectedVersion)
- $selected = $version;
- $versionNumbers[(int)$version['version']] = array(
- 'version' => $version['version']
- );
- }
- if ($selected === false) continue; // No versions for this system!?
- ksort($versionNumbers);
- // Mark latest version as selected
- $versionNumbers[(int)$selected['version']]['selected'] = true;
- // Add status information to system and its files
- foreach ($selected['files'] as &$file) {
- $file['uid'] = 'dlid' . $count++;
- $local = CONFIG_HTTP_DIR . '/' . $system['id'] . '/' . $file['name'];
- if (!file_exists($local) || filesize($local) !== $file['size'] || filemtime($local) < $file['mtime']) {
- $file['fileChanged'] = true;
- $system['systemChanged'] = true;
- }
- $taskId = Property::getDownloadTask($file['md5']);
- if ($taskId !== false) {
- $task = Taskmanager::status($taskId);
- if (isset($task['data']['progress'])) {
- $file['download'] = Render::parse('minilinux/download', array(
- 'task' => $taskId,
- 'name' => $file['name']
- ));
- }
- }
- }
- unset($system['versions']);
- $system['files'] = $selected['files'];
- $system['version'] = $selected['version'];
- }
- $data['versions'] = array_values($versionNumbers);
- echo Render::parse('minilinux/filelist', $data);
- return;
- case 'download':
- $id = Request::post('id');
- $name = Request::post('name');
- if (!$id || !$name || strpos("$id$name", '/') !== false) {
- echo "Invalid download request";
- return;
- }
- $file = false;
- $gpg = 'missing';
- foreach ($data['systems'] as &$system) {
- if ($system['id'] !== $id) continue;
- foreach ($system['versions'] as &$version) {
- if ($version['version'] != $selectedVersion) continue;
- foreach ($version['files'] as &$f) {
- if ($f['name'] !== $name) continue;
- $file = $f;
- if (!empty($f['gpg'])) $gpg = $f['gpg'];
- break;
- }
- }
- }
- if ($file === false) {
- echo "Nonexistent system/file: $id / $name";
- return;
- }
- $task = Taskmanager::submit('DownloadFile', array(
- 'url' => CONFIG_REMOTE_ML . '/' . $id . '/' . $selectedVersion . '/' . $name,
- 'destination' => CONFIG_HTTP_DIR . '/' . $id . '/' . $name,
- 'gpg' => $gpg
- ));
- if (!isset($task['id'])) {
- echo 'Error launching download task: ' . $task['statusCode'];
- return;
- }
- Property::setDownloadTask($file['md5'], $task['id']);
- echo Render::parse('minilinux/download', array(
- 'name' => $name,
- 'task' => $task['id']
- ));
- return;
- }
- }
-
-}
diff --git a/modules/minilinux/config.json b/modules/minilinux/config.json
new file mode 100644
index 00000000..93209f62
--- /dev/null
+++ b/modules/minilinux/config.json
@@ -0,0 +1,4 @@
+{
+ "category":"settings",
+ "enabled":"true"
+}
diff --git a/modules/minilinux/module.inc.php b/modules/minilinux/module.inc.php
new file mode 100644
index 00000000..91be456e
--- /dev/null
+++ b/modules/minilinux/module.inc.php
@@ -0,0 +1,128 @@
+ '?do=MiniLinux&async=true&action=list'
+ ));
+ Render::addFooter('');
+ }
+
+ protected function doAjax()
+ {
+ $data = Property::getVersionCheckInformation();
+ if (!is_array($data) || !isset($data['systems'])) {
+ echo Render::parse('messagebox-error', array(
+ 'message' => 'Failed to retrieve the list: ' . print_r($data, true)
+ ),'main');
+ return;
+ }
+ $action = Request::any('action');
+ $selectedVersion = (int)Request::any('version', 0);
+ switch ($action) {
+ case 'list':
+ $count = 0;
+ foreach ($data['systems'] as &$system) {
+ // Get latest version, build simple array of all version numbers
+ $versionNumbers = array();
+ $selected = false;
+ foreach ($system['versions'] as $version) {
+ if (!is_numeric($version['version']) || $version['version'] < 1)
+ continue;
+ if ($selectedVersion === 0 && ($selected === false || $selected['version'] < $version['version']))
+ $selected = $version;
+ elseif ($version['version'] == $selectedVersion)
+ $selected = $version;
+ $versionNumbers[(int)$version['version']] = array(
+ 'version' => $version['version']
+ );
+ }
+ if ($selected === false) continue; // No versions for this system!?
+ ksort($versionNumbers);
+ // Mark latest version as selected
+ $versionNumbers[(int)$selected['version']]['selected'] = true;
+ // Add status information to system and its files
+ foreach ($selected['files'] as &$file) {
+ $file['uid'] = 'dlid' . $count++;
+ $local = CONFIG_HTTP_DIR . '/' . $system['id'] . '/' . $file['name'];
+ if (!file_exists($local) || filesize($local) !== $file['size'] || filemtime($local) < $file['mtime']) {
+ $file['fileChanged'] = true;
+ $system['systemChanged'] = true;
+ }
+ $taskId = Property::getDownloadTask($file['md5']);
+ if ($taskId !== false) {
+ $task = Taskmanager::status($taskId);
+ if (isset($task['data']['progress'])) {
+ $file['download'] = Render::parse('download', array(
+ 'task' => $taskId,
+ 'name' => $file['name']
+ ));
+ }
+ }
+ }
+ unset($system['versions']);
+ $system['files'] = $selected['files'];
+ $system['version'] = $selected['version'];
+ }
+ $data['versions'] = array_values($versionNumbers);
+ echo Render::parse('filelist', $data);
+ return;
+ case 'download':
+ $id = Request::post('id');
+ $name = Request::post('name');
+ if (!$id || !$name || strpos("$id$name", '/') !== false) {
+ echo "Invalid download request";
+ return;
+ }
+ $file = false;
+ $gpg = 'missing';
+ foreach ($data['systems'] as &$system) {
+ if ($system['id'] !== $id) continue;
+ foreach ($system['versions'] as &$version) {
+ if ($version['version'] != $selectedVersion) continue;
+ foreach ($version['files'] as &$f) {
+ if ($f['name'] !== $name) continue;
+ $file = $f;
+ if (!empty($f['gpg'])) $gpg = $f['gpg'];
+ break;
+ }
+ }
+ }
+ if ($file === false) {
+ echo "Nonexistent system/file: $id / $name";
+ return;
+ }
+ $task = Taskmanager::submit('DownloadFile', array(
+ 'url' => CONFIG_REMOTE_ML . '/' . $id . '/' . $selectedVersion . '/' . $name,
+ 'destination' => CONFIG_HTTP_DIR . '/' . $id . '/' . $name,
+ 'gpg' => $gpg
+ ));
+ if (!isset($task['id'])) {
+ echo 'Error launching download task: ' . $task['statusCode'];
+ return;
+ }
+ Property::setDownloadTask($file['md5'], $task['id']);
+ echo Render::parse('download', array(
+ 'name' => $name,
+ 'task' => $task['id']
+ ));
+ return;
+ }
+ }
+
+}
diff --git a/modules/minilinux/templates/download.html b/modules/minilinux/templates/download.html
new file mode 100644
index 00000000..2e32df5a
--- /dev/null
+++ b/modules/minilinux/templates/download.html
@@ -0,0 +1 @@
+{{name}}
\ No newline at end of file
diff --git a/modules/minilinux/templates/filelist.html b/modules/minilinux/templates/filelist.html
new file mode 100644
index 00000000..ca94f4d0
--- /dev/null
+++ b/modules/minilinux/templates/filelist.html
@@ -0,0 +1,77 @@
+ {{#systems}}
+
+
+
{{title}}
+
+
+
+ {{lang_desiredVersion}}
+
+ {{#versions}}
+ {{#selected}}
+ {{version}}
+ {{/selected}}
+ {{^selected}}
+ {{version}}
+ {{/selected}}
+ {{/versions}}
+
+
+ {{#systemChanged}}
+
+ {{lang_canUpdate1}} {{title}} {{lang_canUpdate2}}
+
+
{{lang_update}}
+ {{/systemChanged}}
+ {{^systemChanged}}
+
{{lang_systemUpdated}}
+ {{/systemChanged}}
+
+
{{lang_filesInVersion}} {{version}}
+
+ {{#files}}
+
+
+
{{name}}
+
+ {{^fileChanged}} {{lang_actual}} {{/fileChanged}}
+ {{#fileChanged}} {{lang_outdated}} {{/fileChanged}}
+
+
+ {{#fileChanged}}{{lang_update}} {{/fileChanged}}
+ {{^fileChanged}}{{lang_redownload}} {{/fileChanged}}
+
+
+ {{{download}}}
+
+ {{/files}}
+
+
+
+ {{/systems}}
+ {{^systems}}
+ {{lang_configurationPackageNotFound}}
+ {{/systems}}
+
+
diff --git a/modules/minilinux/templates/page-minilinux.html b/modules/minilinux/templates/page-minilinux.html
new file mode 100644
index 00000000..007e1e1b
--- /dev/null
+++ b/modules/minilinux/templates/page-minilinux.html
@@ -0,0 +1,28 @@
+
+
{{lang_listObtained}}
+
+
+
+
\ No newline at end of file
diff --git a/modules/news.inc.php b/modules/news.inc.php
deleted file mode 100644
index d0aa32ea..00000000
--- a/modules/news.inc.php
+++ /dev/null
@@ -1,167 +0,0 @@
-newsId = false;
- $this->newsTitle = false;
- $this->newsContent = false;
- $this->newsDate = false;
- } elseif ($action === 'show') {
- // show news
- if (!$this->loadNews(Request::any('newsid'))) {
- Message::addError('news-empty');
- }
- } elseif ($action === 'save') {
- // save to DB
- if (!$this->saveNews()) {
- // re-set the fields we got
- Request::post('news-title') ? $this->newsTitle = Request::post('news-title') : $this->newsTitle = false;
- Request::post('news-content') ? $this->newsContent = Request::post('news-content') : $this->newsContent = false;
- } else {
- Message::addSuccess('news-save-success');
- Util::redirect('?do=News');
- }
- } elseif ($action === 'delete') {
- // delete it
- $this->delNews(Request::post('newsid'));
- } else {
- // unknown action, redirect user
- Message::addError('invalid-action', $action);
- Util::redirect('?do=News');
- }
- }
-
- /**
- * Implementation of the abstract doRender function
- *
- * Fetch the list of news from the database and paginate it.
- *
- */
- protected function doRender()
- {
- // fetch the list of the older news
- $lines = array();
- $paginate = new Paginate("SELECT newsid, dateline, title, content FROM news ORDER BY dateline DESC", 10);
- $res = $paginate->exec();
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- $row['date'] = date('d.m.Y H:i', $row['dateline']);
-
- if ($row['newsid'] == $this->newsId) $row['active'] = "active";
- $lines[] = $row;
- }
- $paginate->render('page-news', array(
- 'token' => Session::get('token'),
- 'latestDate' => ($this->newsDate ? date('d.m.Y H:i', $this->newsDate) : '--'),
- 'latestContent' => $this->newsContent,
- 'latestTitle' => $this->newsTitle,
- 'list' => $lines ));
-
- }
- /**
- * Loads the news with the given ID into the form.
- *
- * @param int $newsId ID of the news to be shown.
- * @return boolean true if loading that news worked
- *
- */
- private function loadNews($newsId)
- {
- // check to see if we need to request a specific newsid
- if ($newsId !== false) {
- $row = Database::queryFirst("SELECT newsid, title, content, dateline FROM news WHERE newsid = :newsid LIMIT 1", array(
- 'newsid' => $newsId
- ));
- } else {
- $row = Database::queryFirst("SELECT newsid, title, content, dateline FROM news ORDER BY dateline DESC LIMIT 1");
- }
-
- // fetch the news to be shown
- if ($row !== false) {
- $this->newsId = $row['newsid'];
- $this->newsTitle = $row['title'];
- $this->newsContent = $row['content'];
- $this->newsDate = $row['dateline'];
- }
- return $row !== false;
- }
-
- /**
- * Save the given $newsTitle and $newsContent as POST'ed into the database.
- *
- */
- private function saveNews()
- {
- // check if news content were set by the user
- $newsTitle = Request::post('news-title');
- $newsContent = Request::post('news-content');
- if ($newsContent !== '' && $newsTitle !== '') {
- // we got title and content, save it to DB
- Database::exec("INSERT INTO news (dateline, title, content) VALUES (:dateline, :title, :content)", array(
- 'dateline' => time(),
- 'title' => $newsTitle,
- 'content' => $newsContent
- ));
- return true;
- } else {
- Message::addError('empty-field');
- return false;
- }
- }
-
- /**
- * Delete the news entry with ID $newsId
- *
- * @param int $newsId ID of the entry to be deleted.
- */
- private function delNews($newsId)
- {
- // sanity check: is newsId even numeric?
- if (!is_numeric($newsId)) {
- Message::addError('value-invalid', 'newsid', $newsId);
- } else {
- // check passed - do delete
- Database::exec("DELETE FROM news WHERE newsid = :newsid LIMIT 1", array(
- 'newsid' => $newsId
- ));
- Message::addSuccess('news-del-success');
- }
- Util::redirect('?do=News');
- }
-
-}
diff --git a/modules/news/config.json b/modules/news/config.json
new file mode 100644
index 00000000..4e7fa5fb
--- /dev/null
+++ b/modules/news/config.json
@@ -0,0 +1,4 @@
+{
+ "category":"content",
+ "enabled":"true"
+}
diff --git a/modules/news/module.inc.php b/modules/news/module.inc.php
new file mode 100644
index 00000000..9bbadc4f
--- /dev/null
+++ b/modules/news/module.inc.php
@@ -0,0 +1,167 @@
+newsId = false;
+ $this->newsTitle = false;
+ $this->newsContent = false;
+ $this->newsDate = false;
+ } elseif ($action === 'show') {
+ // show news
+ if (!$this->loadNews(Request::any('newsid'))) {
+ Message::addError('news-empty');
+ }
+ } elseif ($action === 'save') {
+ // save to DB
+ if (!$this->saveNews()) {
+ // re-set the fields we got
+ Request::post('news-title') ? $this->newsTitle = Request::post('news-title') : $this->newsTitle = false;
+ Request::post('news-content') ? $this->newsContent = Request::post('news-content') : $this->newsContent = false;
+ } else {
+ Message::addSuccess('news-save-success');
+ Util::redirect('?do=News');
+ }
+ } elseif ($action === 'delete') {
+ // delete it
+ $this->delNews(Request::post('newsid'));
+ } else {
+ // unknown action, redirect user
+ Message::addError('invalid-action', $action);
+ Util::redirect('?do=News');
+ }
+ }
+
+ /**
+ * Implementation of the abstract doRender function
+ *
+ * Fetch the list of news from the database and paginate it.
+ *
+ */
+ protected function doRender()
+ {
+ // fetch the list of the older news
+ $lines = array();
+ $paginate = new Paginate("SELECT newsid, dateline, title, content FROM news ORDER BY dateline DESC", 10);
+ $res = $paginate->exec();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $row['date'] = date('d.m.Y H:i', $row['dateline']);
+
+ if ($row['newsid'] == $this->newsId) $row['active'] = "active";
+ $lines[] = $row;
+ }
+ $paginate->render('page-news', array(
+ 'token' => Session::get('token'),
+ 'latestDate' => ($this->newsDate ? date('d.m.Y H:i', $this->newsDate) : '--'),
+ 'latestContent' => $this->newsContent,
+ 'latestTitle' => $this->newsTitle,
+ 'list' => $lines ));
+
+ }
+ /**
+ * Loads the news with the given ID into the form.
+ *
+ * @param int $newsId ID of the news to be shown.
+ * @return boolean true if loading that news worked
+ *
+ */
+ private function loadNews($newsId)
+ {
+ // check to see if we need to request a specific newsid
+ if ($newsId !== false) {
+ $row = Database::queryFirst("SELECT newsid, title, content, dateline FROM news WHERE newsid = :newsid LIMIT 1", array(
+ 'newsid' => $newsId
+ ));
+ } else {
+ $row = Database::queryFirst("SELECT newsid, title, content, dateline FROM news ORDER BY dateline DESC LIMIT 1");
+ }
+
+ // fetch the news to be shown
+ if ($row !== false) {
+ $this->newsId = $row['newsid'];
+ $this->newsTitle = $row['title'];
+ $this->newsContent = $row['content'];
+ $this->newsDate = $row['dateline'];
+ }
+ return $row !== false;
+ }
+
+ /**
+ * Save the given $newsTitle and $newsContent as POST'ed into the database.
+ *
+ */
+ private function saveNews()
+ {
+ // check if news content were set by the user
+ $newsTitle = Request::post('news-title');
+ $newsContent = Request::post('news-content');
+ if ($newsContent !== '' && $newsTitle !== '') {
+ // we got title and content, save it to DB
+ Database::exec("INSERT INTO news (dateline, title, content) VALUES (:dateline, :title, :content)", array(
+ 'dateline' => time(),
+ 'title' => $newsTitle,
+ 'content' => $newsContent
+ ));
+ return true;
+ } else {
+ Message::addError('empty-field');
+ return false;
+ }
+ }
+
+ /**
+ * Delete the news entry with ID $newsId
+ *
+ * @param int $newsId ID of the entry to be deleted.
+ */
+ private function delNews($newsId)
+ {
+ // sanity check: is newsId even numeric?
+ if (!is_numeric($newsId)) {
+ Message::addError('value-invalid', 'newsid', $newsId);
+ } else {
+ // check passed - do delete
+ Database::exec("DELETE FROM news WHERE newsid = :newsid LIMIT 1", array(
+ 'newsid' => $newsId
+ ));
+ Message::addSuccess('news-del-success');
+ }
+ Util::redirect('?do=News');
+ }
+
+}
\ No newline at end of file
diff --git a/modules/news/templates/page-news.html b/modules/news/templates/page-news.html
new file mode 100644
index 00000000..8e400498
--- /dev/null
+++ b/modules/news/templates/page-news.html
@@ -0,0 +1,57 @@
+
+
+ {{lang_editNews}}
+
+
+
{{lang_newsIntro}}
+
+
+ {{lang_title}}
+
+
+
+ {{lang_content}}
+ {{latestContent}}
+
+ {{lang_latestUpdate}}: {{latestDate}}
+ {{lang_save}}
+
+
+
+
+
+
+
+ {{lang_newsOld}}
+
+
+
+
+
+
+
+
+ {{lang_date}}
+ {{lang_title}}
+ {{lang_content}}
+
+
+
+
+ {{#list}}
+
+ {{date}}
+ {{title}}
+ {{content}}
+
+ {{lang_show}}
+ {{lang_delete}}
+
+
+ {{/list}}
+
+
+
+
+
+
diff --git a/modules/serversetup.inc.php b/modules/serversetup.inc.php
deleted file mode 100644
index 87ebfdad..00000000
--- a/modules/serversetup.inc.php
+++ /dev/null
@@ -1,141 +0,0 @@
-currentMenu = Property::getBootMenu();
-
- $action = Request::post('action');
-
- if ($action === false) {
- $this->currentAddress = Property::getServerIp();
- $this->getLocalAddresses();
- }
-
- if ($action === 'ip') {
- // New address is to be set
- $this->getLocalAddresses();
- $this->updateLocalAddress();
- }
-
- if ($action === 'ipxe') {
- // iPXE stuff changes
- $this->updatePxeMenu();
- }
- }
-
- protected function doRender()
- {
- Render::setTitle(Dictionary::translate('lang_serverConfiguration'));
-
- $taskid = Request::any('taskid');
- if ($taskid !== false && Taskmanager::isTask($taskid)) {
- Render::addTemplate('serversetup/ipxe_update', array('taskid' => $taskid));
- }
-
- Render::addTemplate('serversetup/ipaddress', array(
- 'ips' => $this->taskStatus['data']['addresses'],
- 'chooseHintClass' => $this->hasIpSet ? '' : 'alert alert-danger'
- ));
- $data = $this->currentMenu;
- if (!isset($data['defaultentry']))
- $data['defaultentry'] = 'net';
- if ($data['defaultentry'] === 'net')
- $data['active-net'] = 'checked';
- if ($data['defaultentry'] === 'hdd')
- $data['active-hdd'] = 'checked';
- if ($data['defaultentry'] === 'custom')
- $data['active-custom'] = 'checked';
- Render::addTemplate('serversetup/ipxe', $data);
- }
-
- // -----------------------------------------------------------------------------------------------
-
- private function getLocalAddresses()
- {
- $this->taskStatus = Taskmanager::submit('LocalAddressesList', array());
-
- if ($this->taskStatus === false) {
- $this->taskStatus['data']['addresses'] = false;
- return false;
- }
-
- if ($this->taskStatus['statusCode'] === TASK_WAITING) { // TODO: Async if just displaying
- $this->taskStatus = Taskmanager::waitComplete($this->taskStatus['id']);
- }
-
- $sortIp = array();
- foreach (array_keys($this->taskStatus['data']['addresses']) as $key) {
- $item = & $this->taskStatus['data']['addresses'][$key];
- if (!isset($item['ip']) || !preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $item['ip']) || substr($item['ip'], 0, 4) === '127.') {
- unset($this->taskStatus['data']['addresses'][$key]);
- continue;
- }
- if ($this->currentAddress === $item['ip']) {
- $item['default'] = true;
- $this->hasIpSet = true;
- }
- $sortIp[] = $item['ip'];
- }
- unset($item);
- array_multisort($sortIp, SORT_STRING, $this->taskStatus['data']['addresses']);
- return true;
- }
-
- private function updateLocalAddress()
- {
- $newAddress = Request::post('ip', 'none');
- $valid = false;
- foreach ($this->taskStatus['data']['addresses'] as $item) {
- if ($item['ip'] !== $newAddress)
- continue;
- $valid = true;
- break;
- }
- if ($valid) {
- Property::setServerIp($newAddress);
- global $tidIpxe;
- if (isset($tidIpxe) && $tidIpxe !== false)
- Util::redirect('?do=ServerSetup&taskid=' . $tidIpxe);
- } else {
- Message::addError('invalid-ip', $newAddress);
- }
- Util::redirect();
- }
-
- private function updatePxeMenu()
- {
- $timeout = Request::post('timeout', 10);
- if ($timeout === '')
- $timeout = 0;
- if (!is_numeric($timeout) || $timeout < 0) {
- Message::addError('value-invalid', 'timeout', $timeout);
- }
- $this->currentMenu['defaultentry'] = Request::post('defaultentry', 'net');
- $this->currentMenu['timeout'] = $timeout;
- $this->currentMenu['custom'] = Request::post('custom', '');
- $this->currentMenu['masterpasswordclear'] = Request::post('masterpassword', '');
- if (empty($this->currentMenu['masterpasswordclear']))
- $this->currentMenu['masterpassword'] = 'invalid';
- else
- $this->currentMenu['masterpassword'] = Crypto::hash6($this->currentMenu['masterpasswordclear']);
- Property::setBootMenu($this->currentMenu);
- $id = Trigger::ipxe();
- Util::redirect('?do=ServerSetup&taskid=' . $id);
- }
-
-}
diff --git a/modules/serversetup/config.json b/modules/serversetup/config.json
new file mode 100644
index 00000000..93209f62
--- /dev/null
+++ b/modules/serversetup/config.json
@@ -0,0 +1,4 @@
+{
+ "category":"settings",
+ "enabled":"true"
+}
diff --git a/modules/serversetup/module.inc.php b/modules/serversetup/module.inc.php
new file mode 100644
index 00000000..e37d7d40
--- /dev/null
+++ b/modules/serversetup/module.inc.php
@@ -0,0 +1,186 @@
+currentMenu = Property::getBootMenu();
+
+ if(Request::get('download') !== false){
+ $this->downloadIpxe(Request::get('download'));
+ }
+
+ if(Request::get('defaultIpxe') !== false){
+ $this->defaultIpxe(Request::get('defaultIpxe'));
+ }
+
+ $action = Request::post('action');
+
+ if ($action === false) {
+ $this->currentAddress = Property::getServerIp();
+ $this->getLocalAddresses();
+ }
+
+ if ($action === 'ip') {
+ // New address is to be set
+ $this->getLocalAddresses();
+ $this->updateLocalAddress();
+ }
+
+ if ($action === 'ipxe') {
+ // iPXE stuff changes
+ $this->updatePxeMenu();
+ }
+
+ if($action === 'save-script') {
+ // Save new iPXE script
+ $this->updateIpxeScript();
+ }
+
+ if($action === 'default-script') {
+ // Restore iPXE script to default
+ $this->defaultIpxe();
+ }
+ }
+
+ protected function doRender()
+ {
+ Render::setTitle(Dictionary::translate('lang_serverConfiguration'));
+
+ $taskid = Request::any('taskid');
+ if ($taskid !== false && Taskmanager::isTask($taskid)) {
+ Render::addTemplate('ipxe_update', array('taskid' => $taskid));
+ }
+
+ Render::addTemplate('ipaddress', array(
+ 'ips' => $this->taskStatus['data']['addresses']
+ ));
+ $data = $this->currentMenu;
+ if (!isset($data['defaultentry']))
+ $data['defaultentry'] = 'net';
+ if ($data['defaultentry'] === 'net')
+ $data['active-net'] = 'checked';
+ if ($data['defaultentry'] === 'hdd')
+ $data['active-hdd'] = 'checked';
+ if ($data['defaultentry'] === 'custom')
+ $data['active-custom'] = 'checked';
+ Render::addTemplate('ipxe', $data);
+ }
+
+ // -----------------------------------------------------------------------------------------------
+
+ private function getLocalAddresses()
+ {
+ $this->taskStatus = Taskmanager::submit('LocalAddressesList', array());
+
+ if ($this->taskStatus === false) {
+ $this->taskStatus['data']['addresses'] = false;
+ return false;
+ }
+
+ if ($this->taskStatus['statusCode'] === TASK_WAITING) { // TODO: Async if just displaying
+ $this->taskStatus = Taskmanager::waitComplete($this->taskStatus['id']);
+ }
+
+ $sortIp = array();
+ foreach (array_keys($this->taskStatus['data']['addresses']) as $key) {
+ $item = & $this->taskStatus['data']['addresses'][$key];
+ if (!isset($item['ip']) || !preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $item['ip']) || substr($item['ip'], 0, 4) === '127.') {
+ unset($this->taskStatus['data']['addresses'][$key]);
+ continue;
+ }
+ if ($this->currentAddress === $item['ip']) {
+ $item['default'] = true;
+ }
+ $sortIp[] = $item['ip'];
+ }
+ unset($item);
+ array_multisort($sortIp, SORT_STRING, $this->taskStatus['data']['addresses']);
+ return true;
+ }
+
+ private function updateLocalAddress()
+ {
+ $newAddress = Request::post('ip', 'none');
+ $valid = false;
+ foreach ($this->taskStatus['data']['addresses'] as $item) {
+ if ($item['ip'] !== $newAddress)
+ continue;
+ $valid = true;
+ break;
+ }
+ if ($valid) {
+ Property::setServerIp($newAddress);
+ global $tidIpxe;
+ if (isset($tidIpxe) && $tidIpxe !== false)
+ Util::redirect('?do=ServerSetup&taskid=' . $tidIpxe);
+ } else {
+ Message::addError('invalid-ip', $newAddress);
+ }
+ Util::redirect();
+ }
+
+ private function updatePxeMenu()
+ {
+ $timeout = Request::post('timeout', 10);
+ if ($timeout === '')
+ $timeout = 0;
+ if (!is_numeric($timeout) || $timeout < 0) {
+ Message::addError('value-invalid', 'timeout', $timeout);
+ }
+ $this->currentMenu['defaultentry'] = Request::post('defaultentry', 'net');
+ $this->currentMenu['timeout'] = $timeout;
+ $this->currentMenu['custom'] = Request::post('custom', '');
+ $this->currentMenu['masterpasswordclear'] = Request::post('masterpassword', '');
+ if (empty($this->currentMenu['masterpasswordclear']))
+ $this->currentMenu['masterpassword'] = 'invalid';
+ else
+ $this->currentMenu['masterpassword'] = Crypto::hash6($this->currentMenu['masterpasswordclear']);
+ Property::setBootMenu($this->currentMenu);
+ $id = Trigger::ipxe();
+ Util::redirect('?do=ServerSetup&taskid=' . $id);
+ }
+
+ private function downloadIpxe($ipxe){
+ $file = '/opt/taskmanager/data/ipxe/src/bin/ipxe.' . $ipxe;
+ if (file_exists($file)) {
+ header('Content-Description: File Transfer');
+ header('Content-Type: application/octet-stream');
+ header('Content-Disposition: attachment; filename='.basename($file));
+ header('Expires: 0');
+ header('Cache-Control: must-revalidate');
+ header('Pragma: public');
+ header('Content-Length: ' . filesize($file));
+ ob_clean();
+ flush();
+ readfile($file);
+ exit();
+ }
+ }
+
+ private function updateIpxeScript(){
+ $newScript = Request::post('custom-script');
+ file_put_contents("/opt/taskmanager/data/pxe.embed",$newScript);
+ Util::redirect('?do=ServerSetup');
+ }
+
+ private function defaultIpxe(){
+ $default = file_get_contents("/opt/taskmanager/data/pxe_default.embed");
+ $default = str_replace("{{ip}}", "http://" . Property::getServerIp(), $default);
+ file_put_contents("/opt/taskmanager/data/pxe.embed",$default);
+ Util::redirect('?do=ServerSetup');
+ }
+}
diff --git a/modules/serversetup/templates/ipaddress.html b/modules/serversetup/templates/ipaddress.html
new file mode 100644
index 00000000..e4967703
--- /dev/null
+++ b/modules/serversetup/templates/ipaddress.html
@@ -0,0 +1,34 @@
+
+
+ {{lang_bootAddress}}
+
+
+
+ {{lang_chooseIP}}
+
+
+
+
+
+ {{#ips}}
+
+ {{ip}}
+ {{#default}}
+
+ {{lang_active}}
+
+ {{/default}}
+ {{^default}}
+
+ {{lang_set}}
+
+ {{/default}}
+
+ {{/ips}}
+
+
+ {{lang_bootHint}}
+
+
+
+
\ No newline at end of file
diff --git a/modules/serversetup/templates/ipxe.html b/modules/serversetup/templates/ipxe.html
new file mode 100644
index 00000000..54d7db16
--- /dev/null
+++ b/modules/serversetup/templates/ipxe.html
@@ -0,0 +1,149 @@
+
+
+ {{lang_mountIpxe}}
+
+
+
{{lang_ipxeInfo}}
+
{{lang_extension}}:
+
+ .kkpxe
+ .usb
+ .iso
+
+
+
+
+
+
+ {{lang_customScript}}
+ {{script}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{lang_bootMenu}}
+
+
+
+ {{lang_bootInfo}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{lang_menuCustomHint1}}
+
{{lang_example}}:
+
LABEL custom
+ MENU LABEL ^My Boot Entry
+ KERNEL http://1.2.3.4/kernel
+ INITRD http://1.2.3.4/initramfs-stage31
+ APPEND custom=option
+ IPAPPEND 3
+ {{lang_menuCustomHint2}} LABEL
custom
+ {{lang_menuCustomHint3}}
+
+
+
+
+
+
+
+
+
diff --git a/modules/serversetup/templates/ipxe_update.html b/modules/serversetup/templates/ipxe_update.html
new file mode 100644
index 00000000..9c598667
--- /dev/null
+++ b/modules/serversetup/templates/ipxe_update.html
@@ -0,0 +1,20 @@
+
+
{{lang_menuGeneration}}
+
+
{{lang_menuGeneration}}
+
+ {{lang_generationFailed}}
+
+
+
+
+
diff --git a/modules/session.inc.php b/modules/session.inc.php
deleted file mode 100644
index 5b9bc7fc..00000000
--- a/modules/session.inc.php
+++ /dev/null
@@ -1,38 +0,0 @@
-
+
+
+
+
+ {{lang_login}}
+ {{lang_register}}
+
+
\ No newline at end of file
diff --git a/modules/statistics.inc.php b/modules/statistics.inc.php
deleted file mode 100644
index dbac4b75..00000000
--- a/modules/statistics.inc.php
+++ /dev/null
@@ -1,785 +0,0 @@
- $uuid,
- 'text' => $text
- ));
- Message::addSuccess('notes-saved');
- Util::redirect('?do=Statistics&uuid=' . $uuid);
- }
- }
-
- protected function doRender()
- {
- Render::setTitle(Dictionary::translate('lang_titleClientStatistics'));
- $uuid = Request::get('uuid', false, 'string');
- if ($uuid !== false) {
- $this->showMachine($uuid);
- return;
- }
- $filter = Request::get('filter', false, 'string');
- if ($filter !== false) {
- $argument = Request::get('argument', false, 'string');
- $this->showMachineList($filter, $argument);
- return;
- }
- Render::addScriptBottom('chart.min');
- Render::openTag('div', array('class' => 'row'));
- $this->showSummary();
- $this->showMemory();
- $this->showId44();
- $this->showKvmState();
- $this->showLatestMachines();
- $this->showSystemModels();
- Render::closeTag('div');
- }
-
- private function capChart(&$json, $cutoff, $minSlice = 0.015)
- {
- $total = 0;
- foreach ($json as $entry) {
- $total += $entry['value'];
- }
- $cap = ceil($total * $cutoff);
- $accounted = 0;
- $id = 0;
- foreach ($json as $entry) {
- if (($accounted >= $cap || $entry['value'] / $total < $minSlice) && $id >= 3) break;
- $id++;
- $accounted += $entry['value'];
- }
- $json = array_slice($json, 0, $id);
- if ($accounted / $total < 0.99) {
- $json[] = array(
- 'color' => '#eee',
- 'label' => 'invalid',
- 'value' => ($total - $accounted)
- );
- }
- }
-
- private function showSummary()
- {
- $cutoff = time() - 86400 * 30;
- $online = time() - 610;
- $known = Database::queryFirst("SELECT Count(*) AS val FROM machine WHERE lastseen > $cutoff");
- $on = Database::queryFirst("SELECT Count(*) AS val FROM machine WHERE lastseen > $online");
- $used = Database::queryFirst("SELECT Count(*) AS val FROM machine WHERE lastseen > $online AND logintime <> 0");
- $hdd = Database::queryFirst("SELECT Count(*) AS val FROM machine WHERE badsectors > 10 AND lastseen > $cutoff");
- $data = array(
- 'known' => $known['val'],
- 'online' => $on['val'],
- 'used' => $used['val'],
- 'usedpercent' => round($used['val'] / $on['val'] * 100),
- 'badhdd' => $hdd['val']
- );
- // Graph
- $cutoff = time() - 2*86400;
- $res = Database::simpleQuery("SELECT dateline, data FROM statistic WHERE typeid = '~stats' AND dateline > $cutoff ORDER BY dateline ASC");
- $labels = array();
- $points1 = array('data' => array(), 'label' => 'Online', 'fillColor' => '#efe', 'strokeColor' => '#aea', 'pointColor' => '#7e7', 'pointStrokeColor' => '#fff', 'pointHighlightFill' => '#fff', 'pointHighlightStroke' => '#7e7');
- $points2 = array('data' => array(), 'label' => 'In use', 'fillColor' => '#fee', 'strokeColor' => '#eaa', 'pointColor' => '#e77', 'pointStrokeColor' => '#fff', 'pointHighlightFill' => '#fff', 'pointHighlightStroke' => '#e77');
- $sum = 0;
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- $x = explode('#', $row['data']);
- if ($sum === 0) {
- $labels[] = date('H:i', $row['dateline']);
- } else {
- $x[1] = max($x[1], array_pop($points1['data']));
- $x[2] = max($x[2], array_pop($points2['data']));
- }
- $points1['data'][] = $x[1];
- $points2['data'][] = $x[2];
- $sum++;
- if ($sum === 12) {
- $sum = 0;
- }
- }
- $data['json'] = json_encode(array('labels' => $labels, 'datasets' => array($points1, $points2)));
- // Draw
- Render::addTemplate('statistics/summary', $data);
- }
-
- private function showSystemModels()
- {
- global $STATS_COLORS;
- $res = Database::simpleQuery("SELECT systemmodel, Round(AVG(realcores)) AS cores, Count(*) AS `count` FROM machine"
- . " GROUP BY systemmodel ORDER BY `count` DESC, systemmodel ASC");
- $lines = array();
- $json = array();
- $id = 0;
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- if (empty($row['systemmodel'])) continue;
- settype($row['count'], 'integer');
- $row['id'] = 'systemid' . $id;
- $row['urlsystemmodel'] = urlencode($row['systemmodel']);
- $lines[] = $row;
- $json[] = array(
- 'color' => $STATS_COLORS[$id % count($STATS_COLORS)],
- 'label' => 'systemid' . $id,
- 'value' => $row['count']
- );
- ++$id;
- }
- $this->capChart($json, 0.92);
- Render::addTemplate('statistics/cpumodels', array('rows' => $lines, 'json' => json_encode($json)));
- }
-
- private function showMemory()
- {
- global $STATS_COLORS, $SIZE_RAM;
- $res = Database::simpleQuery("SELECT mbram, Count(*) AS `count` FROM machine GROUP BY mbram");
- $lines = array();
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- $gb = ceil($row['mbram'] / 1024);
- for ($i = 1; $i < count($SIZE_RAM); ++$i) {
- if ($SIZE_RAM[$i] < $gb) continue;
- if ($SIZE_RAM[$i] - $gb >= $gb - $SIZE_RAM[$i-1]) --$i;
- $gb = $SIZE_RAM[$i];
- break;
- }
- if (isset($lines[$gb])) {
- $lines[$gb] += $row['count'];
- } else {
- $lines[$gb] = $row['count'];
- }
- }
- asort($lines);
- $data = array('rows' => array());
- $json = array();
- $id = 0;
- foreach (array_reverse($lines, true) as $k => $v) {
- $data['rows'][] = array('gb' => $k, 'count' => $v, 'class' => $this->ramColorClass($k * 1024));
- $json[] = array(
- 'color' => $STATS_COLORS[$id % count($STATS_COLORS)],
- 'label' => (string)$k,
- 'value' => $v
- );
- ++$id;
- }
- $this->capChart($json, 0.92);
- $data['json'] = json_encode($json);
- Render::addTemplate('statistics/memory', $data);
- }
-
- private function showKvmState()
- {
- $colors = array('UNKNOWN' => '#666', 'UNSUPPORTED' => '#ea5', 'DISABLED' => '#e55', 'ENABLED' => '#6d6');
- $res = Database::simpleQuery("SELECT kvmstate, Count(*) AS `count` FROM machine GROUP BY kvmstate ORDER BY `count` DESC");
- $lines = array();
- $json = array();
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- $lines[] = $row;
- $json[] = array(
- 'color' => isset($colors[$row['kvmstate']]) ? $colors[$row['kvmstate']] : '#000',
- 'label' => $row['kvmstate'],
- 'value' => $row['count']
- );
- }
- Render::addTemplate('statistics/kvmstate', array('rows' => $lines, 'json' => json_encode($json)));
- }
-
- private function showId44()
- {
- global $STATS_COLORS, $SIZE_ID44;
- $res = Database::simpleQuery("SELECT id44mb, Count(*) AS `count` FROM machine GROUP BY id44mb");
- $lines = array();
- $total = 0;
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- $total += $row['count'];
- $gb = ceil($row['id44mb'] / 1024);
- for ($i = 1; $i < count($SIZE_ID44); ++$i) {
- if ($SIZE_ID44[$i] < $gb) continue;
- if ($SIZE_ID44[$i] - $gb >= $gb - $SIZE_ID44[$i-1]) --$i;
- $gb = $SIZE_ID44[$i];
- break;
- }
- if (isset($lines[$gb])) {
- $lines[$gb] += $row['count'];
- } else {
- $lines[$gb] = $row['count'];
- }
- }
- asort($lines);
- $data = array('rows' => array());
- $json = array();
- $id = 0;
- foreach (array_reverse($lines, true) as $k => $v) {
- $data['rows'][] = array('gb' => $k, 'count' => $v, 'class' => $this->hddColorClass($k));
- if ($k === 0) {
- $color = '#e55';
- } else {
- $color = $STATS_COLORS[$id++ % count($STATS_COLORS)];
- }
- $json[] = array(
- 'color' => $color,
- 'label' => (string)$k,
- 'value' => $v
- );
- }
- $this->capChart($json, 0.95);
- $data['json'] = json_encode($json);
- Render::addTemplate('statistics/id44', $data);
- }
-
- private function showLatestMachines()
- {
- $data = array('cutoff' => ceil(time() / 3600) * 3600 - 86400 * 7);
- $res = Database::simpleQuery("SELECT machineuuid, clientip, hostname, firstseen, mbram, kvmstate, id44mb FROM machine"
- . " WHERE firstseen > :cutoff ORDER BY firstseen DESC LIMIT 32", $data);
- $rows = array();
- $count = 0;
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- if (empty($row['hostname'])) {
- $row['hostname'] = $row['clientip'];
- }
- $row['firstseen'] = date('d.m. H:i', $row['firstseen']);
- $row['gbram'] = round(round($row['mbram'] / 500) / 2, 1); // Trial and error until we got "expected" rounding..
- $row['gbtmp'] = round($row['id44mb'] / 1024);
- $row['ramclass'] = $this->ramColorClass($row['mbram']);
- $row['kvmclass'] = $this->kvmColorClass($row['kvmstate']);
- $row['hddclass'] = $this->hddColorClass($row['gbtmp']);
- $row['kvmicon'] = $row['kvmstate'] === 'ENABLED' ? 'âś“' : 'âś—';
- if (++$count > 5) {
- $row['style'] = 'display:none';
- }
- $rows[] = $row;
- }
- Render::addTemplate('statistics/newclients', array('rows' => $rows, 'openbutton' => $count > 5));
- }
-
- private function showMachineList($filter, $argument)
- {
- global $SIZE_RAM, $SIZE_ID44;
- $join = '';
- $filters = array('cpumodel', 'realcores', 'kvmstate', 'clientip', 'macaddr', 'machineuuid', 'systemmodel');
- if (in_array($filter, $filters)) {
- // Simple filters mapping into db
- $where = " $filter = :argument";
- $args = array('argument' => $argument);
- } elseif ($filter === 'gbram') {
- // Memory by rounded GB
- $lower = floor($this->findBestValue($SIZE_RAM, $argument, false) * 1024 - 100);
- $upper = ceil($this->findBestValue($SIZE_RAM, $argument, true) * 1024 + 100);
- $where = " mbram BETWEEN $lower AND $upper";
- $args = array();
- } elseif ($filter === 'hddgb') {
- // HDD by rounded GB
- $lower = floor($this->findBestValue($SIZE_ID44, $argument, false) * 1024 - 100);
- $upper = ceil($this->findBestValue($SIZE_ID44, $argument, true) * 1024 + 100);
- $where = " id44mb BETWEEN $lower AND $upper";
- $args = array();
- } elseif ($filter === 'subnet') {
- $argument = preg_replace('/[^0-9\.:]/', '', $argument);
- $where = " clientip LIKE '$argument%'";
- $args = array();
- } elseif ($filter === 'badsectors') {
- $where = " badsectors >= :argument ";
- $args = array('argument' => $argument);
- } elseif ($filter === 'state') {
- if ( $argument === 'on') {
- $where = " lastseen + 600 > UNIX_TIMESTAMP() ";
- } elseif ($argument === 'off') {
- $where = " lastseen + 600 < UNIX_TIMESTAMP() ";
- } elseif ($argument === 'idle') {
- $where = " lastseen + 600 > UNIX_TIMESTAMP() AND logintime = 0 ";
- } elseif ($argument === 'occupied') {
- $where = " lastseen + 600 > UNIX_TIMESTAMP() AND logintime <> 0 ";
- } else {
- Message::addError('invalid-filter');
- return;
- }
- } elseif ($filter === 'location') {
- $where = "subnet.locationid = :lid OR machine.locationid = :lid";
- $join = " INNER JOIN subnet ON (INET_ATON(clientip) BETWEEN startaddr AND endaddr) ";
- $args = array('lid' => (int)$argument);
- } else {
- Message::addError('invalid-filter');
- return;
- }
- $res = Database::simpleQuery("SELECT machineuuid, macaddr, clientip, firstseen, lastseen,"
- . " logintime, lastboot, realcores, mbram, kvmstate, cpumodel, id44mb, hostname, notes IS NOT NULL AS hasnotes, badsectors FROM machine"
- . " $join WHERE $where ORDER BY lastseen DESC, clientip ASC", $args);
- $rows = array();
- $NOW = time();
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- if ($NOW - $row['lastseen'] > 610) {
- $row['state_off'] = true;
- } elseif ($row['logintime'] == 0) {
- $row['state_idle'] = true;
- } else {
- $row['state_occupied'] = true;
- }
- //$row['firstseen'] = date('d.m.Y H:i', $row['firstseen']);
- $row['lastseen'] = date('d.m. H:i', $row['lastseen']);
- //$row['lastboot'] = date('d.m. H:i', $row['lastboot']);
- $row['gbram'] = round(round($row['mbram'] / 500) / 2, 1); // Trial and error until we got "expected" rounding..
- $row['gbtmp'] = round($row['id44mb'] / 1024);
- $octets = explode('.', $row['clientip']);
- if (count($octets) === 4) {
- $row['subnet'] = "$octets[0].$octets[1].$octets[2].";
- $row['lastoctet'] = $octets[3];
- }
- $row['ramclass'] = $this->ramColorClass($row['mbram']);
- $row['kvmclass'] = $this->kvmColorClass($row['kvmstate']);
- $row['hddclass'] = $this->hddColorClass($row['gbtmp']);
- if (empty($row['hostname'])) $row['hostname'] = $row['clientip'];
- $rows[] = $row;
- }
- Render::addTemplate('statistics/clientlist', array('rows' => $rows, 'filter' => $filter, 'argument' => $argument));
- }
-
- private function ramColorClass($mb)
- {
- if ($mb < 1500)
- return 'danger';
- if ($mb < 2500)
- return 'warning';
- return '';
- }
-
- private function kvmColorClass($state)
- {
- if ($state === 'DISABLED')
- return 'danger';
- if ($state === 'UNKNOWN' || $state === 'UNSUPPORTED')
- return 'warning';
- return '';
- }
-
- private function hddColorClass($gb)
- {
- if ($gb < 7)
- return 'danger';
- if ($gb < 25)
- return 'warning';
- return '';
- }
-
- private function findBestValue($array, $value, $up)
- {
- $best = 0;
- for ($i = 0; $i < count($array); ++$i) {
- if (abs($array[$i] - $value) < abs($array[$best] - $value)) {
- $best = $i;
- }
- }
- if (!$up && $best === 0) {
- return $array[0];
- }
- if ($up && $best + 1 === count($array)) {
- return $array[$best];
- }
- if ($up) {
- return ($array[$best] + $array[$best + 1]) / 2;
- }
- return ($array[$best] + $array[$best - 1]) / 2;
- }
-
- private function fillSessionInfo(&$row)
- {
- $res = Database::simpleQuery("SELECT dateline, username, data FROM statistic"
- . " WHERE clientip = :ip AND typeid = '.vmchooser-session-name'"
- . " AND dateline BETWEEN :start AND :end", array(
- 'ip' => $row['clientip'],
- 'start' => $row['logintime'] - 60,
- 'end' => $row['logintime'] + 300
- ));
- $session = false;
- while ($r = $res->fetch(PDO::FETCH_ASSOC)) {
- if ($session === false || abs($session['dateline'] - $row['logintime']) > abs($r['dateline'] - $row['logintime'])) {
- $session = $r;
- }
- }
- if ($session !== false) {
- $row['session'] = $session['data'];
- $row['username'] = $session['username'];
- }
- }
-
- private function showMachine($uuid)
- {
- $client = Database::queryFirst("SELECT machineuuid, macaddr, clientip, firstseen, lastseen, logintime, lastboot,"
- . " mbram, kvmstate, cpumodel, id44mb, data, hostname, notes FROM machine WHERE machineuuid = :uuid",
- array('uuid' => $uuid));
- // Mangle fields
- $NOW = time();
- if ($NOW - $client['lastseen'] > 610) {
- $client['state_off'] = true;
- } elseif ($client['logintime'] == 0) {
- $client['state_idle'] = true;
- } else {
- $client['state_occupied'] = true;
- $this->fillSessionInfo($client);
- }
- $client['firstseen_s'] = date('d.m.Y H:i', $client['firstseen']);
- $client['lastseen_s'] = date('d.m.Y H:i', $client['lastseen']);
- $uptime = $NOW - $client['lastboot'];
- $client['lastboot_s'] = date('d.m.Y H:i', $client['lastboot']) . ' (Up ' . floor($uptime / 86400) . 'd ' . gmdate('H:i', $uptime) . ')';
- $client['logintime_s'] = date('d.m.Y H:i', $client['logintime']);
- $client['gbram'] = round(round($client['mbram'] / 500) / 2, 1);
- $client['gbtmp'] = round($client['id44mb'] / 1024);
- $client['ramclass'] = $this->ramColorClass($client['mbram']);
- $client['kvmclass'] = $this->kvmColorClass($client['kvmstate']);
- $client['hddclass'] = $this->hddColorClass($client['gbtmp']);
- // Parse the giant blob of data
- $hdds = array();
- if (preg_match_all('/##### ([^#]+) #+$(.*?)^#####/ims', $client['data'] . '########', $out, PREG_SET_ORDER)) {
- foreach ($out as $section) {
- if ($section[1] === 'CPU') {
- $this->parseCpu($client, $section[2]);
- }
- if ($section[1] === 'dmidecode') {
- $this->parseDmiDecode($client, $section[2]);
- }
- if ($section[1] === 'Partition tables') {
- $this->parseHdd($hdds, $section[2]);
- }
- if (isset($hdds['hdds']) && $section[1] === 'smartctl') {
- // This currently required that the partition table section comes first...
- $this->parseSmartctl($hdds['hdds'], $section[2]);
- }
- }
- }
- unset($client['data']);
- // Throw output at user
- Render::addTemplate('statistics/machine-main', $client);
- // Sessions
- $NOW = time();
- $cutoff = $NOW - 86400 * 7;
- //if ($cutoff < $client['firstseen']) $cutoff = $client['firstseen'];
- $scale = 100 / ($NOW - $cutoff);
- $res = Database::simpleQuery("SELECT dateline, typeid, data FROM statistic"
- . " WHERE dateline > :cutoff AND typeid IN ('~session-length', '~offline-length') AND machineuuid = :uuid ORDER BY dateline ASC", array(
- 'cutoff' => $cutoff - 86400 * 14,
- 'uuid' => $uuid
- ));
- $spans['rows'] = array();
- $spans['graph'] = '';
- $last = false;
- $first = true;
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- if ($first && $row['dateline'] > $cutoff && $client['lastboot'] > $cutoff) {
- // Special case: offline before
- $spans['graph'] .= '
';
- }
- $first = false;
- if ($row['dateline'] + $row['data'] < $cutoff || $row['data'] > 864000) continue;
- if ($last !== false && abs($last['dateline'] - $row['dateline']) < 30
- && abs($last['data'] - $row['data']) < 30) continue;
- if ($last !== false && $last['dateline'] + $last['data'] > $row['dateline']) {
- $point = $last['dateline'] + $last['data'];
- $row['data'] -= ($point - $row['dateline']);
- $row['dateline'] = $point;
- }
- if ($row['dateline'] < $cutoff) {
- $row['data'] -= ($cutoff - $row['dateline']);
- $row['dateline'] = $cutoff;
- }
- $row['from'] = date('d.m. H:i', $row['dateline']);
- $row['duration'] = floor($row['data'] / 86400) . 'd ' . gmdate('H:i', $row['data']);
- if ($row['typeid'] === '~offline-length') {
- $row['glyph'] = 'off';
- $color = '#444';
- } else {
- $row['glyph'] = 'user';
- $color = '#e77';
- }
- $spans['graph'] .= '
';
- $spans['rows'][] = $row;
- $last = $row;
- }
- if ($first && $client['lastboot'] > $cutoff) {
- // Special case: offline before
- $spans['graph'] .= '
';
- }
- if (isset($client['state_occupied'])) {
- $spans['graph'] .= '
';
- } elseif (isset($client['state_off'])) {
- $spans['graph'] .= '
';
- }
- $t = explode('-', date('Y-n-j-G', $cutoff));
- if ($t[3] >= 8 && $t[3] <= 22) {
- $start = mktime(22, 0, 0, $t[1], $t[2], $t[0]);
- } else {
- $start = mktime(22, 0, 0, $t[1], $t[2] - 1, $t[0]);
- }
- for ($i = $start; $i < $NOW; $i += 86400) {
- $spans['graph'] .= '
';
- }
- if (count($spans['rows']) > 10) {
- $spans['hasrows2'] = true;
- $spans['rows2'] = array_slice($spans['rows'], ceil(count($spans['rows']) / 2));
- $spans['rows'] = array_slice($spans['rows'], 0, ceil(count($spans['rows']) / 2));
- }
- Render::addTemplate('statistics/machine-usage', $spans);
- // Any hdds?
- if (!empty($hdds['hdds'])) {
- Render::addScriptBottom('chart.min');
- Render::addTemplate('statistics/machine-hdds', $hdds);
- }
- // Client log
- $lres = Database::simpleQuery("SELECT logid, dateline, logtypeid, clientip, description, extra FROM clientlog"
- . " WHERE clientip = :clientip ORDER BY logid DESC LIMIT 25", array('clientip' => $client['clientip']));
- $today = date('d.m.Y');
- $yesterday = date('d.m.Y', time() - 86400);
- $count = 0;
- $log = array();
- while ($row = $lres->fetch(PDO::FETCH_ASSOC)) {
- if (substr($row['description'], -5) === 'on :0' && strpos($row['description'], 'root logged') === false) continue;
- $day = date('d.m.Y', $row['dateline']);
- if ($day === $today) {
- $day = Dictionary::translate('today');
- } elseif ($day === $yesterday) {
- $day = Dictionary::translate('yesterday');
- }
- $row['date'] = $day . date(' H:i', $row['dateline']);
- $row['icon'] = $this->eventToIconName($row['logtypeid']);
- $log[] = $row;
- if (++$count === 10) break;
- }
- Render::addTemplate('statistics/syslog', array(
- 'clientip' => $client['clientip'],
- 'list' => $log
- ));
- // Notes
- Render::addTemplate('statistics/machine-notes', $client);
- }
-
- private function eventToIconName($event)
- {
- switch ($event) {
- case 'session-open':
- return 'glyphicon-log-in';
- case 'session-close':
- return 'glyphicon-log-out';
- case 'partition-swap':
- return 'glyphicon-info-sign';
- case 'partition-temp':
- case 'smartctl-realloc':
- return 'glyphicon-exclamation-sign';
- default:
- return 'glyphicon-minus';
- }
- }
-
- private function parseCpu(&$row, $data)
- {
- if (0 >= preg_match_all('/^(.+):\s+(\d+)$/im', $data, $out, PREG_SET_ORDER)) return;
- foreach ($out as $entry) {
- $row[str_replace(' ', '', $entry[1])] = $entry[2];
- }
- }
-
- private function parseDmiDecode(&$row, $data)
- {
- $lines = preg_split("/[\r\n]+/", $data);
- $section = false;
- $ramOk = false;
- $ramForm = $ramType = $ramSpeed = $ramClockSpeed = false;
- foreach ($lines as $line) {
- if (empty($line)) continue;
- if ($line{0} !== "\t" && $line{0} !== ' ') {
- $section = $line;
- $ramOk = false;
- if (($ramForm || $ramType) && ($ramSpeed || $ramClockSpeed)) {
- if (isset($row['ramtype']) && !$ramClockSpeed) continue;
- $row['ramtype'] = $ramType . ' ' . $ramForm;
- if ($ramClockSpeed) $row['ramtype'] .= ', ' . $ramClockSpeed;
- elseif ($ramSpeed) $row['ramtype'] .= ', ' . $ramSpeed;
- $ramForm = false;
- $ramType = false;
- $ramClockSpeed = false;
- }
- continue;
- }
- if ($section === 'System Information' || $section === 'Base Board Information') {
- if (empty($row['pcmodel']) && preg_match('/^\s*Product Name: +(\S.+?) *$/i', $line, $out)) {
- $row['pcmodel'] = $out[1];
- }
- if (empty($row['manufacturer']) && preg_match('/^\s*Manufacturer: +(\S.+?) *$/i', $line, $out)) {
- $row['manufacturer'] = $out[1];
- }
- }
- else if ($section === 'Physical Memory Array') {
- if (!$ramOk && preg_match('/Use: System Memory/i', $line)) {
- $ramOk = true;
- }
- if ($ramOk && preg_match('/^\s*Number Of Devices: +(\S.+?) *$/i', $line, $out)) {
- $row['ramslotcount'] = $out[1];
- }
- if ($ramOk && preg_match('/^\s*Maximum Capacity: +(\S.+?)\s*$/i', $line, $out)) {
- $row['maxram'] = preg_replace('/([MGT])B/', '$1iB', $out[1]);
- }
- }
- else if ($section === 'Memory Device') {
- if (preg_match('/^\s*Size:\s*(.*?)\s*$/i', $line, $out)) {
- $row['extram'] = true;
- if (preg_match('/(\d+)\s*(\w)i?B/i', $out[1], $out)) {
- $out[2] = strtoupper($out[2]);
- if ($out[2] === 'K' || ($out[2] === 'M' && $out[1] < 500)) {
- $ramForm = $ramType = $ramSpeed = $ramClockSpeed = false;
- continue;
- }
- if ($out[2] === 'M' && $out[1] >= 1024) {
- $out[2] = 'G';
- $out[1] = floor(($out[1] + 100) / 1024);
- }
- $row['ramslot'][]['size'] = $out[1] . ' ' . strtoupper($out[2]) . 'iB';
- } else if (!isset($row['ramslot']) || (count($row['ramslot']) < 8 && (!isset($row['ramslotcount']) || $row['ramslotcount'] <= 8))) {
- $row['ramslot'][]['size'] = '_____';
- }
- }
- if (preg_match('/^\s*Form Factor:\s*(.*?)\s*$/i', $line, $out) && $out[1] !== 'Unknown') {
- $ramForm = $out[1];
- }
- if (preg_match('/^\s*Type:\s*(.*?)\s*$/i', $line, $out) && $out[1] !== 'Unknown') {
- $ramType = $out[1];
- }
- if (preg_match('/^\s*Speed:\s*(\d.*?)\s*$/i', $line, $out)) {
- $ramSpeed = $out[1];
- }
- if (preg_match('/^\s*Configured Clock Speed:\s*(\d.*?)\s*$/i', $line, $out)) {
- $ramClockSpeed = $out[1];
- }
- }
- }
- if (empty($row['ramslotcount'])) $row['ramslotcount'] = count($row['ramslot']);
- }
-
- private function parseHdd(&$row, $data)
- {
- $hdds = array();
- // Could have more than one disk - linear scan
- $lines = preg_split("/[\r\n]+/", $data);
- $dev = false;
- $i = 0;
- foreach ($lines as $line) {
- if (preg_match('/^Disk (\S+):.* (\d+) bytes/i', $line, $out)) {
- // disk total size and name
- unset($hdd);
- $unit = 0;
- $hdd = array(
- 'devid' => 'devid-' . ++$i,
- 'dev' => $out[1],
- 'size' => round($out[2] / (1024 * 1024 * 1024)),
- 'used' => 0,
- 'partitions' => array(),
- 'json' => array(),
- );
- $hdds[] = &$hdd;
- } elseif (preg_match('/^Units =.*= (\d+) bytes/i', $line, $out)) {
- // Unit for start and end
- $unit = $out[1] / (1024 * 1024); // Convert so that multiplying by unit yields MiB
- } else if (isset($hdd) && $unit !== 0 && preg_match(',^/dev/(\S+)\s+.*\s(\d+)[\+\-]?\s+(\d+)[\+\-]?\s+\d+[\+\-]?\s+([0-9a-f]+)\s+(.*)$,i', $line, $out)) {
- // Some partition
- $type = strtolower($out[4]);
- if ($type === '5' || $type === 'f' || $type === '85') continue;
- $partsize = round(($out[3] - $out[2]) * $unit);
- $hdd['partitions'][] = array(
- 'id' => $out[1],
- 'name' => $out[1],
- 'size' => round($partsize / 1024, $partsize < 1024 ? 1 : 0),
- 'type' => ($type === '44' ? 'OpenSLX' : $out[5]),
- );
- $hdd['json'][] = array(
- 'label' => $out[1],
- 'value' => $partsize,
- 'color' => ($type === '44' ? '#4d4' : ($type === '82' ? '#48f' : '#e55')),
- );
- $hdd['used'] += $partsize;
- }
- }
- unset($hdd);
- $i = 0;
- foreach ($hdds as &$hdd) {
- $hdd['used'] = round($hdd['used'] / 1024);
- $free = $hdd['size'] - $hdd['used'];
- if ($free > 5) {
- $hdd['partitions'][] = array(
- 'id' => 'free-id-' . $i,
- 'name' => Dictionary::translate('unused'),
- 'size' => $free,
- 'type' => '-',
- );
- $hdd['json'][] = array(
- 'label' => 'free-id-' . $i,
- 'value' => $free * 1024,
- 'color' => '#aaa',
- );
- ++$i;
- }
- $hdd['json'] = json_encode($hdd['json']);
- }
- unset($hdd);
- $row['hdds'] = &$hdds;
- }
-
- private function parseSmartctl(&$hdds, $data)
- {
- $lines = preg_split("/[\r\n]+/", $data);
- $i = 0;
- foreach ($lines as $line) {
- if (preg_match('/^NEXTHDD=(.+)$/', $line, $out)) {
- unset($dev);
- foreach ($hdds as &$hdd) {
- if ($hdd['dev'] === $out[1]) $dev =& $hdd;
- }
- continue;
- }
- if (!isset($dev)) continue;
- if (preg_match('/^([A-Z][^:]+):\s*(.*)$/', $line, $out)) {
- $dev['s_' . preg_replace('/\s|-|_/', '', $out[1])] = $out[2];
- } elseif (preg_match('/^\s*\d+\s+(\S+)\s+\S+\s+\d+\s+\d+\s+\d+\s+\S+\s+(\d+)(\s|$)/', $line, $out)) {
- $dev['s_' . preg_replace('/\s|-|_/', '', $out[1])] = $out[2];
- }
- }
- // Format strings
- foreach ($hdds as &$hdd) {
- if (isset($hdd['s_PowerOnHours'])) {
- $hdd['PowerOnTime'] = '';
- $val = (int)$hdd['s_PowerOnHours'];
- if ($val > 8760) {
- $hdd['PowerOnTime'] .= floor($val / 8760) . 'Y, ';
- $val %= 8760;
- }
- if ($val > 720) {
- $hdd['PowerOnTime'] .= floor($val / 720) . 'M, ';
- $val %= 720;
- }
- if ($val > 24) {
- $hdd['PowerOnTime'] .= floor($val / 24) . 'd, ';
- $val %= 24;
- }
- $hdd['PowerOnTime'] .= $val . 'h';
- }
- }
- }
-
-}
diff --git a/modules/statistics/config.json b/modules/statistics/config.json
new file mode 100644
index 00000000..17acab76
--- /dev/null
+++ b/modules/statistics/config.json
@@ -0,0 +1,5 @@
+{
+ "category":"status",
+ "enabled":"true",
+ "permission":"0"
+}
diff --git a/modules/statistics/module.inc.php b/modules/statistics/module.inc.php
new file mode 100644
index 00000000..faf88521
--- /dev/null
+++ b/modules/statistics/module.inc.php
@@ -0,0 +1,792 @@
+ $uuid,
+ 'text' => $text
+ ));
+ Message::addSuccess('notes-saved');
+ Util::redirect('?do=Statistics&uuid=' . $uuid);
+ }
+ }
+
+ protected function doRender()
+ {
+ Render::setTitle(Dictionary::translate('lang_titleClientStatistics'));
+ $uuid = Request::get('uuid', false, 'string');
+ if ($uuid !== false) {
+ $this->showMachine($uuid);
+ return;
+ }
+ $filter = Request::get('filter', false, 'string');
+ if ($filter !== false) {
+ $argument = Request::get('argument', false, 'string');
+ $this->showMachineList($filter, $argument);
+ return;
+ }
+ Render::addScriptBottom('chart.min');
+ Render::openTag('div', array('class' => 'row'));
+ $this->showSummary();
+ $this->showMemory();
+ $this->showId44();
+ $this->showKvmState();
+ $this->showLatestMachines();
+ $this->showSystemModels();
+ Render::closeTag('div');
+ }
+
+ private function capChart(&$json, $cutoff, $minSlice = 0.015)
+ {
+ $total = 0;
+ foreach ($json as $entry) {
+ $total += $entry['value'];
+ }
+ if ($total === 0)
+ return;
+ $cap = ceil($total * $cutoff);
+ $accounted = 0;
+ $id = 0;
+ foreach ($json as $entry) {
+ if (($accounted >= $cap || $entry['value'] / $total < $minSlice) && $id >= 3) break;
+ $id++;
+ $accounted += $entry['value'];
+ }
+ $json = array_slice($json, 0, $id);
+ if ($accounted / $total < 0.99) {
+ $json[] = array(
+ 'color' => '#eee',
+ 'label' => 'invalid',
+ 'value' => ($total - $accounted)
+ );
+ }
+ }
+
+ private function showSummary()
+ {
+ $cutoff = time() - 86400 * 30;
+ $online = time() - 610;
+ $known = Database::queryFirst("SELECT Count(*) AS val FROM machine WHERE lastseen > $cutoff");
+ $on = Database::queryFirst("SELECT Count(*) AS val FROM machine WHERE lastseen > $online");
+ $used = Database::queryFirst("SELECT Count(*) AS val FROM machine WHERE lastseen > $online AND logintime <> 0");
+ $hdd = Database::queryFirst("SELECT Count(*) AS val FROM machine WHERE badsectors > 10 AND lastseen > $cutoff");
+ if ($on['val'] != 0) {
+ $usedpercent = round($used['val'] / $on['val'] * 100);
+ } else {
+ $usedpercent = 0;
+ }
+ $data = array(
+ 'known' => $known['val'],
+ 'online' => $on['val'],
+ 'used' => $used['val'],
+ 'usedpercent' => $usedpercent,
+ 'badhdd' => $hdd['val']
+ );
+ // Graph
+ $cutoff = time() - 2*86400;
+ $res = Database::simpleQuery("SELECT dateline, data FROM statistic WHERE typeid = '~stats' AND dateline > $cutoff ORDER BY dateline ASC");
+ $labels = array();
+ $points1 = array('data' => array(), 'label' => 'Online', 'fillColor' => '#efe', 'strokeColor' => '#aea', 'pointColor' => '#7e7', 'pointStrokeColor' => '#fff', 'pointHighlightFill' => '#fff', 'pointHighlightStroke' => '#7e7');
+ $points2 = array('data' => array(), 'label' => 'In use', 'fillColor' => '#fee', 'strokeColor' => '#eaa', 'pointColor' => '#e77', 'pointStrokeColor' => '#fff', 'pointHighlightFill' => '#fff', 'pointHighlightStroke' => '#e77');
+ $sum = 0;
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $x = explode('#', $row['data']);
+ if ($sum === 0) {
+ $labels[] = date('H:i', $row['dateline']);
+ } else {
+ $x[1] = max($x[1], array_pop($points1['data']));
+ $x[2] = max($x[2], array_pop($points2['data']));
+ }
+ $points1['data'][] = $x[1];
+ $points2['data'][] = $x[2];
+ $sum++;
+ if ($sum === 12) {
+ $sum = 0;
+ }
+ }
+ $data['json'] = json_encode(array('labels' => $labels, 'datasets' => array($points1, $points2)));
+ // Draw
+ Render::addTemplate('summary', $data);
+ }
+
+ private function showSystemModels()
+ {
+ global $STATS_COLORS;
+ $res = Database::simpleQuery("SELECT systemmodel, Round(AVG(realcores)) AS cores, Count(*) AS `count` FROM machine"
+ . " GROUP BY systemmodel ORDER BY `count` DESC, systemmodel ASC");
+ $lines = array();
+ $json = array();
+ $id = 0;
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ if (empty($row['systemmodel'])) continue;
+ settype($row['count'], 'integer');
+ $row['id'] = 'systemid' . $id;
+ $row['urlsystemmodel'] = urlencode($row['systemmodel']);
+ $lines[] = $row;
+ $json[] = array(
+ 'color' => $STATS_COLORS[$id % count($STATS_COLORS)],
+ 'label' => 'systemid' . $id,
+ 'value' => $row['count']
+ );
+ ++$id;
+ }
+ $this->capChart($json, 0.92);
+ Render::addTemplate('cpumodels', array('rows' => $lines, 'json' => json_encode($json)));
+ }
+
+ private function showMemory()
+ {
+ global $STATS_COLORS, $SIZE_RAM;
+ $res = Database::simpleQuery("SELECT mbram, Count(*) AS `count` FROM machine GROUP BY mbram");
+ $lines = array();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $gb = ceil($row['mbram'] / 1024);
+ for ($i = 1; $i < count($SIZE_RAM); ++$i) {
+ if ($SIZE_RAM[$i] < $gb) continue;
+ if ($SIZE_RAM[$i] - $gb >= $gb - $SIZE_RAM[$i-1]) --$i;
+ $gb = $SIZE_RAM[$i];
+ break;
+ }
+ if (isset($lines[$gb])) {
+ $lines[$gb] += $row['count'];
+ } else {
+ $lines[$gb] = $row['count'];
+ }
+ }
+ asort($lines);
+ $data = array('rows' => array());
+ $json = array();
+ $id = 0;
+ foreach (array_reverse($lines, true) as $k => $v) {
+ $data['rows'][] = array('gb' => $k, 'count' => $v, 'class' => $this->ramColorClass($k * 1024));
+ $json[] = array(
+ 'color' => $STATS_COLORS[$id % count($STATS_COLORS)],
+ 'label' => (string)$k,
+ 'value' => $v
+ );
+ ++$id;
+ }
+ $this->capChart($json, 0.92);
+ $data['json'] = json_encode($json);
+ Render::addTemplate('memory', $data);
+ }
+
+ private function showKvmState()
+ {
+ $colors = array('UNKNOWN' => '#666', 'UNSUPPORTED' => '#ea5', 'DISABLED' => '#e55', 'ENABLED' => '#6d6');
+ $res = Database::simpleQuery("SELECT kvmstate, Count(*) AS `count` FROM machine GROUP BY kvmstate ORDER BY `count` DESC");
+ $lines = array();
+ $json = array();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $lines[] = $row;
+ $json[] = array(
+ 'color' => isset($colors[$row['kvmstate']]) ? $colors[$row['kvmstate']] : '#000',
+ 'label' => $row['kvmstate'],
+ 'value' => $row['count']
+ );
+ }
+ Render::addTemplate('kvmstate', array('rows' => $lines, 'json' => json_encode($json)));
+ }
+
+ private function showId44()
+ {
+ global $STATS_COLORS, $SIZE_ID44;
+ $res = Database::simpleQuery("SELECT id44mb, Count(*) AS `count` FROM machine GROUP BY id44mb");
+ $lines = array();
+ $total = 0;
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $total += $row['count'];
+ $gb = ceil($row['id44mb'] / 1024);
+ for ($i = 1; $i < count($SIZE_ID44); ++$i) {
+ if ($SIZE_ID44[$i] < $gb) continue;
+ if ($SIZE_ID44[$i] - $gb >= $gb - $SIZE_ID44[$i-1]) --$i;
+ $gb = $SIZE_ID44[$i];
+ break;
+ }
+ if (isset($lines[$gb])) {
+ $lines[$gb] += $row['count'];
+ } else {
+ $lines[$gb] = $row['count'];
+ }
+ }
+ asort($lines);
+ $data = array('rows' => array());
+ $json = array();
+ $id = 0;
+ foreach (array_reverse($lines, true) as $k => $v) {
+ $data['rows'][] = array('gb' => $k, 'count' => $v, 'class' => $this->hddColorClass($k));
+ if ($k === 0) {
+ $color = '#e55';
+ } else {
+ $color = $STATS_COLORS[$id++ % count($STATS_COLORS)];
+ }
+ $json[] = array(
+ 'color' => $color,
+ 'label' => (string)$k,
+ 'value' => $v
+ );
+ }
+ $this->capChart($json, 0.95);
+ $data['json'] = json_encode($json);
+ Render::addTemplate('id44', $data);
+ }
+
+ private function showLatestMachines()
+ {
+ $data = array('cutoff' => ceil(time() / 3600) * 3600 - 86400 * 7);
+ $res = Database::simpleQuery("SELECT machineuuid, clientip, hostname, firstseen, mbram, kvmstate, id44mb FROM machine"
+ . " WHERE firstseen > :cutoff ORDER BY firstseen DESC LIMIT 32", $data);
+ $rows = array();
+ $count = 0;
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ if (empty($row['hostname'])) {
+ $row['hostname'] = $row['clientip'];
+ }
+ $row['firstseen'] = date('d.m. H:i', $row['firstseen']);
+ $row['gbram'] = round(round($row['mbram'] / 500) / 2, 1); // Trial and error until we got "expected" rounding..
+ $row['gbtmp'] = round($row['id44mb'] / 1024);
+ $row['ramclass'] = $this->ramColorClass($row['mbram']);
+ $row['kvmclass'] = $this->kvmColorClass($row['kvmstate']);
+ $row['hddclass'] = $this->hddColorClass($row['gbtmp']);
+ $row['kvmicon'] = $row['kvmstate'] === 'ENABLED' ? 'âś“' : 'âś—';
+ if (++$count > 5) {
+ $row['style'] = 'display:none';
+ }
+ $rows[] = $row;
+ }
+ Render::addTemplate('newclients', array('rows' => $rows, 'openbutton' => $count > 5));
+ }
+
+ private function showMachineList($filter, $argument)
+ {
+ global $SIZE_RAM, $SIZE_ID44;
+ $join = '';
+ $filters = array('cpumodel', 'realcores', 'kvmstate', 'clientip', 'macaddr', 'machineuuid', 'systemmodel');
+ if (in_array($filter, $filters)) {
+ // Simple filters mapping into db
+ $where = " $filter = :argument";
+ $args = array('argument' => $argument);
+ } elseif ($filter === 'gbram') {
+ // Memory by rounded GB
+ $lower = floor($this->findBestValue($SIZE_RAM, $argument, false) * 1024 - 100);
+ $upper = ceil($this->findBestValue($SIZE_RAM, $argument, true) * 1024 + 100);
+ $where = " mbram BETWEEN $lower AND $upper";
+ $args = array();
+ } elseif ($filter === 'hddgb') {
+ // HDD by rounded GB
+ $lower = floor($this->findBestValue($SIZE_ID44, $argument, false) * 1024 - 100);
+ $upper = ceil($this->findBestValue($SIZE_ID44, $argument, true) * 1024 + 100);
+ $where = " id44mb BETWEEN $lower AND $upper";
+ $args = array();
+ } elseif ($filter === 'subnet') {
+ $argument = preg_replace('/[^0-9\.:]/', '', $argument);
+ $where = " clientip LIKE '$argument%'";
+ $args = array();
+ } elseif ($filter === 'badsectors') {
+ $where = " badsectors >= :argument ";
+ $args = array('argument' => $argument);
+ } elseif ($filter === 'state') {
+ if ( $argument === 'on') {
+ $where = " lastseen + 600 > UNIX_TIMESTAMP() ";
+ } elseif ($argument === 'off') {
+ $where = " lastseen + 600 < UNIX_TIMESTAMP() ";
+ } elseif ($argument === 'idle') {
+ $where = " lastseen + 600 > UNIX_TIMESTAMP() AND logintime = 0 ";
+ } elseif ($argument === 'occupied') {
+ $where = " lastseen + 600 > UNIX_TIMESTAMP() AND logintime <> 0 ";
+ } else {
+ Message::addError('invalid-filter');
+ return;
+ }
+ } elseif ($filter === 'location') {
+ $where = "subnet.locationid = :lid OR machine.locationid = :lid";
+ $join = " INNER JOIN subnet ON (INET_ATON(clientip) BETWEEN startaddr AND endaddr) ";
+ $args = array('lid' => (int)$argument);
+ } else {
+ Message::addError('invalid-filter');
+ return;
+ }
+ $res = Database::simpleQuery("SELECT machineuuid, macaddr, clientip, firstseen, lastseen,"
+ . " logintime, lastboot, realcores, mbram, kvmstate, cpumodel, id44mb, hostname, notes IS NOT NULL AS hasnotes, badsectors FROM machine"
+ . " $join WHERE $where ORDER BY lastseen DESC, clientip ASC", $args);
+ $rows = array();
+ $NOW = time();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ if ($NOW - $row['lastseen'] > 610) {
+ $row['state_off'] = true;
+ } elseif ($row['logintime'] == 0) {
+ $row['state_idle'] = true;
+ } else {
+ $row['state_occupied'] = true;
+ }
+ //$row['firstseen'] = date('d.m.Y H:i', $row['firstseen']);
+ $row['lastseen'] = date('d.m. H:i', $row['lastseen']);
+ //$row['lastboot'] = date('d.m. H:i', $row['lastboot']);
+ $row['gbram'] = round(round($row['mbram'] / 500) / 2, 1); // Trial and error until we got "expected" rounding..
+ $row['gbtmp'] = round($row['id44mb'] / 1024);
+ $octets = explode('.', $row['clientip']);
+ if (count($octets) === 4) {
+ $row['subnet'] = "$octets[0].$octets[1].$octets[2].";
+ $row['lastoctet'] = $octets[3];
+ }
+ $row['ramclass'] = $this->ramColorClass($row['mbram']);
+ $row['kvmclass'] = $this->kvmColorClass($row['kvmstate']);
+ $row['hddclass'] = $this->hddColorClass($row['gbtmp']);
+ if (empty($row['hostname'])) $row['hostname'] = $row['clientip'];
+ $rows[] = $row;
+ }
+ Render::addTemplate('clientlist', array('rows' => $rows, 'filter' => $filter, 'argument' => $argument));
+ }
+
+ private function ramColorClass($mb)
+ {
+ if ($mb < 1500)
+ return 'danger';
+ if ($mb < 2500)
+ return 'warning';
+ return '';
+ }
+
+ private function kvmColorClass($state)
+ {
+ if ($state === 'DISABLED')
+ return 'danger';
+ if ($state === 'UNKNOWN' || $state === 'UNSUPPORTED')
+ return 'warning';
+ return '';
+ }
+
+ private function hddColorClass($gb)
+ {
+ if ($gb < 7)
+ return 'danger';
+ if ($gb < 25)
+ return 'warning';
+ return '';
+ }
+
+ private function findBestValue($array, $value, $up)
+ {
+ $best = 0;
+ for ($i = 0; $i < count($array); ++$i) {
+ if (abs($array[$i] - $value) < abs($array[$best] - $value)) {
+ $best = $i;
+ }
+ }
+ if (!$up && $best === 0) {
+ return $array[0];
+ }
+ if ($up && $best + 1 === count($array)) {
+ return $array[$best];
+ }
+ if ($up) {
+ return ($array[$best] + $array[$best + 1]) / 2;
+ }
+ return ($array[$best] + $array[$best - 1]) / 2;
+ }
+
+ private function fillSessionInfo(&$row)
+ {
+ $res = Database::simpleQuery("SELECT dateline, username, data FROM statistic"
+ . " WHERE clientip = :ip AND typeid = '.vmchooser-session-name'"
+ . " AND dateline BETWEEN :start AND :end", array(
+ 'ip' => $row['clientip'],
+ 'start' => $row['logintime'] - 60,
+ 'end' => $row['logintime'] + 300
+ ));
+ $session = false;
+ while ($r = $res->fetch(PDO::FETCH_ASSOC)) {
+ if ($session === false || abs($session['dateline'] - $row['logintime']) > abs($r['dateline'] - $row['logintime'])) {
+ $session = $r;
+ }
+ }
+ if ($session !== false) {
+ $row['session'] = $session['data'];
+ $row['username'] = $session['username'];
+ }
+ }
+
+ private function showMachine($uuid)
+ {
+ $client = Database::queryFirst("SELECT machineuuid, macaddr, clientip, firstseen, lastseen, logintime, lastboot,"
+ . " mbram, kvmstate, cpumodel, id44mb, data, hostname, notes FROM machine WHERE machineuuid = :uuid",
+ array('uuid' => $uuid));
+ // Mangle fields
+ $NOW = time();
+ if ($NOW - $client['lastseen'] > 610) {
+ $client['state_off'] = true;
+ } elseif ($client['logintime'] == 0) {
+ $client['state_idle'] = true;
+ } else {
+ $client['state_occupied'] = true;
+ $this->fillSessionInfo($client);
+ }
+ $client['firstseen_s'] = date('d.m.Y H:i', $client['firstseen']);
+ $client['lastseen_s'] = date('d.m.Y H:i', $client['lastseen']);
+ $uptime = $NOW - $client['lastboot'];
+ $client['lastboot_s'] = date('d.m.Y H:i', $client['lastboot']) . ' (Up ' . floor($uptime / 86400) . 'd ' . gmdate('H:i', $uptime) . ')';
+ $client['logintime_s'] = date('d.m.Y H:i', $client['logintime']);
+ $client['gbram'] = round(round($client['mbram'] / 500) / 2, 1);
+ $client['gbtmp'] = round($client['id44mb'] / 1024);
+ $client['ramclass'] = $this->ramColorClass($client['mbram']);
+ $client['kvmclass'] = $this->kvmColorClass($client['kvmstate']);
+ $client['hddclass'] = $this->hddColorClass($client['gbtmp']);
+ // Parse the giant blob of data
+ $hdds = array();
+ if (preg_match_all('/##### ([^#]+) #+$(.*?)^#####/ims', $client['data'] . '########', $out, PREG_SET_ORDER)) {
+ foreach ($out as $section) {
+ if ($section[1] === 'CPU') {
+ $this->parseCpu($client, $section[2]);
+ }
+ if ($section[1] === 'dmidecode') {
+ $this->parseDmiDecode($client, $section[2]);
+ }
+ if ($section[1] === 'Partition tables') {
+ $this->parseHdd($hdds, $section[2]);
+ }
+ if (isset($hdds['hdds']) && $section[1] === 'smartctl') {
+ // This currently required that the partition table section comes first...
+ $this->parseSmartctl($hdds['hdds'], $section[2]);
+ }
+ }
+ }
+ unset($client['data']);
+ // Throw output at user
+ Render::addTemplate('machine-main', $client);
+ // Sessions
+ $NOW = time();
+ $cutoff = $NOW - 86400 * 7;
+ //if ($cutoff < $client['firstseen']) $cutoff = $client['firstseen'];
+ $scale = 100 / ($NOW - $cutoff);
+ $res = Database::simpleQuery("SELECT dateline, typeid, data FROM statistic"
+ . " WHERE dateline > :cutoff AND typeid IN ('~session-length', '~offline-length') AND machineuuid = :uuid ORDER BY dateline ASC", array(
+ 'cutoff' => $cutoff - 86400 * 14,
+ 'uuid' => $uuid
+ ));
+ $spans['rows'] = array();
+ $spans['graph'] = '';
+ $last = false;
+ $first = true;
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ if ($first && $row['dateline'] > $cutoff && $client['lastboot'] > $cutoff) {
+ // Special case: offline before
+ $spans['graph'] .= '
';
+ }
+ $first = false;
+ if ($row['dateline'] + $row['data'] < $cutoff || $row['data'] > 864000) continue;
+ if ($last !== false && abs($last['dateline'] - $row['dateline']) < 30
+ && abs($last['data'] - $row['data']) < 30) continue;
+ if ($last !== false && $last['dateline'] + $last['data'] > $row['dateline']) {
+ $point = $last['dateline'] + $last['data'];
+ $row['data'] -= ($point - $row['dateline']);
+ $row['dateline'] = $point;
+ }
+ if ($row['dateline'] < $cutoff) {
+ $row['data'] -= ($cutoff - $row['dateline']);
+ $row['dateline'] = $cutoff;
+ }
+ $row['from'] = date('d.m. H:i', $row['dateline']);
+ $row['duration'] = floor($row['data'] / 86400) . 'd ' . gmdate('H:i', $row['data']);
+ if ($row['typeid'] === '~offline-length') {
+ $row['glyph'] = 'off';
+ $color = '#444';
+ } else {
+ $row['glyph'] = 'user';
+ $color = '#e77';
+ }
+ $spans['graph'] .= '
';
+ $spans['rows'][] = $row;
+ $last = $row;
+ }
+ if ($first && $client['lastboot'] > $cutoff) {
+ // Special case: offline before
+ $spans['graph'] .= '
';
+ }
+ if (isset($client['state_occupied'])) {
+ $spans['graph'] .= '
';
+ } elseif (isset($client['state_off'])) {
+ $spans['graph'] .= '
';
+ }
+ $t = explode('-', date('Y-n-j-G', $cutoff));
+ if ($t[3] >= 8 && $t[3] <= 22) {
+ $start = mktime(22, 0, 0, $t[1], $t[2], $t[0]);
+ } else {
+ $start = mktime(22, 0, 0, $t[1], $t[2] - 1, $t[0]);
+ }
+ for ($i = $start; $i < $NOW; $i += 86400) {
+ $spans['graph'] .= '
';
+ }
+ if (count($spans['rows']) > 10) {
+ $spans['hasrows2'] = true;
+ $spans['rows2'] = array_slice($spans['rows'], ceil(count($spans['rows']) / 2));
+ $spans['rows'] = array_slice($spans['rows'], 0, ceil(count($spans['rows']) / 2));
+ }
+ Render::addTemplate('machine-usage', $spans);
+ // Any hdds?
+ if (!empty($hdds['hdds'])) {
+ Render::addScriptBottom('chart.min');
+ Render::addTemplate('machine-hdds', $hdds);
+ }
+ // Client log
+ $lres = Database::simpleQuery("SELECT logid, dateline, logtypeid, clientip, description, extra FROM clientlog"
+ . " WHERE clientip = :clientip ORDER BY logid DESC LIMIT 25", array('clientip' => $client['clientip']));
+ $today = date('d.m.Y');
+ $yesterday = date('d.m.Y', time() - 86400);
+ $count = 0;
+ $log = array();
+ while ($row = $lres->fetch(PDO::FETCH_ASSOC)) {
+ if (substr($row['description'], -5) === 'on :0' && strpos($row['description'], 'root logged') === false) continue;
+ $day = date('d.m.Y', $row['dateline']);
+ if ($day === $today) {
+ $day = Dictionary::translate('today');
+ } elseif ($day === $yesterday) {
+ $day = Dictionary::translate('yesterday');
+ }
+ $row['date'] = $day . date(' H:i', $row['dateline']);
+ $row['icon'] = $this->eventToIconName($row['logtypeid']);
+ $log[] = $row;
+ if (++$count === 10) break;
+ }
+ Render::addTemplate('syslog', array(
+ 'clientip' => $client['clientip'],
+ 'list' => $log
+ ));
+ // Notes
+ Render::addTemplate('machine-notes', $client);
+ }
+
+ private function eventToIconName($event)
+ {
+ switch ($event) {
+ case 'session-open':
+ return 'glyphicon-log-in';
+ case 'session-close':
+ return 'glyphicon-log-out';
+ case 'partition-swap':
+ return 'glyphicon-info-sign';
+ case 'partition-temp':
+ case 'smartctl-realloc':
+ return 'glyphicon-exclamation-sign';
+ default:
+ return 'glyphicon-minus';
+ }
+ }
+
+ private function parseCpu(&$row, $data)
+ {
+ if (0 >= preg_match_all('/^(.+):\s+(\d+)$/im', $data, $out, PREG_SET_ORDER)) return;
+ foreach ($out as $entry) {
+ $row[str_replace(' ', '', $entry[1])] = $entry[2];
+ }
+ }
+
+ private function parseDmiDecode(&$row, $data)
+ {
+ $lines = preg_split("/[\r\n]+/", $data);
+ $section = false;
+ $ramOk = false;
+ $ramForm = $ramType = $ramSpeed = $ramClockSpeed = false;
+ foreach ($lines as $line) {
+ if (empty($line)) continue;
+ if ($line{0} !== "\t" && $line{0} !== ' ') {
+ $section = $line;
+ $ramOk = false;
+ if (($ramForm || $ramType) && ($ramSpeed || $ramClockSpeed)) {
+ if (isset($row['ramtype']) && !$ramClockSpeed) continue;
+ $row['ramtype'] = $ramType . ' ' . $ramForm;
+ if ($ramClockSpeed) $row['ramtype'] .= ', ' . $ramClockSpeed;
+ elseif ($ramSpeed) $row['ramtype'] .= ', ' . $ramSpeed;
+ $ramForm = false;
+ $ramType = false;
+ $ramClockSpeed = false;
+ }
+ continue;
+ }
+ if ($section === 'System Information' || $section === 'Base Board Information') {
+ if (empty($row['pcmodel']) && preg_match('/^\s*Product Name: +(\S.+?) *$/i', $line, $out)) {
+ $row['pcmodel'] = $out[1];
+ }
+ if (empty($row['manufacturer']) && preg_match('/^\s*Manufacturer: +(\S.+?) *$/i', $line, $out)) {
+ $row['manufacturer'] = $out[1];
+ }
+ }
+ else if ($section === 'Physical Memory Array') {
+ if (!$ramOk && preg_match('/Use: System Memory/i', $line)) {
+ $ramOk = true;
+ }
+ if ($ramOk && preg_match('/^\s*Number Of Devices: +(\S.+?) *$/i', $line, $out)) {
+ $row['ramslotcount'] = $out[1];
+ }
+ if ($ramOk && preg_match('/^\s*Maximum Capacity: +(\S.+?)\s*$/i', $line, $out)) {
+ $row['maxram'] = preg_replace('/([MGT])B/', '$1iB', $out[1]);
+ }
+ }
+ else if ($section === 'Memory Device') {
+ if (preg_match('/^\s*Size:\s*(.*?)\s*$/i', $line, $out)) {
+ $row['extram'] = true;
+ if (preg_match('/(\d+)\s*(\w)i?B/i', $out[1], $out)) {
+ $out[2] = strtoupper($out[2]);
+ if ($out[2] === 'K' || ($out[2] === 'M' && $out[1] < 500)) {
+ $ramForm = $ramType = $ramSpeed = $ramClockSpeed = false;
+ continue;
+ }
+ if ($out[2] === 'M' && $out[1] >= 1024) {
+ $out[2] = 'G';
+ $out[1] = floor(($out[1] + 100) / 1024);
+ }
+ $row['ramslot'][]['size'] = $out[1] . ' ' . strtoupper($out[2]) . 'iB';
+ } else if (!isset($row['ramslot']) || (count($row['ramslot']) < 8 && (!isset($row['ramslotcount']) || $row['ramslotcount'] <= 8))) {
+ $row['ramslot'][]['size'] = '_____';
+ }
+ }
+ if (preg_match('/^\s*Form Factor:\s*(.*?)\s*$/i', $line, $out) && $out[1] !== 'Unknown') {
+ $ramForm = $out[1];
+ }
+ if (preg_match('/^\s*Type:\s*(.*?)\s*$/i', $line, $out) && $out[1] !== 'Unknown') {
+ $ramType = $out[1];
+ }
+ if (preg_match('/^\s*Speed:\s*(\d.*?)\s*$/i', $line, $out)) {
+ $ramSpeed = $out[1];
+ }
+ if (preg_match('/^\s*Configured Clock Speed:\s*(\d.*?)\s*$/i', $line, $out)) {
+ $ramClockSpeed = $out[1];
+ }
+ }
+ }
+ if (empty($row['ramslotcount'])) $row['ramslotcount'] = count($row['ramslot']);
+ }
+
+ private function parseHdd(&$row, $data)
+ {
+ $hdds = array();
+ // Could have more than one disk - linear scan
+ $lines = preg_split("/[\r\n]+/", $data);
+ $dev = false;
+ $i = 0;
+ foreach ($lines as $line) {
+ if (preg_match('/^Disk (\S+):.* (\d+) bytes/i', $line, $out)) {
+ // disk total size and name
+ unset($hdd);
+ $unit = 0;
+ $hdd = array(
+ 'devid' => 'devid-' . ++$i,
+ 'dev' => $out[1],
+ 'size' => round($out[2] / (1024 * 1024 * 1024)),
+ 'used' => 0,
+ 'partitions' => array(),
+ 'json' => array(),
+ );
+ $hdds[] = &$hdd;
+ } elseif (preg_match('/^Units =.*= (\d+) bytes/i', $line, $out)) {
+ // Unit for start and end
+ $unit = $out[1] / (1024 * 1024); // Convert so that multiplying by unit yields MiB
+ } else if (isset($hdd) && $unit !== 0 && preg_match(',^/dev/(\S+)\s+.*\s(\d+)[\+\-]?\s+(\d+)[\+\-]?\s+\d+[\+\-]?\s+([0-9a-f]+)\s+(.*)$,i', $line, $out)) {
+ // Some partition
+ $type = strtolower($out[4]);
+ if ($type === '5' || $type === 'f' || $type === '85') continue;
+ $partsize = round(($out[3] - $out[2]) * $unit);
+ $hdd['partitions'][] = array(
+ 'id' => $out[1],
+ 'name' => $out[1],
+ 'size' => round($partsize / 1024, $partsize < 1024 ? 1 : 0),
+ 'type' => ($type === '44' ? 'OpenSLX' : $out[5]),
+ );
+ $hdd['json'][] = array(
+ 'label' => $out[1],
+ 'value' => $partsize,
+ 'color' => ($type === '44' ? '#4d4' : ($type === '82' ? '#48f' : '#e55')),
+ );
+ $hdd['used'] += $partsize;
+ }
+ }
+ unset($hdd);
+ $i = 0;
+ foreach ($hdds as &$hdd) {
+ $hdd['used'] = round($hdd['used'] / 1024);
+ $free = $hdd['size'] - $hdd['used'];
+ if ($free > 5) {
+ $hdd['partitions'][] = array(
+ 'id' => 'free-id-' . $i,
+ 'name' => Dictionary::translate('unused'),
+ 'size' => $free,
+ 'type' => '-',
+ );
+ $hdd['json'][] = array(
+ 'label' => 'free-id-' . $i,
+ 'value' => $free * 1024,
+ 'color' => '#aaa',
+ );
+ ++$i;
+ }
+ $hdd['json'] = json_encode($hdd['json']);
+ }
+ unset($hdd);
+ $row['hdds'] = &$hdds;
+ }
+
+ private function parseSmartctl(&$hdds, $data)
+ {
+ $lines = preg_split("/[\r\n]+/", $data);
+ $i = 0;
+ foreach ($lines as $line) {
+ if (preg_match('/^NEXTHDD=(.+)$/', $line, $out)) {
+ unset($dev);
+ foreach ($hdds as &$hdd) {
+ if ($hdd['dev'] === $out[1]) $dev =& $hdd;
+ }
+ continue;
+ }
+ if (!isset($dev)) continue;
+ if (preg_match('/^([A-Z][^:]+):\s*(.*)$/', $line, $out)) {
+ $dev['s_' . preg_replace('/\s|-|_/', '', $out[1])] = $out[2];
+ } elseif (preg_match('/^\s*\d+\s+(\S+)\s+\S+\s+\d+\s+\d+\s+\d+\s+\S+\s+(\d+)(\s|$)/', $line, $out)) {
+ $dev['s_' . preg_replace('/\s|-|_/', '', $out[1])] = $out[2];
+ }
+ }
+ // Format strings
+ foreach ($hdds as &$hdd) {
+ if (isset($hdd['s_PowerOnHours'])) {
+ $hdd['PowerOnTime'] = '';
+ $val = (int)$hdd['s_PowerOnHours'];
+ if ($val > 8760) {
+ $hdd['PowerOnTime'] .= floor($val / 8760) . 'Y, ';
+ $val %= 8760;
+ }
+ if ($val > 720) {
+ $hdd['PowerOnTime'] .= floor($val / 720) . 'M, ';
+ $val %= 720;
+ }
+ if ($val > 24) {
+ $hdd['PowerOnTime'] .= floor($val / 24) . 'd, ';
+ $val %= 24;
+ }
+ $hdd['PowerOnTime'] .= $val . 'h';
+ }
+ }
+ }
+
+}
diff --git a/modules/statistics/templates/clientlist.html b/modules/statistics/templates/clientlist.html
new file mode 100644
index 00000000..8e8565fe
--- /dev/null
+++ b/modules/statistics/templates/clientlist.html
@@ -0,0 +1,45 @@
+{{lang_clientList}}
+{{filter}} ~= {{argument}}
+
+
+
+
+ {{lang_machine}}
+ {{lang_address}}
+ {{lang_lastSeen}}
+ {{lang_kvmSupport}}
+ {{lang_gbRam}}
+ {{lang_tmpGb}}
+ {{lang_cpuModel}}
+
+ {{#rows}}
+
+
+ {{#hasnotes}} {{/hasnotes}}
+ {{#state_off}}
+
+ {{/state_off}}
+ {{#state_idle}}
+
+ {{/state_idle}}
+ {{#state_occupied}}
+
+ {{/state_occupied}}
+ {{hostname}}
+ {{machineuuid}}
+
+ {{subnet}} {{lastoctet}} {{macaddr}}
+ {{lastseen}}
+ {{kvmstate}}
+ {{gbram}} GiB
+
+ {{gbtmp}} GiB
+ {{#badsectors}}
+
+ {{badsectors}}
+
{{/badsectors}}
+
+ {{lang_realCores}}: {{realcores}}{{cpumodel}}
+
+ {{/rows}}
+
diff --git a/modules/statistics/templates/cpumodels.html b/modules/statistics/templates/cpumodels.html
new file mode 100644
index 00000000..2f24cd92
--- /dev/null
+++ b/modules/statistics/templates/cpumodels.html
@@ -0,0 +1,51 @@
+
+
+
+ {{lang_modelStats}}
+
+
+
+
+
+
+ {{lang_modelName}}
+ {{lang_cpuCores}}
+ {{lang_modelCount}}
+
+ {{#rows}}
+
+
+ {{systemmodel}}
+
+ {{cores}}
+ {{count}}
+
+ {{/rows}}
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/statistics/templates/id44.html b/modules/statistics/templates/id44.html
new file mode 100644
index 00000000..730839b1
--- /dev/null
+++ b/modules/statistics/templates/id44.html
@@ -0,0 +1,48 @@
+
+
+
+ {{lang_tempPartStats}}
+
+
+
+
+
+
+ {{lang_partitionSize}}
+ {{lang_machineCount}}
+
+ {{#rows}}
+
+ {{gb}} GiB
+ {{count}}
+
+ {{/rows}}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/statistics/templates/kvmstate.html b/modules/statistics/templates/kvmstate.html
new file mode 100644
index 00000000..107a34f7
--- /dev/null
+++ b/modules/statistics/templates/kvmstate.html
@@ -0,0 +1,47 @@
+
+
+
+ {{lang_kvmStats}}
+
+
+
+
+
+
+ {{lang_kvmState}}
+ {{lang_machineCount}}
+
+ {{#rows}}
+
+ {{kvmstate}}
+ {{count}}
+
+ {{/rows}}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/statistics/templates/machine-hdds.html b/modules/statistics/templates/machine-hdds.html
new file mode 100644
index 00000000..fd6cf1be
--- /dev/null
+++ b/modules/statistics/templates/machine-hdds.html
@@ -0,0 +1,67 @@
+{{lang_hdds}}
+
+ {{#hdds}}
+
+
+
+ {{s_ModelFamily}} {{dev}}
+
+
+ {{#s_DeviceModel}}
+
{{lang_modelNo}}: {{s_DeviceModel}}, {{lang_serialNo}}: {{s_SerialNumber}}
+ {{/s_DeviceModel}}
+ {{#s_ReallocatedSectorCt}}
+
{{lang_reallocatedSectors}}: {{s_ReallocatedSectorCt}}
+ {{/s_ReallocatedSectorCt}}
+ {{#s_CurrentPendingSector}}
+
{{lang_pendingSectors}}: {{s_CurrentPendingSector}}
+ {{/s_CurrentPendingSector}}
+ {{#s_PowerOnHours}}
+
{{lang_powerOnTime}}: {{s_PowerOnHours}} {{lang_hours}} ({{PowerOnTime}})
+ {{/s_PowerOnHours}}
+
+
+
+
+ {{lang_partName}}
+ {{lang_partSize}}
+ {{lang_partType}}
+
+ {{#partitions}}
+
+ {{name}}
+ {{size}} GiB
+ {{type}}
+
+ {{/partitions}}
+
+
{{lang_total}}: {{size}} GiB
+
+
+
+
+
+
+
+
+
+ {{/hdds}}
+
\ No newline at end of file
diff --git a/modules/statistics/templates/machine-main.html b/modules/statistics/templates/machine-main.html
new file mode 100644
index 00000000..8071416a
--- /dev/null
+++ b/modules/statistics/templates/machine-main.html
@@ -0,0 +1,124 @@
+
+ {{hostname}} {{#hostname}}–{{/hostname}} {{clientip}}
+ {{#notes}} {{/notes}}
+
+
+
+
+
+
+ {{lang_machineSummary}}
+
+
+
+
+ {{lang_uuid}}
+ {{machineuuid}}
+
+
+ {{lang_macAddr}}
+ {{macaddr}}
+
+
+ {{lang_ip}}
+ {{clientip}}
+
+ {{#hostname}}
+
+ {{lang_hostname}}
+ {{hostname}}
+
+ {{/hostname}}
+
+ {{lang_firstSeen}}
+ {{firstseen_s}}
+
+
+ {{lang_lastBoot}}
+ {{lastboot_s}}
+
+
+ {{lang_lastSeen}}
+ {{lastseen_s}}
+
+
+ {{lang_usageState}}
+
+ {{#state_off}}
+ {{lang_machineOff}}
+ {{/state_off}}
+ {{#state_idle}}
+ {{lang_machineIdle}}
+ {{/state_idle}}
+ {{#state_occupied}}
+ {{#username}}
+ {{lang_machineOccupiedBy}} {{username}}
+ {{/username}}
+ {{^username}}
+ {{lang_machineOccupied}}
+ {{/username}}
+ {{logintime_s}}
+ {{/state_occupied}}
+ {{#session}}
+ {{session}}
+ {{/session}}
+
+
+
+
+
+
+
+
+
+ {{lang_hardwareSummary}}
+
+
+
+
+ {{lang_cpuModel}}
+
+ {{cpumodel}}
+ {{#Sockets}}
+
+ {{lang_sockets}}: {{Sockets}}, {{lang_cores}}: {{Realcores}}, {{lang_virtualCores}}: {{Virtualcores}}
+
+ {{/Sockets}}
+
+
+
+ {{lang_model}}
+ {{pcmodel}} ({{manufacturer}})
+
+
+ {{lang_ram}}
+
+ {{gbram}} GiB
+ {{#maxram}}({{lang_maximumAbbrev}} {{maxram}}){{/maxram}}
+ {{ramtype}}
+
+
+ {{#extram}}
+
+ {{lang_ramSlots}}
+
+ {{ramslotcount}}:
+ {{#ramslot}}
+ [ {{size}} ]
+ {{/ramslot}}
+
+
+ {{/extram}}
+
+ {{lang_tempPart}}
+ {{gbtmp}} GiB
+
+
+ {{lang_64bitSupport}}
+ {{kvmstate}}
+
+
+
+
+
+
diff --git a/modules/statistics/templates/machine-notes.html b/modules/statistics/templates/machine-notes.html
new file mode 100644
index 00000000..c4f97543
--- /dev/null
+++ b/modules/statistics/templates/machine-notes.html
@@ -0,0 +1,17 @@
+
+{{lang_notes}}
+
+
+
+
+
+
+
+
+ {{notes}}
+ {{lang_save}}
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/statistics/templates/machine-usage.html b/modules/statistics/templates/machine-usage.html
new file mode 100644
index 00000000..ffaa747b
--- /dev/null
+++ b/modules/statistics/templates/machine-usage.html
@@ -0,0 +1,51 @@
+
+
+
+
+ {{lang_usageDetails}}
+
+
+
+
+
+
+ Type
+ When
+ Length
+
+ {{#rows}}
+
+
+ {{from}}
+ {{duration}}
+
+ {{/rows}}
+
+
+
+
+ {{#hasrows2}}
+
+ Type
+ When
+ Length
+
+ {{/hasrows2}}
+ {{#rows2}}
+
+
+ {{from}}
+ {{duration}}
+
+ {{/rows2}}
+
+
+
+
{{{graph}}}
+
+ {{lang_timebarDesc}}
+
+
+
+
+
diff --git a/modules/statistics/templates/memory.html b/modules/statistics/templates/memory.html
new file mode 100644
index 00000000..f4d2ad24
--- /dev/null
+++ b/modules/statistics/templates/memory.html
@@ -0,0 +1,47 @@
+
+
+
+ {{lang_memoryStats}}
+
+
+
+
+
+
+ {{lang_ramSize}}
+ {{lang_machineCount}}
+
+ {{#rows}}
+
+ {{gb}} GiB
+ {{count}}
+
+ {{/rows}}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/statistics/templates/newclients.html b/modules/statistics/templates/newclients.html
new file mode 100644
index 00000000..0d9c74df
--- /dev/null
+++ b/modules/statistics/templates/newclients.html
@@ -0,0 +1,44 @@
+
+
+
+ {{lang_newMachines}}
+
+
+
+
+ {{lang_machine}}
+
+ 64Bit
+ RAM
+ HDD
+
+ {{#rows}}
+
+ {{hostname}}
+ {{firstseen}}
+ {{kvmicon}}
+ {{gbram}} GiB
+ {{gbtmp}} GiB
+
+ {{/rows}}
+ {{#openbutton}}
+
+
+
+
+
+
+
+
+
+
+ {{/openbutton}}
+
+
+
+
\ No newline at end of file
diff --git a/modules/statistics/templates/summary.html b/modules/statistics/templates/summary.html
new file mode 100644
index 00000000..5f16fd89
--- /dev/null
+++ b/modules/statistics/templates/summary.html
@@ -0,0 +1,33 @@
+
+
+
+
+ {{#badhdd}}
+
+ {{/badhdd}}
+
+
+
+
+
+
+
+
diff --git a/modules/statistics/templates/syslog.html b/modules/statistics/templates/syslog.html
new file mode 100644
index 00000000..c82cb8ac
--- /dev/null
+++ b/modules/statistics/templates/syslog.html
@@ -0,0 +1,43 @@
+{{lang_logHeadline}}
+
+
+
+ {{lang_when}}
+ {{lang_event}}
+ {{lang_details}}
+
+
+ {{#list}}
+
+
+ {{date}}
+ {{description}}
+ {{#extra}}
+ »
+
+ {{/extra}}
+
+ {{/list}}
+
+
+
+
+
+
+
+
diff --git a/modules/support/config.json b/modules/support/config.json
new file mode 100644
index 00000000..4e7fa5fb
--- /dev/null
+++ b/modules/support/config.json
@@ -0,0 +1,4 @@
+{
+ "category":"content",
+ "enabled":"true"
+}
diff --git a/modules/support/module.inc.php b/modules/support/module.inc.php
new file mode 100644
index 00000000..d4012c1a
--- /dev/null
+++ b/modules/support/module.inc.php
@@ -0,0 +1,77 @@
+SMTPDebug = 3; // Enable verbose debug output
+ $mail->isSMTP(); // Set mailer to use SMTP
+ $mail->Host = 'mx.c3sl.ufpr.br'; // Specify main and backup SMTP servers
+ $mail->SMTPAuth = true; // Enable SMTP authentication
+ $mail->Username = 'xxx00@inf.ufpr.br'; // SMTP username
+ $mail->Password = ''; // SMTP password
+ // $mail->SMTPSecure = 'false'; // Enable TLS encryption, `ssl` also accepted
+ $mail->Port = 25; // TCP port to connect to
+
+ $mail->From = 'xxx00@inf.ufpr.br';
+ $mail->FromName = 'Someone';
+ $mail->addAddress('receiver@email.com', 'Another One'); // Add a recipient
+ // $mail->addAttachment('/var/tmp/file.tar.gz'); // Add attachments
+ // $mail->addAttachment('/tmp/image.jpg', 'new.jpg'); // Optional name
+ $mail->isHTML(true); // Set email format to HTML
+ $mail->Subject = 'Here is the subject';
+ $mail->Body = 'This is the HTML message body in bold! ';
+ $mail->AltBody = 'This is the body in plain text for non-HTML mail clients';
+
+ if(!$mail->send()) {
+ echo 'Message could not be sent.';
+ echo 'Mailer Error: ' . $mail->ErrorInfo;
+ } else {
+ echo 'Message has been sent';
+ }
+ */
+// $uploaddir = '/var/www/uploads/';
+// $uploadfile = $uploaddir . basename($_FILES['inp_file']['name']);
+// if(move_uploaded_file($_FILES['inp_file']['tmp_name'], $uploadfile))
+// Message::addSuccess('news-save-success');
+// else
+// Message::addError('news-empty');
+// mail($to,$_POST[assuntoEmail],$_POST[conteudoEmail],"-r".$from);
+// mail($to,$assunto,$content,$headers);
+
+
+ }
+
+
+
+ protected function doRender(){
+ error_reporting(E_ALL);
+ ini_set('display_errors','on');
+ if (strpos($_SERVER['REQUEST_URI'], "true") !== false)
+ Render::addTemplate('page-faq');
+ else
+ Render::addTemplate('page-support');
+// Render::addTemplate('page-support', array(
+// 'token' => Session:get('token'));
+ }
+
+
+}
diff --git a/modules/support/templates/go-pear.phar b/modules/support/templates/go-pear.phar
new file mode 100644
index 00000000..9038c747
--- /dev/null
+++ b/modules/support/templates/go-pear.phar
@@ -0,0 +1,99492 @@
+
+ * @author Greg Beaver
+ * @link http://www.synapticmedia.net Synaptic Media
+ * @version Id: Archive.php,v 1.52 2007/09/01 20:28:14 cellog Exp $
+ * @package PHP_Archive
+ * @category PHP
+ */
+
+class PHP_Archive
+{
+ const GZ = 0x00001000;
+ const BZ2 = 0x00002000;
+ const SIG = 0x00010000;
+ const SHA1 = 0x0002;
+ const MD5 = 0x0001;
+ /**
+ * Whether this archive is compressed with zlib
+ *
+ * @var bool
+ */
+ private $_compressed;
+ /**
+ * @var string Real path to the .phar archive
+ */
+ private $_archiveName = null;
+ /**
+ * Current file name in the phar
+ * @var string
+ */
+ protected $currentFilename = null;
+ /**
+ * Length of current file in the phar
+ * @var string
+ */
+ protected $internalFileLength = null;
+ /**
+ * Current file statistics (size, creation date, etc.)
+ * @var string
+ */
+ protected $currentStat = null;
+ /**
+ * @var resource|null Pointer to open .phar
+ */
+ protected $fp = null;
+ /**
+ * @var int Current Position of the pointer
+ */
+ protected $position = 0;
+
+ /**
+ * Map actual realpath of phars to meta-data about the phar
+ *
+ * Data is indexed by the alias that is used by internal files. In other
+ * words, if a file is included via:
+ *
+ * require_once 'phar://PEAR.phar/PEAR/Installer.php';
+ *
+ * then the alias is "PEAR.phar"
+ *
+ * Information stored is a boolean indicating whether this .phar is compressed
+ * with zlib, another for bzip2, phar-specific meta-data, and
+ * the precise offset of internal files
+ * within the .phar, used with the {@link $_manifest} to load actual file contents
+ * @var array
+ */
+ private static $_pharMapping = array();
+ /**
+ * Map real file paths to alias used
+ *
+ * @var array
+ */
+ private static $_pharFiles = array();
+ /**
+ * File listing for the .phar
+ *
+ * The manifest is indexed per phar.
+ *
+ * Files within the .phar are indexed by their relative path within the
+ * .phar. Each file has this information in its internal array
+ *
+ * - 0 = uncompressed file size
+ * - 1 = timestamp of when file was added to phar
+ * - 2 = offset of file within phar relative to internal file's start
+ * - 3 = compressed file size (actual size in the phar)
+ * @var array
+ */
+ private static $_manifest = array();
+ /**
+ * Absolute offset of internal files within the .phar, indexed by absolute
+ * path to the .phar
+ *
+ * @var array
+ */
+ private static $_fileStart = array();
+ /**
+ * file name of the phar
+ *
+ * @var string
+ */
+ private $_basename;
+
+
+ /**
+ * Default MIME types used for the web front controller
+ *
+ * @var array
+ */
+ public static $defaultmimes = array(
+ 'aif' => 'audio/x-aiff',
+ 'aiff' => 'audio/x-aiff',
+ 'arc' => 'application/octet-stream',
+ 'arj' => 'application/octet-stream',
+ 'art' => 'image/x-jg',
+ 'asf' => 'video/x-ms-asf',
+ 'asx' => 'video/x-ms-asf',
+ 'avi' => 'video/avi',
+ 'bin' => 'application/octet-stream',
+ 'bm' => 'image/bmp',
+ 'bmp' => 'image/bmp',
+ 'bz2' => 'application/x-bzip2',
+ 'css' => 'text/css',
+ 'doc' => 'application/msword',
+ 'dot' => 'application/msword',
+ 'dv' => 'video/x-dv',
+ 'dvi' => 'application/x-dvi',
+ 'eps' => 'application/postscript',
+ 'exe' => 'application/octet-stream',
+ 'gif' => 'image/gif',
+ 'gz' => 'application/x-gzip',
+ 'gzip' => 'application/x-gzip',
+ 'htm' => 'text/html',
+ 'html' => 'text/html',
+ 'ico' => 'image/x-icon',
+ 'jpe' => 'image/jpeg',
+ 'jpg' => 'image/jpeg',
+ 'jpeg' => 'image/jpeg',
+ 'js' => 'application/x-javascript',
+ 'log' => 'text/plain',
+ 'mid' => 'audio/x-midi',
+ 'mov' => 'video/quicktime',
+ 'mp2' => 'audio/mpeg',
+ 'mp3' => 'audio/mpeg3',
+ 'mpg' => 'audio/mpeg',
+ 'pdf' => 'aplication/pdf',
+ 'png' => 'image/png',
+ 'rtf' => 'application/rtf',
+ 'tif' => 'image/tiff',
+ 'tiff' => 'image/tiff',
+ 'txt' => 'text/plain',
+ 'xml' => 'text/xml',
+ );
+
+ public static $defaultphp = array(
+ 'php' => true
+ );
+
+ public static $defaultphps = array(
+ 'phps' => true
+ );
+
+ public static $deny = array('/.+\.inc$/');
+
+ public static function viewSource($archive, $file)
+ {
+ // security, idea borrowed from PHK
+ if (!file_exists($archive . '.introspect')) {
+ header("HTTP/1.0 404 Not Found");
+ return false;
+ }
+ if (self::_fileExists($archive, $_GET['viewsource'])) {
+ $source = highlight_file('phar://go-pear.phar/' .
+ $_GET['viewsource'], true);
+ header('Content-Type: text/html');
+ header('Content-Length: ' . strlen($source));
+ echo 'Source of ',
+ htmlspecialchars($_GET['viewsource']), ' ';
+ echo 'Source of ',
+ htmlspecialchars($_GET['viewsource']), ' ';
+ if (isset($_GET['introspect'])) {
+ echo 'Return to ', htmlspecialchars($_GET['introspect']), ' ';
+ }
+ echo $source;
+ return false;
+ } else {
+ header("HTTP/1.0 404 Not Found");
+ return false;
+ }
+
+ }
+
+ public static function introspect($archive, $dir)
+ {
+ // security, idea borrowed from PHK
+ if (!file_exists($archive . '.introspect')) {
+ header("HTTP/1.0 404 Not Found");
+ return false;
+ }
+ if (!$dir) {
+ $dir = '/';
+ }
+ $dir = self::processFile($dir);
+ if ($dir[0] != '/') {
+ $dir = '/' . $dir;
+ }
+ try {
+ $self = htmlspecialchars($_SERVER['PHP_SELF']);
+ $iterate = new DirectoryIterator('phar://go-pear.phar' . $dir);
+ echo 'Introspect ', htmlspecialchars($dir),
+ ' Introspect ', htmlspecialchars($dir),
+ ' ';
+ if ($dir != '/') {
+ echo '.. ';
+ }
+ foreach ($iterate as $entry) {
+ if ($entry->isDot()) continue;
+ $name = self::processFile($entry->getPathname());
+ $name = str_replace('phar://go-pear.phar/', '', $name);
+ if ($entry->isDir()) {
+ echo '',
+ htmlspecialchars($entry->getFilename()), '/ [directory] ';
+ } else {
+ echo '',
+ htmlspecialchars($entry->getFilename()), ' ';
+ }
+ }
+ return false;
+ } catch (Exception $e) {
+ echo 'Directory not found: ',
+ htmlspecialchars($dir), ' ',
+ 'Directory not found: ', htmlspecialchars($dir), ' ',
+ 'Try ',
+ 'This link
';
+ return false;
+ }
+ }
+
+ public static function webFrontController($initfile)
+ {
+ if (isset($_SERVER) && isset($_SERVER['REQUEST_URI'])) {
+ $uri = parse_url($_SERVER['REQUEST_URI']);
+ $archive = realpath($_SERVER['SCRIPT_FILENAME']);
+ $subpath = str_replace('/' . basename($archive), '', $uri['path']);
+ if (!$subpath || $subpath == '/') {
+ if (isset($_GET['viewsource'])) {
+ return self::viewSource($archive, $_GET['viewsource']);
+ }
+ if (isset($_GET['introspect'])) {
+ return self::introspect($archive, $_GET['introspect']);
+ }
+ $subpath = '/' . $initfile;
+ }
+ if (!self::_fileExists($archive, substr($subpath, 1))) {
+ header("HTTP/1.0 404 Not Found");
+ return false;
+ }
+ foreach (self::$deny as $pattern) {
+ if (preg_match($pattern, $subpath)) {
+ header("HTTP/1.0 404 Not Found");
+ return false;
+ }
+ }
+ $inf = pathinfo(basename($subpath));
+ if (!isset($inf['extension'])) {
+ header('Content-Type: text/plain');
+ header('Content-Length: ' .
+ self::_filesize($archive, substr($subpath, 1)));
+ readfile('phar://go-pear.phar' . $subpath);
+ return false;
+ }
+ if (isset(self::$defaultphp[$inf['extension']])) {
+ include 'phar://go-pear.phar' . $subpath;
+ return false;
+ }
+ if (isset(self::$defaultmimes[$inf['extension']])) {
+ header('Content-Type: ' . self::$defaultmimes[$inf['extension']]);
+ header('Content-Length: ' .
+ self::_filesize($archive, substr($subpath, 1)));
+ readfile('phar://go-pear.phar' . $subpath);
+ return false;
+ }
+ if (isset(self::$defaultphps[$inf['extension']])) {
+ header('Content-Type: text/html');
+ $c = highlight_file('phar://go-pear.phar' . $subpath, true);
+ header('Content-Length: ' . strlen($c));
+ echo $c;
+ return false;
+ }
+ header('Content-Type: text/plain');
+ header('Content-Length: ' .
+ self::_filesize($archive, substr($subpath, 1)));
+ readfile('phar://go-pear.phar' . $subpath);
+ }
+ }
+
+ /**
+ * Detect end of stub
+ *
+ * @param string $buffer stub past '__HALT_'.'COMPILER();'
+ * @return end of stub, prior to length of manifest.
+ */
+ private static final function _endOfStubLength($buffer)
+ {
+ $pos = 0;
+ if (!strlen($buffer)) {
+ return $pos;
+ }
+ if (($buffer[0] == ' ' || $buffer[0] == "\n") && @substr($buffer, 1, 2) == '')
+ {
+ $pos += 3;
+ if ($buffer[$pos] == "\r" && $buffer[$pos+1] == "\n") {
+ $pos += 2;
+ }
+ else if ($buffer[$pos] == "\n") {
+ $pos += 1;
+ }
+ }
+ return $pos;
+ }
+
+ /**
+ * Allows loading an external Phar archive without include()ing it
+ *
+ * @param string $file phar package to load
+ * @param string $alias alias to use
+ * @throws Exception
+ */
+ public static final function loadPhar($file, $alias = NULL)
+ {
+ $file = realpath($file);
+ if ($file) {
+ $fp = fopen($file, 'rb');
+ $buffer = '';
+ while (!feof($fp)) {
+ $buffer .= fread($fp, 8192);
+ // don't break phars
+ if ($pos = strpos($buffer, '__HALT_COMPI' . 'LER();')) {
+ $buffer .= fread($fp, 5);
+ fclose($fp);
+ $pos += 18;
+ $pos += self::_endOfStubLength(substr($buffer, $pos));
+ return self::_mapPhar($file, $pos, $alias);
+ }
+ }
+ fclose($fp);
+ }
+ }
+
+ /**
+ * Map a full real file path to an alias used to refer to the .phar
+ *
+ * This function can only be called from the initialization of the .phar itself.
+ * Any attempt to call from outside the .phar or to re-alias the .phar will fail
+ * as a security measure.
+ * @param string $alias
+ * @param int $dataoffset the value of 42313
+ */
+ public static final function mapPhar($alias = NULL, $dataoffset = NULL)
+ {
+ try {
+ $trace = debug_backtrace();
+ $file = $trace[0]['file'];
+ // this ensures that this is safe
+ if (!in_array($file, get_included_files())) {
+ die('SECURITY ERROR: PHP_Archive::mapPhar can only be called from within ' .
+ 'the phar that initiates it');
+ }
+ $file = realpath($file);
+ if (!isset($dataoffset)) {
+ $dataoffset = constant('__COMPILER_HALT_OFFSET'.'__');
+ $fp = fopen($file, 'rb');
+ fseek($fp, $dataoffset, SEEK_SET);
+ $dataoffset = $dataoffset + self::_endOfStubLength(fread($fp, 5));
+ fclose($fp);
+ }
+
+ self::_mapPhar($file, $dataoffset);
+ } catch (Exception $e) {
+ die($e->getMessage());
+ }
+ }
+
+ /**
+ * Sub-function, allows recovery from errors
+ *
+ * @param unknown_type $file
+ * @param unknown_type $dataoffset
+ */
+ private static function _mapPhar($file, $dataoffset, $alias = NULL)
+ {
+ $file = realpath($file);
+ if (isset(self::$_manifest[$file])) {
+ return;
+ }
+ if (!is_array(self::$_pharMapping)) {
+ self::$_pharMapping = array();
+ }
+ $fp = fopen($file, 'rb');
+ // seek to __HALT_COMPILER_OFFSET__
+ fseek($fp, $dataoffset);
+ $manifest_length = unpack('Vlen', fread($fp, 4));
+ $manifest = '';
+ $last = '1';
+ while (strlen($last) && strlen($manifest) < $manifest_length['len']) {
+ $read = 8192;
+ if ($manifest_length['len'] - strlen($manifest) < 8192) {
+ $read = $manifest_length['len'] - strlen($manifest);
+ }
+ $last = fread($fp, $read);
+ $manifest .= $last;
+ }
+ if (strlen($manifest) < $manifest_length['len']) {
+ throw new Exception('ERROR: manifest length read was "' .
+ strlen($manifest) .'" should be "' .
+ $manifest_length['len'] . '"');
+ }
+ $info = self::_unserializeManifest($manifest);
+ if ($info['alias']) {
+ $alias = $info['alias'];
+ $explicit = true;
+ } else {
+ if (!isset($alias)) {
+ $alias = $file;
+ }
+ $explicit = false;
+ }
+ self::$_manifest[$file] = $info['manifest'];
+ $compressed = $info['compressed'];
+ self::$_fileStart[$file] = ftell($fp);
+ fclose($fp);
+ if ($compressed & 0x00001000) {
+ if (!function_exists('gzinflate')) {
+ throw new Exception('Error: zlib extension is not enabled - gzinflate() function needed' .
+ ' for compressed .phars');
+ }
+ }
+ if ($compressed & 0x00002000) {
+ if (!function_exists('bzdecompress')) {
+ throw new Exception('Error: bzip2 extension is not enabled - bzdecompress() function needed' .
+ ' for compressed .phars');
+ }
+ }
+ if (isset(self::$_pharMapping[$alias])) {
+ throw new Exception('ERROR: PHP_Archive::mapPhar has already been called for alias "' .
+ $alias . '" cannot re-alias to "' . $file . '"');
+ }
+ self::$_pharMapping[$alias] = array($file, $compressed, $dataoffset, $explicit,
+ $info['metadata']);
+ self::$_pharFiles[$file] = $alias;
+ }
+
+ /**
+ * extract the manifest into an internal array
+ *
+ * @param string $manifest
+ * @return false|array
+ */
+ private static function _unserializeManifest($manifest)
+ {
+ // retrieve the number of files in the manifest
+ $info = unpack('V', substr($manifest, 0, 4));
+ $apiver = substr($manifest, 4, 2);
+ $apiver = bin2hex($apiver);
+ $apiver_dots = hexdec($apiver[0]) . '.' . hexdec($apiver[1]) . '.' . hexdec($apiver[2]);
+ $majorcompat = hexdec($apiver[0]);
+ $calcapi = explode('.', self::APIVersion());
+ if ($calcapi[0] != $majorcompat) {
+ throw new Exception('Phar is incompatible API version ' . $apiver_dots . ', but ' .
+ 'PHP_Archive is API version '.self::APIVersion());
+ }
+ if ($calcapi[0] === '0') {
+ if (self::APIVersion() != $apiver_dots) {
+ throw new Exception('Phar is API version ' . $apiver_dots .
+ ', but PHP_Archive is API version '.self::APIVersion(), E_USER_ERROR);
+ }
+ }
+ $flags = unpack('V', substr($manifest, 6, 4));
+ $ret = array('compressed' => $flags & 0x00003000);
+ // signature is not verified by default in PHP_Archive, phar is better
+ $ret['hassignature'] = $flags & 0x00010000;
+ $aliaslen = unpack('V', substr($manifest, 10, 4));
+ if ($aliaslen) {
+ $ret['alias'] = substr($manifest, 14, $aliaslen[1]);
+ } else {
+ $ret['alias'] = false;
+ }
+ $manifest = substr($manifest, 14 + $aliaslen[1]);
+ $metadatalen = unpack('V', substr($manifest, 0, 4));
+ if ($metadatalen[1]) {
+ $ret['metadata'] = unserialize(substr($manifest, 4, $metadatalen[1]));
+ $manifest = substr($manifest, 4 + $metadatalen[1]);
+ } else {
+ $ret['metadata'] = null;
+ $manifest = substr($manifest, 4);
+ }
+ $offset = 0;
+ $start = 0;
+ for ($i = 0; $i < $info[1]; $i++) {
+ // length of the file name
+ $len = unpack('V', substr($manifest, $start, 4));
+ $start += 4;
+ // file name
+ $savepath = substr($manifest, $start, $len[1]);
+ $start += $len[1];
+ // retrieve manifest data:
+ // 0 = uncompressed file size
+ // 1 = timestamp of when file was added to phar
+ // 2 = compressed filesize
+ // 3 = crc32
+ // 4 = flags
+ // 5 = metadata length
+ $ret['manifest'][$savepath] = array_values(unpack('Va/Vb/Vc/Vd/Ve/Vf', substr($manifest, $start, 24)));
+ $ret['manifest'][$savepath][3] = sprintf('%u', $ret['manifest'][$savepath][3]
+ & 0xffffffff);
+ if ($ret['manifest'][$savepath][5]) {
+ $ret['manifest'][$savepath][6] = unserialize(substr($manifest, $start + 24,
+ $ret['manifest'][$savepath][5]));
+ } else {
+ $ret['manifest'][$savepath][6] = null;
+ }
+ $ret['manifest'][$savepath][7] = $offset;
+ $offset += $ret['manifest'][$savepath][2];
+ $start += 24 + $ret['manifest'][$savepath][5];
+ }
+ return $ret;
+ }
+
+ /**
+ * @param string
+ */
+ private static function processFile($path)
+ {
+ if ($path == '.') {
+ return '';
+ }
+ $std = str_replace("\\", "/", $path);
+ while ($std != ($std = ereg_replace("[^\/:?]+/\.\./", "", $std))) ;
+ $std = str_replace("/./", "", $std);
+ if (strlen($std) > 1 && $std[0] == '/') {
+ $std = substr($std, 1);
+ }
+ if (strncmp($std, "./", 2) == 0) {
+ return substr($std, 2);
+ } else {
+ return $std;
+ }
+ }
+
+ /**
+ * Seek in the master archive to a matching file or directory
+ * @param string
+ */
+ protected function selectFile($path, $allowdirs = true)
+ {
+ $std = self::processFile($path);
+ if (isset(self::$_manifest[$this->_archiveName][$path])) {
+ $this->_setCurrentFile($path);
+ return true;
+ }
+ if (!$allowdirs) {
+ return 'Error: "' . $path . '" is not a file in phar "' . $this->_basename . '"';
+ }
+ foreach (self::$_manifest[$this->_archiveName] as $file => $info) {
+ if (empty($std) ||
+ //$std is a directory
+ strncmp($std.'/', $path, strlen($std)+1) == 0) {
+ $this->currentFilename = $this->internalFileLength = $this->currentStat = null;
+ return true;
+ }
+ }
+ return 'Error: "' . $path . '" not found in phar "' . $this->_basename . '"';
+ }
+
+ private function _setCurrentFile($path)
+ {
+ $this->currentStat = array(
+ 2 => 0100444, // file mode, readable by all, writeable by none
+ 4 => 0, // uid
+ 5 => 0, // gid
+ 7 => self::$_manifest[$this->_archiveName][$path][0], // size
+ 9 => self::$_manifest[$this->_archiveName][$path][1], // creation time
+ );
+ $this->currentFilename = $path;
+ $this->internalFileLength = self::$_manifest[$this->_archiveName][$path][2];
+ // seek to offset of file header within the .phar
+ if (is_resource(@$this->fp)) {
+ fseek($this->fp, self::$_fileStart[$this->_archiveName] + self::$_manifest[$this->_archiveName][$path][7]);
+ }
+ }
+
+ private static function _fileExists($archive, $path)
+ {
+ return isset(self::$_manifest[$archive]) &&
+ isset(self::$_manifest[$archive][$path]);
+ }
+
+ private static function _filesize($archive, $path)
+ {
+ return self::$_manifest[$archive][$path][0];
+ }
+
+ /**
+ * Seek to a file within the master archive, and extract its contents
+ * @param string
+ * @return array|string an array containing an error message string is returned
+ * upon error, otherwise the file contents are returned
+ */
+ public function extractFile($path)
+ {
+ $this->fp = @fopen($this->_archiveName, "rb");
+ if (!$this->fp) {
+ return array('Error: cannot open phar "' . $this->_archiveName . '"');
+ }
+ if (($e = $this->selectFile($path, false)) === true) {
+ $data = '';
+ $count = $this->internalFileLength;
+ while ($count) {
+ if ($count < 8192) {
+ $data .= @fread($this->fp, $count);
+ $count = 0;
+ } else {
+ $count -= 8192;
+ $data .= @fread($this->fp, 8192);
+ }
+ }
+ @fclose($this->fp);
+ if (self::$_manifest[$this->_archiveName][$path][4] & self::GZ) {
+ $data = gzinflate($data);
+ } elseif (self::$_manifest[$this->_archiveName][$path][4] & self::BZ2) {
+ $data = bzdecompress($data);
+ }
+ if (!isset(self::$_manifest[$this->_archiveName][$path]['ok'])) {
+ if (strlen($data) != $this->currentStat[7]) {
+ return array("Not valid internal .phar file (size error {$size} != " .
+ $this->currentStat[7] . ")");
+ }
+ if (self::$_manifest[$this->_archiveName][$path][3] != sprintf("%u", crc32($data) & 0xffffffff)) {
+ return array("Not valid internal .phar file (checksum error)");
+ }
+ self::$_manifest[$this->_archiveName][$path]['ok'] = true;
+ }
+ return $data;
+ } else {
+ @fclose($this->fp);
+ return array($e);
+ }
+ }
+
+ /**
+ * Parse urls like phar:///fullpath/to/my.phar/file.txt
+ *
+ * @param string $file
+ * @return false|array
+ */
+ static protected function parseUrl($file)
+ {
+ if (substr($file, 0, 7) != 'phar://') {
+ return false;
+ }
+ $file = substr($file, 7);
+
+ $ret = array('scheme' => 'phar');
+ $pos_p = strpos($file, '.phar.php');
+ $pos_z = strpos($file, '.phar.gz');
+ $pos_b = strpos($file, '.phar.bz2');
+ if ($pos_p) {
+ if ($pos_z) {
+ return false;
+ }
+ $ret['host'] = substr($file, 0, $pos_p + strlen('.phar.php'));
+ $ret['path'] = substr($file, strlen($ret['host']));
+ } elseif ($pos_z) {
+ $ret['host'] = substr($file, 0, $pos_z + strlen('.phar.gz'));
+ $ret['path'] = substr($file, strlen($ret['host']));
+ } elseif ($pos_b) {
+ $ret['host'] = substr($file, 0, $pos_z + strlen('.phar.bz2'));
+ $ret['path'] = substr($file, strlen($ret['host']));
+ } elseif (($pos_p = strpos($file, ".phar")) !== false) {
+ $ret['host'] = substr($file, 0, $pos_p + strlen('.phar'));
+ $ret['path'] = substr($file, strlen($ret['host']));
+ } else {
+ return false;
+ }
+ if (!$ret['path']) {
+ $ret['path'] = '/';
+ }
+ return $ret;
+ }
+
+ /**
+ * Locate the .phar archive in the include_path and detect the file to open within
+ * the archive.
+ *
+ * Possible parameters are phar://pharname.phar/filename_within_phar.ext
+ * @param string a file within the archive
+ * @return string the filename within the .phar to retrieve
+ */
+ public function initializeStream($file)
+ {
+ $file = self::processFile($file);
+ $info = @parse_url($file);
+ if (!$info) {
+ $info = self::parseUrl($file);
+ }
+ if (!$info) {
+ return false;
+ }
+ if (!isset($info['host'])) {
+ // malformed internal file
+ return false;
+ }
+ if (!isset(self::$_pharFiles[$info['host']]) &&
+ !isset(self::$_pharMapping[$info['host']])) {
+ try {
+ self::loadPhar($info['host']);
+ // use alias from here out
+ $info['host'] = self::$_pharFiles[$info['host']];
+ } catch (Exception $e) {
+ return false;
+ }
+ }
+ if (!isset($info['path'])) {
+ return false;
+ } elseif (strlen($info['path']) > 1) {
+ $info['path'] = substr($info['path'], 1);
+ }
+ if (isset(self::$_pharMapping[$info['host']])) {
+ $this->_basename = $info['host'];
+ $this->_archiveName = self::$_pharMapping[$info['host']][0];
+ $this->_compressed = self::$_pharMapping[$info['host']][1];
+ } elseif (isset(self::$_pharFiles[$info['host']])) {
+ $this->_archiveName = $info['host'];
+ $this->_basename = self::$_pharFiles[$info['host']];
+ $this->_compressed = self::$_pharMapping[$this->_basename][1];
+ }
+ $file = $info['path'];
+ return $file;
+ }
+
+ /**
+ * Open the requested file - PHP streams API
+ *
+ * @param string $file String provided by the Stream wrapper
+ * @access private
+ */
+ public function stream_open($file)
+ {
+ return $this->_streamOpen($file);
+ }
+
+ /**
+ * @param string filename to opne, or directory name
+ * @param bool if true, a directory will be matched, otherwise only files
+ * will be matched
+ * @uses trigger_error()
+ * @return bool success of opening
+ * @access private
+ */
+ private function _streamOpen($file, $searchForDir = false)
+ {
+ $path = $this->initializeStream($file);
+ if (!$path) {
+ trigger_error('Error: Unknown phar in "' . $file . '"', E_USER_ERROR);
+ }
+ if (is_array($this->file = $this->extractFile($path))) {
+ trigger_error($this->file[0], E_USER_ERROR);
+ return false;
+ }
+ if ($path != $this->currentFilename) {
+ if (!$searchForDir) {
+ trigger_error("Cannot open '$file', is a directory", E_USER_ERROR);
+ return false;
+ } else {
+ $this->file = '';
+ return true;
+ }
+ }
+
+ if (!is_null($this->file) && $this->file !== false) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Read the data - PHP streams API
+ *
+ * @param int
+ * @access private
+ */
+ public function stream_read($count)
+ {
+ $ret = substr($this->file, $this->position, $count);
+ $this->position += strlen($ret);
+ return $ret;
+ }
+
+ /**
+ * Whether we've hit the end of the file - PHP streams API
+ * @access private
+ */
+ function stream_eof()
+ {
+ return $this->position >= $this->currentStat[7];
+ }
+
+ /**
+ * For seeking the stream - PHP streams API
+ * @param int
+ * @param SEEK_SET|SEEK_CUR|SEEK_END
+ * @access private
+ */
+ public function stream_seek($pos, $whence)
+ {
+ switch ($whence) {
+ case SEEK_SET:
+ if ($pos < 0) {
+ return false;
+ }
+ $this->position = $pos;
+ break;
+ case SEEK_CUR:
+ if ($pos + $this->currentStat[7] < 0) {
+ return false;
+ }
+ $this->position += $pos;
+ break;
+ case SEEK_END:
+ if ($pos + $this->currentStat[7] < 0) {
+ return false;
+ }
+ $this->position = $pos + $this->currentStat[7];
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * The current position in the stream - PHP streams API
+ * @access private
+ */
+ public function stream_tell()
+ {
+ return $this->position;
+ }
+
+ /**
+ * The result of an fstat call, returns mod time from creation, and file size -
+ * PHP streams API
+ * @uses _stream_stat()
+ * @access private
+ */
+ public function stream_stat()
+ {
+ return $this->_stream_stat();
+ }
+
+ /**
+ * Retrieve statistics on a file or directory within the .phar
+ * @param string file/directory to stat
+ * @access private
+ */
+ public function _stream_stat($file = null)
+ {
+ $std = $file ? self::processFile($file) : $this->currentFilename;
+ if ($file) {
+ if (isset(self::$_manifest[$this->_archiveName][$file])) {
+ $this->_setCurrentFile($file);
+ $isdir = false;
+ } else {
+ do {
+ $isdir = false;
+ if ($file == '/') {
+ break;
+ }
+ foreach (self::$_manifest[$this->_archiveName] as $path => $info) {
+ if (strpos($path, $file) === 0) {
+ if (strlen($path) > strlen($file) &&
+ $path[strlen($file)] == '/') {
+ break 2;
+ }
+ }
+ }
+ // no files exist and no directories match this string
+ return false;
+ } while (false);
+ $isdir = true;
+ }
+ } else {
+ $isdir = false; // open streams must be files
+ }
+ $mode = $isdir ? 0040444 : 0100444;
+ // 040000 = dir, 010000 = file
+ // everything is readable, nothing is writeable
+ return array(
+ 0, 0, $mode, 0, 0, 0, 0, 0, 0, 0, 0, 0, // non-associative indices
+ 'dev' => 0, 'ino' => 0,
+ 'mode' => $mode,
+ 'nlink' => 0, 'uid' => 0, 'gid' => 0, 'rdev' => 0, 'blksize' => 0, 'blocks' => 0,
+ 'size' => $this->currentStat[7],
+ 'atime' => $this->currentStat[9],
+ 'mtime' => $this->currentStat[9],
+ 'ctime' => $this->currentStat[9],
+ );
+ }
+
+ /**
+ * Stat a closed file or directory - PHP streams API
+ * @param string
+ * @param int
+ * @access private
+ */
+ public function url_stat($url, $flags)
+ {
+ $path = $this->initializeStream($url);
+ return $this->_stream_stat($path);
+ }
+
+ /**
+ * Open a directory in the .phar for reading - PHP streams API
+ * @param string directory name
+ * @access private
+ */
+ public function dir_opendir($path)
+ {
+ $info = @parse_url($path);
+ if (!$info) {
+ $info = self::parseUrl($path);
+ if (!$info) {
+ trigger_error('Error: "' . $path . '" is a file, and cannot be opened with opendir',
+ E_USER_ERROR);
+ return false;
+ }
+ }
+ $path = !empty($info['path']) ?
+ $info['host'] . $info['path'] : $info['host'] . '/';
+ $path = $this->initializeStream('phar://' . $path);
+ if (isset(self::$_manifest[$this->_archiveName][$path])) {
+ trigger_error('Error: "' . $path . '" is a file, and cannot be opened with opendir',
+ E_USER_ERROR);
+ return false;
+ }
+ if ($path == false) {
+ trigger_error('Error: Unknown phar in "' . $file . '"', E_USER_ERROR);
+ return false;
+ }
+ $this->fp = @fopen($this->_archiveName, "rb");
+ if (!$this->fp) {
+ trigger_error('Error: cannot open phar "' . $this->_archiveName . '"');
+ return false;
+ }
+ $this->_dirFiles = array();
+ foreach (self::$_manifest[$this->_archiveName] as $file => $info) {
+ if ($path == '/') {
+ if (strpos($file, '/')) {
+ $a = explode('/', $file);
+ $this->_dirFiles[array_shift($a)] = true;
+ } else {
+ $this->_dirFiles[$file] = true;
+ }
+ } elseif (strpos($file, $path) === 0) {
+ $fname = substr($file, strlen($path) + 1);
+ if (strpos($fname, '/')) {
+ // this is a directory
+ $a = explode('/', $fname);
+ $this->_dirFiles[array_shift($a)] = true;
+ } elseif ($file[strlen($path)] == '/') {
+ // this is a file
+ $this->_dirFiles[$fname] = true;
+ }
+ }
+ }
+ @fclose($this->fp);
+ if (!count($this->_dirFiles)) {
+ return false;
+ }
+ @uksort($this->_dirFiles, 'strnatcmp');
+ return true;
+ }
+
+ /**
+ * Read the next directory entry - PHP streams API
+ * @access private
+ */
+ public function dir_readdir()
+ {
+ $ret = key($this->_dirFiles);
+ @next($this->_dirFiles);
+ if (!$ret) {
+ return false;
+ }
+ return $ret;
+ }
+
+ /**
+ * Close a directory handle opened with opendir() - PHP streams API
+ * @access private
+ */
+ public function dir_closedir()
+ {
+ $this->_dirFiles = array();
+ return true;
+ }
+
+ /**
+ * Rewind to the first directory entry - PHP streams API
+ * @access private
+ */
+ public function dir_rewinddir()
+ {
+ @reset($this->_dirFiles);
+ return true;
+ }
+
+ /**
+ * API version of this class
+ * @return string
+ */
+ public static final function APIVersion()
+ {
+ return '1.0.0';
+ }
+
+ /**
+ * Retrieve Phar-specific metadata for a Phar archive
+ *
+ * @param string $phar full path to Phar archive, or alias
+ * @return null|mixed The value that was serialized for the Phar
+ * archive's metadata
+ * @throws Exception
+ */
+ public static function getPharMetadata($phar)
+ {
+ if (isset(self::$_pharFiles[$phar])) {
+ $phar = self::$_pharFiles[$phar];
+ }
+ if (!isset(self::$_pharMapping[$phar])) {
+ throw new Exception('Unknown Phar archive: "' . $phar . '"');
+ }
+ return self::$_pharMapping[$phar][4];
+ }
+
+ /**
+ * Retrieve File-specific metadata for a Phar archive file
+ *
+ * @param string $phar full path to Phar archive, or alias
+ * @param string $file relative path to file within Phar archive
+ * @return null|mixed The value that was serialized for the Phar
+ * archive's metadata
+ * @throws Exception
+ */
+ public static function getFileMetadata($phar, $file)
+ {
+ if (!isset(self::$_pharFiles[$phar])) {
+ if (!isset(self::$_pharMapping[$phar])) {
+ throw new Exception('Unknown Phar archive: "' . $phar . '"');
+ }
+ $phar = self::$_pharMapping[$phar][0];
+ }
+ if (!isset(self::$_manifest[$phar])) {
+ throw new Exception('Unknown Phar: "' . $phar . '"');
+ }
+ $file = self::processFile($file);
+ if (!isset(self::$_manifest[$phar][$file])) {
+ throw new Exception('Unknown file "' . $file . '" within Phar "'. $phar . '"');
+ }
+ return self::$_manifest[$phar][$file][6];
+ }
+
+ /**
+ * @return list of supported signature algorithmns.
+ */
+ public static function getsupportedsignatures()
+ {
+ $ret = array('MD5', 'SHA-1');
+ if (extension_loaded('hash')) {
+ $ret[] = 'SHA-256';
+ $ret[] = 'SHA-512';
+ }
+ return $ret;
+ }
+}}
+if (!class_exists('Phar')) {
+ PHP_Archive::mapPhar(null, 42313 );
+} else {
+ try {
+ Phar::mapPhar();
+ } catch (Exception $e) {
+ echo $e->getMessage();
+ }
+}
+if (class_exists('PHP_Archive') && !in_array('phar', stream_get_wrappers())) {
+ stream_wrapper_register('phar', 'PHP_Archive');
+}
+
+@ini_set('memory_limit', -1);
+if (extension_loaded('phar')) {if (isset($_SERVER) && isset($_SERVER['REQUEST_URI'])) {
+ $uri = parse_url($_SERVER['REQUEST_URI']);
+ $archive = realpath($_SERVER['SCRIPT_FILENAME']);
+ $subpath = str_replace('/' . basename($archive), '', $uri['path']);
+ $mimetypes = array (
+ 'aif' => 'audio/x-aiff',
+ 'aiff' => 'audio/x-aiff',
+ 'arc' => 'application/octet-stream',
+ 'arj' => 'application/octet-stream',
+ 'art' => 'image/x-jg',
+ 'asf' => 'video/x-ms-asf',
+ 'asx' => 'video/x-ms-asf',
+ 'avi' => 'video/avi',
+ 'bin' => 'application/octet-stream',
+ 'bm' => 'image/bmp',
+ 'bmp' => 'image/bmp',
+ 'bz2' => 'application/x-bzip2',
+ 'css' => 'text/css',
+ 'doc' => 'application/msword',
+ 'dot' => 'application/msword',
+ 'dv' => 'video/x-dv',
+ 'dvi' => 'application/x-dvi',
+ 'eps' => 'application/postscript',
+ 'exe' => 'application/octet-stream',
+ 'gif' => 'image/gif',
+ 'gz' => 'application/x-gzip',
+ 'gzip' => 'application/x-gzip',
+ 'htm' => 'text/html',
+ 'html' => 'text/html',
+ 'ico' => 'image/x-icon',
+ 'jpe' => 'image/jpeg',
+ 'jpg' => 'image/jpeg',
+ 'jpeg' => 'image/jpeg',
+ 'js' => 'application/x-javascript',
+ 'log' => 'text/plain',
+ 'mid' => 'audio/x-midi',
+ 'mov' => 'video/quicktime',
+ 'mp2' => 'audio/mpeg',
+ 'mp3' => 'audio/mpeg3',
+ 'mpg' => 'audio/mpeg',
+ 'pdf' => 'aplication/pdf',
+ 'png' => 'image/png',
+ 'rtf' => 'application/rtf',
+ 'tif' => 'image/tiff',
+ 'tiff' => 'image/tiff',
+ 'txt' => 'text/plain',
+ 'xml' => 'text/xml',
+);
+ $phpfiles = array (
+ 'php' => true,
+);
+ $phpsfiles = array (
+ 'phps' => true,
+);
+ $deny = array (
+ 0 => '/.+\\.inc$/',
+);
+ $subpath = str_replace('/' . basename($archive), '', $uri['path']);
+ if (!$subpath || $subpath == '/') {
+ $subpath = '/PEAR.php';
+ }
+ if ($subpath[0] != '/') {
+ $subpath = '/' . $subpath;
+ }
+ if (!@file_exists('phar://' . $archive . $subpath)) {
+ header("HTTP/1.0 404 Not Found");
+ exit;
+ }
+
+ foreach ($deny as $pattern) {
+ if (preg_match($pattern, $subpath)) {
+ header("HTTP/1.0 404 Not Found");
+ exit;
+ }
+ }
+ $inf = pathinfo(basename($subpath));
+ if (!isset($inf['extension'])) {
+ header('Content-Type: text/plain');
+ header('Content-Length: ' . filesize('phar://' . $archive . $subpath));
+ readfile('phar://' . $archive . $subpath);
+ exit;
+ }
+ if (isset($phpfiles[$inf['extension']])) {
+ include 'phar://' . $archive . '/' . $subpath;
+ exit;
+ }
+ if (isset($mimetypes[$inf['extension']])) {
+ header('Content-Type: ' . $mimetypes[$inf['extension']]);
+ header('Content-Length: ' . filesize('phar://' . $archive . $subpath));
+ readfile('phar://' . $archive . $subpath);
+ exit;
+ }
+ if (isset($phpsfiles[$inf['extension']])) {
+ header('Content-Type: text/html');
+ $c = highlight_file('phar://' . $archive . $subpath, true);
+ header('Content-Length: ' . strlen($c));
+ echo $c;
+ exit;
+ }
+ header('Content-Type: text/plain');
+ header('Content-Length: ' . filesize('phar://' . $archive . '/' . $subpath));
+ readfile('phar://' . $archive . '/' . $subpath);
+ exit;
+}} else {if (!empty($_SERVER['REQUEST_URI'])) {PHP_Archive::webFrontController('PEAR.php');exit;}}
+
+
+
+require_once 'phar://go-pear.phar/index.php';
+__HALT_COMPILER();b F go-pear.phar Archive/Tar.php™> )ÄçS™> ©Î.m Console/Getopt.php¶4 )ÄçS¶4 ë5–.m index.php‹ )ÄçS‹ AŹ#m OS/Guess.php‡) )ÄçS‡) cR§m PEAR.phpn„ )ÄçSn„ ěMŘm PEAR/ChannelFile.phpfÇ )ÄçSfÇ ëĽŐôm PEAR/ChannelFile/Parser.phpő )ÄçSő ç5 Mm PEAR/Command.php¶1 )ÄçS¶1 ůŤm PEAR/Command/Common.phpN )ÄçSN Čôm PEAR/Command/Install.phpgČ )ÄçSgČ ¤ň?ům PEAR/Command/Install.xml~! )ÄçS~! 2ˇVm PEAR/Common.phpxe )ÄçSxe WoŘm PEAR/Config.phpŞ )ÄçSŞ şŢ um PEAR/Dependency2.phpšĹ )ÄçSšĹ Tíśm PEAR/DependencyDB.php«^ )ÄçS«^ Řú&Sm PEAR/Downloader.phpD )ÄçSD BęÁm PEAR/Downloader/Package.php%* )ÄçS%* OöCm PEAR/ErrorStack.php\„ )ÄçS\„ Az‡m PEAR/Frontend.php' )ÄçS' ”ś5„m PEAR/Frontend/CLI.php{d )ÄçS{d "cm , PEAR/go-pear-tarballs/Archive_Tar-1.3.12.tar Ä )ÄçS Ä ?Ł