From dac3bffe6b531bef60021e7b06991ec3d35f59bf Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 13 Jan 2018 04:02:26 -0500 Subject: [PATCH 01/28] Updated PortalAuth to 1.6 & CursedScreech to 1.4 (#6) --- CursedScreech/api/module.php | 13 ++++++++----- CursedScreech/includes/changelog/Version 1.4 | 4 ++++ CursedScreech/js/module.js | 13 +++++++++---- CursedScreech/module.info | 2 +- PortalAuth/api/module.php | 20 +++++++++++++------- PortalAuth/includes/changelog/Version 1.5 | 5 +++++ PortalAuth/includes/changelog/Version 1.6 | 6 ++++++ PortalAuth/includes/help/payloads.help | 1 + PortalAuth/js/module.js | 13 +++++++++---- PortalAuth/module.info | 2 +- 10 files changed, 57 insertions(+), 22 deletions(-) create mode 100644 CursedScreech/includes/changelog/Version 1.4 create mode 100644 PortalAuth/includes/changelog/Version 1.5 create mode 100644 PortalAuth/includes/changelog/Version 1.6 diff --git a/CursedScreech/api/module.php b/CursedScreech/api/module.php index 4defeff..17c711b 100755 --- a/CursedScreech/api/module.php +++ b/CursedScreech/api/module.php @@ -37,24 +37,27 @@ if (!empty($_FILES)) { $response = []; foreach ($_FILES as $file) { $tempPath = $file[ 'tmp_name' ]; - $name = $file['name']; + $name = pathinfo($file['name'], PATHINFO_FILENAME); + $type = pathinfo($file['name'], PATHINFO_EXTENSION); // Ensure the upload directory exists if (!file_exists(__PAYLOADS__)) { if (!mkdir(__PAYLOADS__, 0755, true)) { - $response[$name] = "Failed"; + $response[$name]['success'] = "Failed"; + $response[$name]['message'] = "Failed to create payloads directory"; echo json_encode($response); die(); } } - $uploadPath = __PAYLOADS__ . $name; + $uploadPath = __PAYLOADS__ . $name . "." . $type; $res = move_uploaded_file($tempPath, $uploadPath); if ($res) { - $response[$name] = "Success"; + $response[$name]['success'] = "Success"; } else { - $response[$name] = "Failed"; + $response[$name]['success'] = "Failed"; + $response[$name]['message'] = "Failed to upload payload '" . $name . "." . $type . "'"; } } echo json_encode($response); diff --git a/CursedScreech/includes/changelog/Version 1.4 b/CursedScreech/includes/changelog/Version 1.4 new file mode 100644 index 0000000..9fd6887 --- /dev/null +++ b/CursedScreech/includes/changelog/Version 1.4 @@ -0,0 +1,4 @@ +January 12, 2018 +

+ - Updated upload functionality to include better responses if uploads fail
+ \ No newline at end of file diff --git a/CursedScreech/js/module.js b/CursedScreech/js/module.js index 8f20077..1c3c94a 100755 --- a/CursedScreech/js/module.js +++ b/CursedScreech/js/module.js @@ -684,13 +684,18 @@ registerController('CursedScreechController', ['$api', '$scope', '$sce', '$inter transformRequest: angular.identity, headers: {'Content-Type': undefined} }).then(function(response) { - for (var key in response) { - if (response.hasOwnProperty(key)) { - if (response.key == "Failed") { - alert("Failed to upload " + key); + var errors = {}; + for (var key in response.data) { + if (response.data[key].success == "Failed") { + var msg = response.data[key].message + '\n'; + if (!errors.hasOwnProperty(msg)) { + errors[msg] = true; } } } + if (Object.keys(errors).length > 0) { + alert(Object.keys(errors).join('')); + } $scope.selectedFiles = []; $scope.getPayloads(); $scope.uploading = false; diff --git a/CursedScreech/module.info b/CursedScreech/module.info index 54cdc2a..8dcf583 100755 --- a/CursedScreech/module.info +++ b/CursedScreech/module.info @@ -6,5 +6,5 @@ "tetra" ], "title": "CursedScreech", - "version": "1.3" + "version": "1.4" } diff --git a/PortalAuth/api/module.php b/PortalAuth/api/module.php index f1d04a6..440f5d6 100755 --- a/PortalAuth/api/module.php +++ b/PortalAuth/api/module.php @@ -43,13 +43,16 @@ if (!empty($_FILES)) { $response = []; foreach ($_FILES as $file) { $tempPath = $file[ 'tmp_name' ]; - $name = $file['name']; + $name = pathinfo($file['name'], PATHINFO_FILENAME); $type = pathinfo($file['name'], PATHINFO_EXTENSION); switch ($type) { case 'exe': $dest = __WINDL__; break; + case 'bat': + $dest = __WINDL__; + break; case 'zip': $dest = __OSXDL__; break; @@ -63,26 +66,29 @@ if (!empty($_FILES)) { $dest = __INJECTS__; break; default: - break; + $response[$name]['success'] = "Failed"; + $response[$name]['message'] = "File type '" . $type . "' is not supported"; + continue 2; } // Ensure the upload directory exists if (!file_exists($dest)) { if (!mkdir($dest, 0755, true)) { - PortalAuth::logError("Failed Upload", "Failed to upload " . $file['name'] . " because the directory structure could not be created"); + PortalAuth::logError("Failed Upload", "Failed to upload " . $name . "." . $type . " because the directory structure could not be created"); } } - $uploadPath = $dest . $name; + $uploadPath = $dest . $name . "." . $type; $res = move_uploaded_file( $tempPath, $uploadPath ); if ($res) { if ($type == "gz") { - exec(__SCRIPTS__ . "unpackInjectionSet.sh " . $name); + exec(__SCRIPTS__ . "unpackInjectionSet.sh " . $name . "." . $type); } - $response[$name] = "Success"; + $response[$name]['success'] = "Success"; } else { - $response[$name] = "Failed"; + $response[$name]['success'] = "Failed"; + $response[$name]['message'] = "Failed to upload " . $name . "." . $type; } } echo json_encode($response); diff --git a/PortalAuth/includes/changelog/Version 1.5 b/PortalAuth/includes/changelog/Version 1.5 new file mode 100644 index 0000000..d1954cd --- /dev/null +++ b/PortalAuth/includes/changelog/Version 1.5 @@ -0,0 +1,5 @@ +January 5, 2018 +

+ - The module now creates expected directories automatically
+ - Fixed a bug that was introduced with latest version of AngularJS
+ \ No newline at end of file diff --git a/PortalAuth/includes/changelog/Version 1.6 b/PortalAuth/includes/changelog/Version 1.6 new file mode 100644 index 0000000..15c8c12 --- /dev/null +++ b/PortalAuth/includes/changelog/Version 1.6 @@ -0,0 +1,6 @@ +January 12, 2018 +

+ - Added changelog for version 1.5
+ - Added support for .bat files in the payload section
+ - Added a response when unsupported file types are uploaded
+ \ No newline at end of file diff --git a/PortalAuth/includes/help/payloads.help b/PortalAuth/includes/help/payloads.help index 20d1981..6539126 100755 --- a/PortalAuth/includes/help/payloads.help +++ b/PortalAuth/includes/help/payloads.help @@ -9,6 +9,7 @@ uploaded they are automatically stored in the proper location based on their fil

+ + + + +

+ + + + Clear +

+ +

+ + Clear +

+ +

+ + Clear +

+ +

+ + +
+ + + + +
+ + + + +
+
+ +

+ +

+
+
+ + + + + + +
+
+
+ Change Log +
+
+
+
+
    +
  • + 1.0 +
  • +
      +
    • Initial Pineapple Nano Release
    • +
    +
+
+
+
+ + + \ No newline at end of file diff --git a/OpenVPNConnect/module.info b/OpenVPNConnect/module.info new file mode 100644 index 0000000..554bd3f --- /dev/null +++ b/OpenVPNConnect/module.info @@ -0,0 +1,10 @@ +{ + "author": "3ndG4me", + "description": "OpenVPN Connection Utility", + "devices": [ + "nano", + "tetra" + ], + "title": "OpenVPNConnect", + "version": "1.0" +} \ No newline at end of file From 4a8791edbbf4fbc4786bca051910027c73d61f69 Mon Sep 17 00:00:00 2001 From: trashbo4t Date: Tue, 6 Mar 2018 14:04:35 -0500 Subject: [PATCH 06/28] Add log directory creation upon running (#14) * Add log directory creation upon running * Update module.info --- wps/module.info | 4 ++-- wps/scripts/wps.sh | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/wps/module.info b/wps/module.info index e6c3d95..f79eb71 100644 --- a/wps/module.info +++ b/wps/module.info @@ -6,5 +6,5 @@ "tetra" ], "title": "wps", - "version": "1.4" -} \ No newline at end of file + "version": "1.5" +} diff --git a/wps/scripts/wps.sh b/wps/scripts/wps.sh index 60c8bc5..da3c5a3 100755 --- a/wps/scripts/wps.sh +++ b/wps/scripts/wps.sh @@ -6,6 +6,13 @@ export PATH=$PATH:/sd/usr/bin:/sd/usr/sbin MYTIME=`date +%s` MYCMD=`cat /tmp/wps.run` +MODULEDIR=/pineapple/modules/wps +LOGDIR=$MODULEDIR/log + +# ensure log directory is created +if [ ! -d $LOGDIR ]; then + mkdir $LOGDIR +fi if [ "$1" = "start" ]; then eval ${MYCMD} From 5d005e4ba27978cee0ce9040ba3a95bb0b9c41d8 Mon Sep 17 00:00:00 2001 From: trashbo4t Date: Wed, 14 Mar 2018 16:02:47 -0400 Subject: [PATCH 07/28] Using HTTPS for macvendor lookups instead of HTTP (#17) Updates WPS, MACInfo, SiteSurvey, and Status modules. --- MACInfo/api/module.php | 4 ++-- MACInfo/module.info | 2 +- SiteSurvey/api/module.php | 2 +- SiteSurvey/module.info | 4 ++-- Status/api/module.php | 2 +- Status/module.info | 4 ++-- wps/api/module.php | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/MACInfo/api/module.php b/MACInfo/api/module.php index 7b1d56f..52cc8e8 100644 --- a/MACInfo/api/module.php +++ b/MACInfo/api/module.php @@ -17,7 +17,7 @@ class MACInfo extends Module private function getMACInfo($mac) { if($this->IsValidMAC($mac)){ - $url = "http://macvendors.co/api/" . $mac . "/JSON"; + $url = "https://macvendors.co/api/" . $mac . "/JSON"; $retJSON = file_get_contents($url); if($retJSON != false){ $mInfo = json_decode($retJSON); @@ -42,4 +42,4 @@ class MACInfo extends Module $pregResult = preg_match('/([a-fA-F0-9]{2}[:|\-]?){6}/', $mac); return ($pregResult != 0 && $pregResult != NULL); } -} \ No newline at end of file +} diff --git a/MACInfo/module.info b/MACInfo/module.info index ae31d8a..e264905 100644 --- a/MACInfo/module.info +++ b/MACInfo/module.info @@ -6,5 +6,5 @@ "tetra" ], "title": "MAC Info", - "version": "1.2" + "version": "1.3" } diff --git a/SiteSurvey/api/module.php b/SiteSurvey/api/module.php index 82d212f..bfebc96 100644 --- a/SiteSurvey/api/module.php +++ b/SiteSurvey/api/module.php @@ -385,7 +385,7 @@ class SiteSurvey extends Module private function getMACInfo() { - $content = file_get_contents("http://api.macvendors.com/".$this->request->mac); + $content = file_get_contents("https://api.macvendors.com/".$this->request->mac); $this->response = array('title' => $this->request->mac, "output" => $content); } diff --git a/SiteSurvey/module.info b/SiteSurvey/module.info index 79862d7..6b1f6e2 100644 --- a/SiteSurvey/module.info +++ b/SiteSurvey/module.info @@ -6,5 +6,5 @@ "tetra" ], "title": "Site Survey", - "version": "1.2" -} \ No newline at end of file + "version": "1.3" +} diff --git a/Status/api/module.php b/Status/api/module.php index 77505e4..9433efe 100644 --- a/Status/api/module.php +++ b/Status/api/module.php @@ -248,7 +248,7 @@ class Status extends Module private function getMACInfo() { - $content = file_get_contents("http://api.macvendors.com/".$this->request->mac); + $content = file_get_contents("https://api.macvendors.com/".$this->request->mac); $this->response = array('title' => $this->request->mac, "output" => $content); } diff --git a/Status/module.info b/Status/module.info index 4c97a5f..91ca8af 100644 --- a/Status/module.info +++ b/Status/module.info @@ -6,5 +6,5 @@ "tetra" ], "title": "Status", - "version": "1.1" -} \ No newline at end of file + "version": "1.2" +} diff --git a/wps/api/module.php b/wps/api/module.php index 96e226d..cde485a 100644 --- a/wps/api/module.php +++ b/wps/api/module.php @@ -349,7 +349,7 @@ class wps extends Module private function getMACInfo() { - $content = file_get_contents("http://api.macvendors.com/".$this->request->mac); + $content = file_get_contents("https://api.macvendors.com/".$this->request->mac); $this->response = array('title' => $this->request->mac, "output" => $content); } From 59e27d303e8ddd99897ee03a4d6e59e77920634d Mon Sep 17 00:00:00 2001 From: trashbo4t Date: Wed, 14 Mar 2018 16:13:01 -0400 Subject: [PATCH 08/28] Ensure the SSLsplit log directory exists (#18) --- SSLsplit/module.info | 4 ++-- SSLsplit/scripts/autostart_sslsplit.sh | 4 ++++ SSLsplit/scripts/sslsplit.sh | 4 ++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/SSLsplit/module.info b/SSLsplit/module.info index 4b8fd7d..6b52227 100644 --- a/SSLsplit/module.info +++ b/SSLsplit/module.info @@ -6,5 +6,5 @@ "tetra" ], "title": "SSLsplit", - "version": "1.1" -} \ No newline at end of file + "version": "1.2" +} diff --git a/SSLsplit/scripts/autostart_sslsplit.sh b/SSLsplit/scripts/autostart_sslsplit.sh index cf1b8bb..9ad8a2d 100755 --- a/SSLsplit/scripts/autostart_sslsplit.sh +++ b/SSLsplit/scripts/autostart_sslsplit.sh @@ -21,4 +21,8 @@ sh /pineapple/modules/SSLsplit/rules/iptables iptables -t nat -A POSTROUTING -j MASQUERADE +if [ ! -d /pineapple/modules/SSLsplit/log ]; then + mkdir /pineapple/modules/SSLsplit/log +fi + sslsplit -D -l /pineapple/modules/SSLsplit/connections.log -L /pineapple/modules/SSLsplit/log/output_${MYTIME}.log -k /pineapple/modules/SSLsplit/cert/certificate.key -c /pineapple/modules/SSLsplit/cert/certificate.crt ssl 0.0.0.0 8443 tcp 0.0.0.0 8080 diff --git a/SSLsplit/scripts/sslsplit.sh b/SSLsplit/scripts/sslsplit.sh index a009bce..ea1d70d 100755 --- a/SSLsplit/scripts/sslsplit.sh +++ b/SSLsplit/scripts/sslsplit.sh @@ -8,6 +8,10 @@ MYTIME=`date +%s` killall sslsplit +if [ ! -d /pineapple/modules/SSLsplit/log ]; then + mkdir /pineapple/modules/SSLsplit/log +fi + if [ "$1" = "start" ]; then echo '1' > /proc/sys/net/ipv4/ip_forward From 65664a98cfaf5729af485a8c82f5f837d45e783e Mon Sep 17 00:00:00 2001 From: Catatonic Date: Mon, 2 Apr 2018 23:32:48 -0700 Subject: [PATCH 09/28] Create CONTRIBUTING.md Establishing Contribution expectations. --- CONTRIBUTING.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..9f66816 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,11 @@ +Contributing +============ + +To ease code reviews please follow the [PSR-1](http://www.php-fig.org/psr/1/) and +[PSR-2](http://www.php-fig.org/psr/2/). Take a few minutes to actually read them, +they're not all that bad. To jump right in use the [PHP-CS-Fixer +tool](http://cs.sensiolabs.org/). Or install PHP_CodeSniffer and use it like so: + +`apt install php-codesniffer` + +`phpcs --standard=PSR2 your_file.php` From c704837917e9b5d5027fd727d51b760ec33c5a7f Mon Sep 17 00:00:00 2001 From: Catatonic Date: Fri, 6 Apr 2018 17:37:36 -0700 Subject: [PATCH 10/28] Adding Tor module (#21) --- tor/api/module.php | 302 ++++++++++++++++++++++++++++++++++++ tor/files/torrc | 193 +++++++++++++++++++++++ tor/js/module.js | 156 +++++++++++++++++++ tor/module.html | 158 +++++++++++++++++++ tor/module.info | 7 + tor/scripts/dependencies.sh | 29 ++++ 6 files changed, 845 insertions(+) create mode 100755 tor/api/module.php create mode 100644 tor/files/torrc create mode 100755 tor/js/module.js create mode 100755 tor/module.html create mode 100755 tor/module.info create mode 100755 tor/scripts/dependencies.sh diff --git a/tor/api/module.php b/tor/api/module.php new file mode 100755 index 0000000..f3a010d --- /dev/null +++ b/tor/api/module.php @@ -0,0 +1,302 @@ +request->action) { + case 'refreshInfo': + $this->refreshInfo(); + break; + case 'refreshStatus': + $this->refreshStatus(); + break; + case 'handleDependencies': + $this->handleDependencies(); + break; + case 'handleDependenciesStatus': + $this->handleDependenciesStatus(); + break; + case 'toggletor': + $this->toggletor(); + break; + case 'refreshHiddenServices': + $this->refreshHiddenServices(); + break; + case 'addHiddenService': + $this->addHiddenService(); + break; + case 'removeHiddenService': + $this->removeHiddenService(); + break; + case 'addServiceForward': + $this->addServiceForward(); + break; + case 'removeServiceForward': + $this->removeServiceForward(); + break; + default: + break; + } + } + + private function success($value) + { + $this->response = array('success' => $value); + } + + private function error($message) + { + $this->response = array('error' => $message); + } + + private function isValidName($name) + { + return preg_match('/^[a-zA-Z0-9_]+$/', $name) === 1; + } + + private function isValidPort($port) + { + return preg_match('/^[0-9]+$/', $port) === 1; + } + + private function isValidRedirectTo($redirect_to) + { + return preg_match('/^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+$/', $redirect_to) === 1; + } + + protected function checkDependency($dependencyName) + { + return (exec("which {$dependencyName}") != '' && !file_exists($this->progressFile)); + } + + protected function refreshInfo() + { + $moduleInfo = @json_decode(file_get_contents("/pineapple/modules/tor/module.info")); + $this->response = array('title' => $moduleInfo->title, 'version' => $moduleInfo->version); + } + + protected function checkRunning($processName) + { + return (exec("pgrep '{$processName}$'") != ''); + } + + private function handleDependencies() + { + $destination = ""; + if (isset($this->request->destination)) { + $destination = $this->request->destination; + if ($destination != "internal" && $destination != "sd") { + $this->error(self::INVALID_DESTINATION); + return; + } + } + + if (!$this->checkDependency("tor")) { + $this->execBackground($this->dependenciesScriptFile . " install " . $destination); + } else { + $this->execBackground($this->dependenciesScriptFile . " remove"); + } + $this->success(true); + } + + private function handleDependenciesStatus() + { + if (file_exists($this->progressFile)) { + $this->success(false); + } else { + $this->success(true); + } + } + + private function toggletor() + { + if ($this->checkRunning("tor")) { + exec("/etc/init.d/tor stop"); + } else { + exec("/etc/init.d/tor start"); + } + } + + private function refreshStatus() + { + + $device = $this->getDevice(); + $sdAvailable = $this->isSDAvailable(); + $installed = false; + $install = "Not Installed"; + $processing = false; + + if (file_exists($this->progressFile)) { + // TOR Is installing, please wait. + $install = "Installing..."; + $installLabel = self::WARNING; + $processing = true; + + $status = "Not running"; + $statusLabel = self::DANGER; + } elseif (!$this->checkDependency("tor")) { + // TOR is not installed, please install. + $installLabel = self::DANGER; + + $status = "Start"; + $statusLabel = self::DANGER; + } else { + // TOR is installed, please configure. + $installed = true; + $install = "Installed"; + $installLabel = self::SUCCESS; + + if ($this->checkRunning("tor")) { + $status = "Started"; + $statusLabel = self::SUCCESS; + } else { + $status = "Stopped"; + $statusLabel = self::DANGER; + } + } + + $this->response = array("device" => $device, + "sdAvailable" => $sdAvailable, + "status" => $status, + "statusLabel" => $statusLabel, + "installed" => $installed, + "install" => $install, + "installLabel" => $installLabel, + "processing" => $processing); + } + + private function generateConfig() + { + $output = file_get_contents("/etc/config/tor/torrc"); + $output .= "\n"; + $hiddenServices = @json_decode(file_get_contents($this->moduleConfigFile)); + foreach ($hiddenServices as $hiddenService) { + $output .= "HiddenServiceDir /etc/config/tor/services/{$hiddenService->name}\n"; + $forwards = $hiddenService->forwards; + foreach ($forwards as $forward) { + $output .= "HiddenServicePort {$forward->port} {$forward->redirect_to}\n"; + } + } + file_put_contents("/etc/tor/torrc", $output); + } + + private function reloadTor() + { + $this->generateConfig(); + //Sending SIGHUP to tor process cause config reload. + exec("pkill -sighup tor$"); + } + + private function refreshHiddenServices() + { + $hiddenServices = @json_decode(file_get_contents($this->moduleConfigFile)); + foreach ($hiddenServices as $hiddenService) { + if (file_exists("/etc/config/tor/services/{$hiddenService->name}/hostname")) { + $hostname = file_get_contents("/etc/config/tor/services/{$hiddenService->name}/hostname"); + $hiddenService->hostname = trim($hostname); + } + } + $this->response = array("hiddenServices" => $hiddenServices); + } + + private function addHiddenService() + { + $name = $this->request->name; + if (!$this->isValidName($name)) { + $this->error(self::INVALID_NAME); + return; + } + + $hiddenService = array("name" => $name, "forwards" => array() ); + $hiddenServices = array(); + if (file_exists($this->moduleConfigFile)) { + $hiddenServices = @json_decode(file_get_contents($this->moduleConfigFile)); + } + array_push($hiddenServices, $hiddenService); + file_put_contents($this->moduleConfigFile, @json_encode($hiddenServices, JSON_PRETTY_PRINT)); + $this->reloadTor(); + } + + private function removeHiddenService() + { + $hiddenServices = @json_decode(file_get_contents($this->moduleConfigFile)); + foreach ($hiddenServices as $key => $hiddenService) { + if ($hiddenService->name == $this->request->name) { + unset($hiddenServices[$key]); + } + } + file_put_contents($this->moduleConfigFile, @json_encode($hiddenServices, JSON_PRETTY_PRINT)); + $this->reloadTor(); + } + + private function addServiceForward() + { + $name = $this->request->name; + $port = $this->request->port; + $redirect_to = $this->request->redirect_to; + + if (!$this->isValidName($name)) { + $this->error(self::INVALID_NAME); + return; + } + if (!$this->isValidPort($port)) { + $this->error(self::INVALID_PORT); + return; + } + if (!$this->isValidRedirectTo($redirect_to)) { + $this->error(self::INVALID_DESTINATION); + return; + } + + $hiddenServices = @json_decode(file_get_contents($this->moduleConfigFile)); + foreach ($hiddenServices as $key => $hiddenService) { + if ($hiddenService->name == $name) { + $forwards = $hiddenService->forwards; + $forward = array("port" => $port, "redirect_to" => $redirect_to); + array_push($forwards, $forward); + $hiddenServices[$key]->forwards = $forwards; + } + } + file_put_contents($this->moduleConfigFile, @json_encode($hiddenServices, JSON_PRETTY_PRINT)); + + $this->reloadTor(); + } + + private function removeServiceForward() + { + $name = $this->request->name; + $port = $this->request->port; + $redirect_to = $this->request->redirect_to; + + $hiddenServices = @json_decode(file_get_contents($this->moduleConfigFile)); + foreach ($hiddenServices as $hiddenService) { + if ($hiddenService->name == $name) { + $forwards = $hiddenService->forwards; + foreach ($forwards as $key => $forward) { + if ($forward->port == $port && $forward->redirect_to == $redirect_to) { + unset($forwards[$key]); + } + } + $hiddenService->forwards = $forwards; + } + } + file_put_contents($this->moduleConfigFile, @json_encode($hiddenServices, JSON_PRETTY_PRINT)); + + $this->reloadTor(); + } +} diff --git a/tor/files/torrc b/tor/files/torrc new file mode 100644 index 0000000..b2d4de6 --- /dev/null +++ b/tor/files/torrc @@ -0,0 +1,193 @@ +## Configuration file for a typical Tor user +## Last updated 9 October 2013 for Tor 0.2.5.2-alpha. +## (may or may not work for much older or much newer versions of Tor.) +## +## Lines that begin with "## " try to explain what's going on. Lines +## that begin with just "#" are disabled commands: you can enable them +## by removing the "#" symbol. +## +## See 'man tor', or https://www.torproject.org/docs/tor-manual.html, +## for more options you can use in this file. +## +## Tor will look for this file in various places based on your platform: +## https://www.torproject.org/docs/faq#torrc + +## Tor opens a socks proxy on port 9050 by default -- even if you don't +## configure one below. Set "SocksPort 0" if you plan to run Tor only +## as a relay, and not make any local application connections yourself. +#SocksPort 9050 # Default: Bind to localhost:9050 for local connections. +#SocksPort 192.168.0.1:9100 # Bind to this address:port too. + +## Entry policies to allow/deny SOCKS requests based on IP address. +## First entry that matches wins. If no SocksPolicy is set, we accept +## all (and only) requests that reach a SocksPort. Untrusted users who +## can access your SocksPort may be able to learn about the connections +## you make. +#SocksPolicy accept 192.168.0.0/16 +#SocksPolicy reject * + +## Logs go to stdout at level "notice" unless redirected by something +## else, like one of the below lines. You can have as many Log lines as +## you want. +## +## We advise using "notice" in most cases, since anything more verbose +## may provide sensitive information to an attacker who obtains the logs. +## +## Send all messages of level 'notice' or higher to /var/log/tor/notices.log +#Log notice file /var/log/tor/notices.log +## Send every possible message to /var/log/tor/debug.log +#Log debug file /var/log/tor/debug.log +## Use the system log instead of Tor's logfiles +#Log notice syslog +## To send all messages to stderr: +#Log debug stderr + +## Uncomment this to start the process in the background... or use +## --runasdaemon 1 on the command line. This is ignored on Windows; +## see the FAQ entry if you want Tor to run as an NT service. +RunAsDaemon 1 + +## The directory for keeping all the keys/etc. By default, we store +## things in $HOME/.tor on Unix, and in Application Data\tor on Windows. +DataDirectory /var/lib/tor + +## The port on which Tor will listen for local connections from Tor +## controller applications, as documented in control-spec.txt. +#ControlPort 9051 +## If you enable the controlport, be sure to enable one of these +## authentication methods, to prevent attackers from accessing it. +#HashedControlPassword 16:872860B76453A77D60CA2BB8C1A7042072093276A3D701AD684053EC4C +#CookieAuthentication 1 + +############### This section is just for location-hidden services ### + +## Once you have configured a hidden service, you can look at the +## contents of the file ".../hidden_service/hostname" for the address +## to tell people. +## +## HiddenServicePort x y:z says to redirect requests on port x to the +## address y:z. + +#HiddenServiceDir /var/lib/tor/hidden_service/ +#HiddenServicePort 80 127.0.0.1:80 + +#HiddenServiceDir /var/lib/tor/other_hidden_service/ +#HiddenServicePort 80 127.0.0.1:80 +#HiddenServicePort 22 127.0.0.1:22 + +################ This section is just for relays ##################### +# +## See https://www.torproject.org/docs/tor-doc-relay for details. + +## Required: what port to advertise for incoming Tor connections. +#ORPort 9001 +## If you want to listen on a port other than the one advertised in +## ORPort (e.g. to advertise 443 but bind to 9090), you can do it as +## follows. You'll need to do ipchains or other port forwarding +## yourself to make this work. +#ORPort 443 NoListen +#ORPort 127.0.0.1:9090 NoAdvertise + +## The IP address or full DNS name for incoming connections to your +## relay. Leave commented out and Tor will guess. +#Address noname.example.com + +## If you have multiple network interfaces, you can specify one for +## outgoing traffic to use. +# OutboundBindAddress 10.0.0.5 + +## A handle for your relay, so people don't have to refer to it by key. +#Nickname ididnteditheconfig + +## Define these to limit how much relayed traffic you will allow. Your +## own traffic is still unthrottled. Note that RelayBandwidthRate must +## be at least 20 KB. +## Note that units for these config options are bytes per second, not bits +## per second, and that prefixes are binary prefixes, i.e. 2^10, 2^20, etc. +#RelayBandwidthRate 100 KB # Throttle traffic to 100KB/s (800Kbps) +#RelayBandwidthBurst 200 KB # But allow bursts up to 200KB/s (1600Kbps) + +## Use these to restrict the maximum traffic per day, week, or month. +## Note that this threshold applies separately to sent and received bytes, +## not to their sum: setting "4 GB" may allow up to 8 GB total before +## hibernating. +## +## Set a maximum of 4 gigabytes each way per period. +#AccountingMax 4 GB +## Each period starts daily at midnight (AccountingMax is per day) +#AccountingStart day 00:00 +## Each period starts on the 3rd of the month at 15:00 (AccountingMax +## is per month) +#AccountingStart month 3 15:00 + +## Administrative contact information for this relay or bridge. This line +## can be used to contact you if your relay or bridge is misconfigured or +## something else goes wrong. Note that we archive and publish all +## descriptors containing these lines and that Google indexes them, so +## spammers might also collect them. You may want to obscure the fact that +## it's an email address and/or generate a new address for this purpose. +#ContactInfo Random Person +## You might also include your PGP or GPG fingerprint if you have one: +#ContactInfo 0xFFFFFFFF Random Person + +## Uncomment this to mirror directory information for others. Please do +## if you have enough bandwidth. +#DirPort 9030 # what port to advertise for directory connections +## If you want to listen on a port other than the one advertised in +## DirPort (e.g. to advertise 80 but bind to 9091), you can do it as +## follows. below too. You'll need to do ipchains or other port +## forwarding yourself to make this work. +#DirPort 80 NoListen +#DirPort 127.0.0.1:9091 NoAdvertise +## Uncomment to return an arbitrary blob of html on your DirPort. Now you +## can explain what Tor is if anybody wonders why your IP address is +## contacting them. See contrib/tor-exit-notice.html in Tor's source +## distribution for a sample. +#DirPortFrontPage /etc/tor/tor-exit-notice.html + +## Uncomment this if you run more than one Tor relay, and add the identity +## key fingerprint of each Tor relay you control, even if they're on +## different networks. You declare it here so Tor clients can avoid +## using more than one of your relays in a single circuit. See +## https://www.torproject.org/docs/faq#MultipleRelays +## However, you should never include a bridge's fingerprint here, as it would +## break its concealability and potentionally reveal its IP/TCP address. +#MyFamily $keyid,$keyid,... + +## A comma-separated list of exit policies. They're considered first +## to last, and the first match wins. If you want to _replace_ +## the default exit policy, end this with either a reject *:* or an +## accept *:*. Otherwise, you're _augmenting_ (prepending to) the +## default exit policy. Leave commented to just use the default, which is +## described in the man page or at +## https://www.torproject.org/documentation.html +## +## Look at https://www.torproject.org/faq-abuse.html#TypicalAbuses +## for issues you might encounter if you use the default exit policy. +## +## If certain IPs and ports are blocked externally, e.g. by your firewall, +## you should update your exit policy to reflect this -- otherwise Tor +## users will be told that those destinations are down. +## +## For security, by default Tor rejects connections to private (local) +## networks, including to your public IP address. See the man page entry +## for ExitPolicyRejectPrivate if you want to allow "exit enclaving". +## +#ExitPolicy accept *:6660-6667,reject *:* # allow irc ports but no more +#ExitPolicy accept *:119 # accept nntp as well as default exit policy +#ExitPolicy reject *:* # no exits allowed + +## Bridge relays (or "bridges") are Tor relays that aren't listed in the +## main directory. Since there is no complete public list of them, even an +## ISP that filters connections to all the known Tor relays probably +## won't be able to block all the bridges. Also, websites won't treat you +## differently because they won't know you're running Tor. If you can +## be a real relay, please do; but if not, be a bridge! +#BridgeRelay 1 +## By default, Tor will advertise your bridge to users through various +## mechanisms like https://bridges.torproject.org/. If you want to run +## a private bridge, for example because you'll give out your bridge +## address manually to your friends, uncomment this line: +#PublishServerDescriptor 0 + +User tor diff --git a/tor/js/module.js b/tor/js/module.js new file mode 100755 index 0000000..4b46001 --- /dev/null +++ b/tor/js/module.js @@ -0,0 +1,156 @@ +registerController('tor_DependenciesController', ['$api', '$scope', '$rootScope', '$interval', '$timeout', function($api, $scope, $rootScope, $interval, $timeout) { + $scope.status = "Loading..."; + $scope.statusLabel = "default"; + $scope.starting = false; + + $scope.install = "Loading..."; + $scope.installLabel = "default"; + $scope.processing = false; + + $scope.saveSettingsLabel = "default"; + + $scope.device = ''; + $scope.sdAvailable = false; + + $rootScope.status = { + installed : false + }; + + $scope.refreshStatus = (function() { + $api.request({ + module: "tor", + action: "refreshStatus" + }, function(response) { + $scope.status = response.status; + $scope.statusLabel = response.statusLabel; + + $rootScope.status.installed = response.installed; + $scope.device = response.device; + $scope.sdAvailable = response.sdAvailable; + if(response.processing) $scope.processing = true; + $scope.install = response.install; + $scope.installLabel = response.installLabel; + }) + }); + + $scope.toggletor = (function() { + if($scope.status != "Stop") + $scope.status = "Starting..."; + else + $scope.status = "Stopping..."; + + $scope.statusLabel = "warning"; + $scope.starting = true; + + $rootScope.status.refreshOutput = false; + $rootScope.status.refreshHistory = false; + + $api.request({ + module: 'tor', + action: 'toggletor' + }, function(response) { + $timeout(function(){ + $scope.starting = false; + $scope.refreshStatus(); + }, 2000); + }) + }); + + $scope.handleDependencies = (function(param) { + if(!$rootScope.status.installed) + $scope.install = "Installing..."; + else + $scope.install = "Removing..."; + + $api.request({ + module: 'tor', + action: 'handleDependencies', + destination: param + }, function(response){ + if (response.success === true) { + $scope.installLabel = "warning"; + $scope.processing = true; + + $scope.handleDependenciesInterval = $interval(function(){ + $api.request({ + module: 'tor', + action: 'handleDependenciesStatus' + }, function(response) { + if (response.success === true){ + $scope.processing = false; + $interval.cancel($scope.handleDependenciesInterval); + $scope.refreshStatus(); + } + }); + }, 5000); + } + }); + }); + $scope.refreshStatus(); +}]); + +registerController('tor_ConfigurationController', ['$api', '$scope', '$rootScope', '$interval', '$timeout', function($api, $scope, $rootScope, $interval, $timeout) { + + $scope.refreshHiddenServices = (function() { + $api.request({ + module: 'tor', + action: 'refreshHiddenServices' + }, function(response) { + $scope.hiddenServices = response.hiddenServices; + }); + }); + + $scope.addHiddenService = (function() { + $api.request({ + module: 'tor', + action: 'addHiddenService', + name: $scope.name + }, function(response){ + $scope.refreshHiddenServices(); + }); + }); + + $scope.removeHiddenService = (function(name) { + $api.request({ + module: 'tor', + action: 'removeHiddenService', + name: name + }, function(response) { + $scope.refreshHiddenServices(); + }); + }); + + $scope.addServiceForward = (function() { + $api.request({ + module: 'tor', + action: 'addServiceForward', + name: $scope.name, + port: $scope.port, + redirect_to: $scope.redirect_to + }, function(response) { + $scope.hiddenServicesLoad = '(reloading...)'; + $timeout(function() { + $scope.hiddenServicesLoad = ''; + $scope.refreshHiddenServices(); + }, 2000); + }); + }); + + $scope.removeServiceForward = (function(name, port, redirect_to) { + $api.request({ + module: 'tor', + action: 'removeServiceForward', + name: name, + port: port, + redirect_to: redirect_to + }, function(response) { + $scope.hiddenServicesLoad = '(reloading...)'; + $timeout(function() { + $scope.hiddenServicesLoad = ''; + $scope.refreshHiddenServices(); + }, 2000); + }); + }); + + $scope.refreshHiddenServices(); +}]); diff --git a/tor/module.html b/tor/module.html new file mode 100755 index 0000000..2ce0d20 --- /dev/null +++ b/tor/module.html @@ -0,0 +1,158 @@ +
+
+
+
+

Dependencies

+
+
+ + + + + + + + + + +
Dependencies + + + +
tor + +
+
+ + + + +
+
+
+
+
+
+
+

Hidden Services {{ hiddenServicesLoad }}

+ {{ config }} +
+ +
+ + +
+

+ {{ service.name }} + ({{service.hostname}}) + + + +

+
+ + + + + + + + + +
PortRedirect to
{{ forward.port }}{{ forward.redirect_to }}
+
+
+
+ + + +
+
diff --git a/tor/module.info b/tor/module.info new file mode 100755 index 0000000..ccac56e --- /dev/null +++ b/tor/module.info @@ -0,0 +1,7 @@ +{ + "author":"catatonicprime", + "description":"Connect device to tor network, manage hidden services, etc.", + "devices": [ "nano", "tetra" ], + "title":"tor", + "version":"1.0" +} diff --git a/tor/scripts/dependencies.sh b/tor/scripts/dependencies.sh new file mode 100755 index 0000000..e2bd6fc --- /dev/null +++ b/tor/scripts/dependencies.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# author: catatonicprime +# date: March 2018 + +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/sd/lib:/sd/usr/lib +export PATH=$PATH:/sd/usr/bin:/sd/usr/sbin + +touch /tmp/tor.progress + +if [ "$1" = "install" ]; then + opkg update + if [ "$2" = "internal" ]; then + opkg install tor-geoip tor + elif [ "$2" = "sd" ]; then + opkg install tor-geoip tor --dest sd + fi + mkdir -p /etc/config/tor/ + cp /pineapple/modules/tor/files/torrc /etc/config/tor + mkdir -p /etc/config/tor/services + chown tor:tor /etc/config/tor/services + chown root:tor /etc/tor/torrc + chmod g+r /etc/tor/torrc +elif [ "$1" = "remove" ]; then + opkg remove tor-geoip tor + sed -i '/tor\/scripts\/autostart_tor.sh/d' /etc/rc.local + rm -rf /etc/config/tor +fi + +rm /tmp/tor.progress From c336fdfbea7e674edfedfbb26ebd1768033484bf Mon Sep 17 00:00:00 2001 From: Catatonic Date: Thu, 26 Apr 2018 16:56:57 -0700 Subject: [PATCH 11/28] Update CONTRIBUTING.md correcting link destinations & including https --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9f66816..2fb333f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,8 +1,8 @@ Contributing ============ -To ease code reviews please follow the [PSR-1](http://www.php-fig.org/psr/1/) and -[PSR-2](http://www.php-fig.org/psr/2/). Take a few minutes to actually read them, +To ease code reviews please follow the [PSR-1](https://www.php-fig.org/psr/psr-1/) and +[PSR-2](https://www.php-fig.org/psr/psr-2/). Take a few minutes to actually read them, they're not all that bad. To jump right in use the [PHP-CS-Fixer tool](http://cs.sensiolabs.org/). Or install PHP_CodeSniffer and use it like so: From df16e3399bc7fa9209fff210162dacc08b009cda Mon Sep 17 00:00:00 2001 From: Marc Date: Mon, 7 May 2018 19:27:28 +0100 Subject: [PATCH 12/28] README: Fix formatting mistake. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 09b4445..2770c83 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ private function hello() { $this->response = array('text' => "Hello World"); } -```. +``` **Note:** You should never use the closing `?>` PHP tag in your `module.php` file. From 6ea36775428f1b1df949735b952fda0d5708bcdf Mon Sep 17 00:00:00 2001 From: trashbo4t Date: Sun, 13 May 2018 17:05:07 -0400 Subject: [PATCH 13/28] Fixes issue 24 --- OnlineHashCrack/module.info | 2 +- OnlineHashCrack/scripts/submit_hash.sh | 2 +- OnlineHashCrack/scripts/submit_wpa.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/OnlineHashCrack/module.info b/OnlineHashCrack/module.info index a77d0ac..83bc744 100644 --- a/OnlineHashCrack/module.info +++ b/OnlineHashCrack/module.info @@ -6,5 +6,5 @@ "tetra" ], "title": "Online Hash Crack", - "version": "1.2" + "version": "1.3" } diff --git a/OnlineHashCrack/scripts/submit_hash.sh b/OnlineHashCrack/scripts/submit_hash.sh index 0949573..47db042 100755 --- a/OnlineHashCrack/scripts/submit_hash.sh +++ b/OnlineHashCrack/scripts/submit_hash.sh @@ -22,7 +22,7 @@ if [ -n "$EMAIL" ]; then echo -e "" >> ${LOG} - curl -s -v -d emailHashes="${EMAIL}" -d textareaHashes="${HASH}" http://www.onlinehashcrack.com/hash-cracking.php > /dev/null 2>> ${LOG} + curl -s -v -d emailHashes="${EMAIL}" -d textareaHashes="${HASH}" https://www.onlinehashcrack.com/hash-cracking.php > /dev/null 2>> ${LOG} else echo -e "Notification email not set in settings." > ${LOG} fi diff --git a/OnlineHashCrack/scripts/submit_wpa.sh b/OnlineHashCrack/scripts/submit_wpa.sh index db4b280..907d265 100755 --- a/OnlineHashCrack/scripts/submit_wpa.sh +++ b/OnlineHashCrack/scripts/submit_wpa.sh @@ -25,7 +25,7 @@ if [ -n "$EMAIL" ]; then echo -e "" >> ${LOG} - curl -s -v -F submit="Submit" -F emailWpa="${EMAIL}" -F wpaFile=@${FILE} http://www.onlinehashcrack.com/wifi-wpa-rsna-psk-crack.php > /dev/null 2>> ${LOG} + curl -s -v -F submit="Submit" -F emailWpa="${EMAIL}" -F wpaFile=@${FILE} https://www.onlinehashcrack.com/wifi-wpa-rsna-psk-crack.php > /dev/null 2>> ${LOG} fi else From 724eecf8b73ccbaa14b9bbf366fe8cc755415622 Mon Sep 17 00:00:00 2001 From: trashbo4t Date: Sun, 20 May 2018 01:59:44 -0400 Subject: [PATCH 14/28] New Module: Themes --- Themes/api/module.php | 565 ++++++++++++++++++++++++++ Themes/css/1980.css | 452 +++++++++++++++++++++ Themes/css/default.css | 203 ++++++++++ Themes/css/hak5.css | 452 +++++++++++++++++++++ Themes/css/kbeflo-dark.css | 529 +++++++++++++++++++++++++ Themes/css/main.css | 203 ++++++++++ Themes/css/malware.css | 478 ++++++++++++++++++++++ Themes/css/morning.css | 464 ++++++++++++++++++++++ Themes/css/neon.css | 457 +++++++++++++++++++++ Themes/css/spring.css | 479 ++++++++++++++++++++++ Themes/css/starstuff.css | 478 ++++++++++++++++++++++ Themes/img/favicon-dark.ico | Bin 0 -> 533 bytes Themes/img/logo-dark.png | Bin 0 -> 26507 bytes Themes/img/throbber-dark.gif | Bin 0 -> 1739 bytes Themes/js/module.js | 558 ++++++++++++++++++++++++++ Themes/module.html | 744 +++++++++++++++++++++++++++++++++++ Themes/module.info | 6 + Themes/module_icon.svg | 52 +++ 18 files changed, 6120 insertions(+) create mode 100644 Themes/api/module.php create mode 100644 Themes/css/1980.css create mode 100644 Themes/css/default.css create mode 100644 Themes/css/hak5.css create mode 100644 Themes/css/kbeflo-dark.css create mode 100644 Themes/css/main.css create mode 100644 Themes/css/malware.css create mode 100644 Themes/css/morning.css create mode 100644 Themes/css/neon.css create mode 100644 Themes/css/spring.css create mode 100644 Themes/css/starstuff.css create mode 100644 Themes/img/favicon-dark.ico create mode 100644 Themes/img/logo-dark.png create mode 100644 Themes/img/throbber-dark.gif create mode 100644 Themes/js/module.js create mode 100644 Themes/module.html create mode 100644 Themes/module.info create mode 100644 Themes/module_icon.svg diff --git a/Themes/api/module.php b/Themes/api/module.php new file mode 100644 index 0000000..a0ee183 --- /dev/null +++ b/Themes/api/module.php @@ -0,0 +1,565 @@ + "FF0000", + "green" => "00FF00", + "blue" => "0000FF", + "purple" => "800080", + "orange" => "cc3300", + "yellow" => "ffff00", + "pink" => "ff0066", + ); + private $LIGHT_COLOR_HEX_MAP = array( + "red" => "ff4d4d", + "green" => "80ff80", + "blue" => "8080ff", + "purple" => "ff66ff", + "orange" => "ff9f80", + "yellow" => "ffff66", + "pink" => "ff99c2", + ); + private $DARK_COLOR_HEX_MAP = array( + "red" => "990000", + "green" => "004d00", + "blue" => "000077", + "purple" => "4d004d", + "orange" => "992600", + "yellow" => "cccc00", + "pink" => "99003d", + ); + + public function route() + { + switch ($this->request->action) { + case 'getThemeList': + $this->handleGetThemeList(); + break; + case 'themeFields': + $this->getThemeFields(); + break; + case 'deleteTheme': + $this->handleDeleteTheme(); + break; + case 'activateTheme': + $this->activateTheme(); + break; + case 'getThemeCode': + $this->getThemeCode(); + break; + case 'submitThemeCode': + $this->submitThemeCode(); + break; + case 'getCurrentTheme': + $this->getCurrentTheme(); + break; + case 'createNewTheme': + $this->handleCreateNewTheme(); + break; + case 'restoreDefault': + $this->restoreDefault(); + break; + case 'backupFiles': + $this->backupFiles(); + break; + case 'replaceImage': + $this->replaceImage(); + break; + } + } + // Get the CURRENT_ file, which is 1 line with the current color of the icon + public function currentFile($name) + { + $upper = strtoupper($name); + return "/pineapple/modules/Themes/img/CURRENT_{$upper}"; + } + // Move an image from light->dark or vice versa + public function replaceImage() + { + $img = $this->request->img; + switch ($img) + { + // Pineapple Logo + case 'Logo': + $this->response = array("message" => "Logo Changed"); + if ($this->request->light) { + exec("cp $this->BACKUP_LOGO /pineapple/img/logo.png"); + exec("echo light > $this->CURRENT_LOGO"); + } + else + { + exec("echo dark > $this->CURRENT_LOGO"); + exec('cp /pineapple/modules/Themes/img/logo-dark.png /pineapple/img/logo.png'); + } + $this->response = array("message" => "Logo Changed"); + break; + + // Pineapple favicon.ico Image + case 'Icon': + if ($this->request->light) { + exec("echo light > $this->CURRENT_FAVICON"); + exec("cp $this->BACKUP_FAVICON /pineapple/img/favicon.ico"); + } + else + { + exec("echo dark > $this->CURRENT_FAVICON"); + exec('cp /pineapple/modules/Themes/img/favicon-dark.ico /pineapple/img/favicon.ico'); + } + $this->response = array("message" => "Icon Changed"); + break; + + // Pineapple Throbber gif + case 'Throbber': + if ($this->request->light) { + exec("echo light > $this->CURRENT_THROBBER"); + exec("cp $this->BACKUP_THROBBER /pineapple/img/throbber.gif"); + } + else + { + exec("echo dark > $this->CURRENT_THROBBER"); + exec('cp /pineapple/modules/Themes/img/throbber-dark.gif /pineapple/img/throbber.gif'); + } + $this->response = array("message" => "Throbber Changed"); + break; + + // Modify all of the module Icons + case 'All': + foreach ($this->ALL_MODULES as $module) + { + $current = $this->currentFile($module); + $success = $this->replaceModuleImage( + $module, + $this->request->color, + $this->request->brightness + ); + } + $this->response = array( + "success" => true, + "message" => "All module icons changed to {$this->request->color}-{$this->request->brightness}" + ); + break; + // Assume module Icon + default: + $success = $this->replaceModuleImage( + $this->request->img, + $this->request->color, + $this->request->brightness + ); + $this->response = array( + "success" => $success, + "message" => "{$this->request->img} icon changed to {$this->request->color}-{$this->request->brightness}" + ); + break; + } + } + /* + * replaceModuleImage + * $moduleName -> String name of module, can be any format (nEtWoRkIng) because it gets formatted + * $color -> string name of the color, used for index of mapping + * $brightness -> string name of brightness, used for map selection + */ + public function replaceModuleImage($moduleName, $color, $brightness) + { + $current = $this->currentFile($moduleName); + $replace = "/pineapple/modules/{$moduleName}/module_icon.svg"; + switch($color) + { + case 'light': + return $this->restoreModuleIcon ( + $moduleName + ); + break; + case 'dark': + if (exec("echo dark > $current") != 0 || + exec("echo $brightness >> $current") != 0 || + !$this->searchAndReplaceFile($replace, "FFFFFF")) + { + return false; + } + break; + default: + $hex = ""; + switch($brightness) + { + case 'light': + $hex = $this->LIGHT_COLOR_HEX_MAP[$color]; + break; + case 'dark': + $hex = $this->DARK_COLOR_HEX_MAP[$color]; + break; + default: + $hex = $this->NORMAL_COLOR_HEX_MAP[$color]; + break; + } + // Replace the modules icon image + if (exec("echo $color > $current") != 0 || + exec("echo $brightness >> $current") != 0) + { + return false; + } + if (!$this->searchAndReplaceFile($replace, $hex)) { + return false; + } + break; + } + return true; + } + /* + * searchAndReplaceFile + * $s -> substring to find + * return: true or false showing succcessful string replacement + */ + public function searchAndReplaceFile($f, $s) + { + // Use a stream editor so we dont have to load the entire file into RAM + return (exec("sed -i 's/fill:\(.*\);/fill:#{$s};/g' $f") == 0); + } + /* + * setCurrentTheme + * $theme -> modify CURRENT_CSS file with new theme + */ + public function setCurrentTheme($theme) + { + $this->current_theme = $theme; + exec('echo '.$theme.' > /pineapple/modules/Themes/css/CURRENT_CSS'); + } + /* + * getCurrentTheme + * return current theme, and all parameters for icon colors/brightness + */ + public function getCurrentTheme() + { + $line = file('/pineapple/modules/Themes/css/CURRENT_CSS')[0]; + $line = trim(preg_replace('/\s+/', ' ', $line)); + + $logo = file('/pineapple/modules/Themes/img/CURRENT_LOGO')[0]; + $logo = trim(preg_replace('/\s+/', ' ', $logo)); + + $icon = file('/pineapple/modules/Themes/img/CURRENT_FAVICON')[0]; + $icon = trim(preg_replace('/\s+/', ' ', $icon)); + + $throbber = file('/pineapple/modules/Themes/img/CURRENT_THROBBER')[0]; + $throbber = trim(preg_replace('/\s+/', ' ', $throbber)); + $this->response = array( + "current" => $line, + "logo" => $logo, + "icon" => $icon, + "throbber" => $throbber, + ); + foreach ($this->ALL_MODULES as $module) + { + $current = $this->currentFile($module); + $lower = strtolower($module); + $color = file($current)[0]; + $color = trim(preg_replace('/\s+/', ' ', $color)); + $brightness = file($current)[1]; + $brightness = trim(preg_replace('/\s+/', ' ', $brightness)); + $this->response[$lower] = $color; + $this->response[$lower.'brightness'] = $brightness; + } + } + /* + * isCurrentThemeEnv + * $theme string name of theme to check if its current + * check if global current_them var is set, compare against that + * this way we dont open,read,close a file every for every check + */ + public function isCurrentThemeEnv($theme) + { + if ($this->current_theme != "") { + return ($this->current_theme == $theme); + } + if (!file_exists($this->CURRENT_CSS)) { + return false; + } + $line = file($this->CURRENT_CSS)[0]; + $line = trim(preg_replace('/\s+/', ' ', $line)); + return ($line === $theme); + } + /* + * restoreImages + * Undo any changes made by this Module + * This includes: original icons, gifs, svg's + */ + public function restoreImages() + { + $success = true; + exec("cp {$this->BACKUP_FAVICON} /pineapple/img/favicon.ico"); + exec("cp {$this->BACKUP_LOGO} /pineapple/img/logo.png"); + exec("cp {$this->BACKUP_THROBBER} /pineapple/img/throbber.gif"); + exec('echo light > /pineapple/modules/Themes/img/CURRENT_LOGO'); + exec('echo light > /pineapple/modules/Themes/img/CURRENT_FAVICON'); + exec('echo light > /pineapple/modules/Themes/img/CURRENT_THROBBER'); + + foreach ($this->ALL_MODULES as $module) + { + $current = $this->currentFile($module); + $success = $this->restoreModuleIcon ( + $module + ); + } + $this->response = array( + "success" => $success, + "message" => "Restored all files" + ); + } + /* + * restoreModuleIcon + * Generic helper function to put a modules icon back to normal + * using only the name of the module (in any format). + */ + public function restoreModuleIcon($moduleName) + { + $current = $this->currentFile($moduleName); + $replace = "/pineapple/modules/{$moduleName}/module_icon.svg"; + if (!$this->searchAndReplaceFile($replace, $this->RESTORE_HEX)) + { + return false; + } + if (exec("echo light > $current") != 0 || + exec("echo normal >> $current") != 0) + { + return false; + } + return true; + } + /* + * restoreDefault + * backup all files if not done yet + * put the original css file back + * restore all of the images + */ + public function restoreDefault() + { + $this->backupFiles(); + exec("cp {$this->BACKUP_MAIN_CSS} /pineapple/css/main.css"); + $this->setCurrentTheme('main.css'); + $this->restoreImages(); + } + /* + * getThemeCode + * retrieve the css styling code from a theme file + */ + public function getThemeCode() + { + $code = file_get_contents($this->CSS_DIR . $this->request->name); + $this->response = array("code" => $code, "file" => $this->CSS_DIR . $this->request->name); + } + /* + * getThemeFields + * more or less only returns the code for now + */ + public function getThemeFields() + { + $allFields = array(); + $code = file_get_contents($this->CSS_DIR . $this->request->name); + $this->response = array("code" => $code); + } + /* + * activateTheme + * mv the users selected theme to main.css file + */ + public function activateTheme() + { + $themeName = $this->request->name; + $cmd = exec("cp {$this->CSS_DIR}{$themeName} /pineapple/css/main.css"); + if ($cmd == 0) { + $this->setCurrentTheme($themeName); + $message = $themeName . " is now active."; + $this->response = array("return" => true, "message" => $message); + } + else + { + $message = "Could not move theme" . $themeName . "(Something is wrong..)"; + $this->response = array("return" => false,"message" => $message); + } + } + /* Credits to SteveRusin at http://php.net/manual/en/ref.strings.php */ + private function endsWith($str, $sub) + { + return (substr($str, strlen($str) - strlen($sub)) === $sub); + } + /* + * handleDeleteTheme + * delete a users theme file from the local css directory + */ + public function handleDeleteTheme() + { + $themeName = $this->request->name; + exec("rm {$this->CSS_DIR}{$themeName}"); + if (!file_exists("/pineapple/modules/Themes/css/" . $themeName)) { + $message = "Deleted " . $themeName; + } else { + $message = "Error deleting " . $themeName; + } + $this->response = array("message" => $message); + } + /* + * submitThemeCode + * save a users theme file in the local css directory + */ + public function submitThemeCode() + { + $code = $this->request->themeCode; + $themeName = $this->request->name; + $fileName = $this->request->fileName; + file_put_contents($this->CSS_DIR . $themeName, $code); + $message = (!file_exists($this->CSS_DIR . $themeName)) ? "Created " . $themeName : "Updated " . $themeName; + + $this->response = array( + "message" => $message, + "filename" => $fileName + ); + } + /* + * handleGetThemeList + * get the list of .css files in the local css directory + * avoid sending back the main.css file so it cannot be modified + */ + public function handleGetThemeList() + { + $all_themes = array(); + $root_themes = preg_grep('/^([^.])/', scandir("{$this->CSS_DIR}")); + foreach ($root_themes as $theme) { + if (!is_file($theme) && $this->endsWith($theme, '.css') && $theme != "main.css") { + $active = $this->isCurrentThemeEnv($theme); + $obj = array("title" => $theme, "location" => "../Themes/css/", "active" => $active); + array_push($all_themes, $obj); + } + } + $this->response = $all_themes; + } + /* + * handleCreateNewTheme + * create a new .css theme file in the local css directory + */ + public function handleCreateNewTheme() + { + $themePath = $this->CSS_DIR; + $themeName = str_replace(' ', '_', $this->request->themeName); + if (!$this->endswith($themeName, '.css')) { + $themeName = $themeName . ".css"; + } + if (file_exists($themePath . $themeName)) { + $this->response = array("create_success" => false, "create_message" => "A theme named {$themeName} already exists."); + return; + } + exec("cp {$this->SKELETON_CSS} {$themePath}{$themeName}"); + $this->response = array("create_success" => true, "create_message" => "Created {$themeName}"); + } + /* + * backupFiles + * Backup all of the .css/IMG files used so the module can properly restore defaults + */ + public function backupFiles() + { + $success = true; + $modules = array(); + if (!file_exists($this->BACKUP_MAIN_CSS)) { + exec("cp /pineapple/css/main.css {$this->BACKUP_MAIN_CSS}"); + array_push($modules, "Backed up main.css."); + } + if (!file_exists($this->SKELETON_CSS)) { + mkdir($this->CSS_DIR); + exec("cp {$this->BACKUP_MAIN_CSS} {$this->SKELETON_CSS}"); + array_push($modules, "Backed up skeleton.css."); + } + if (!file_exists($this->BACKUP_THROBBER)) { + exec("cp /pineapple/img/throbber.gif {$this->BACKUP_THROBBER}"); + array_push($modules, "Backed up favicon.ico"); + } + if (!file_exists($this->CURRENT_THROBBER)) { + exec("echo light > $this->CURRENT_THROBBER"); + array_push($modules, "Wrote to {$this->CURRENT_THROBBER}"); + } + if (!file_exists($this->BACKUP_FAVICON)) { + exec("cp /pineapple/img/favicon.ico {$this->BACKUP_FAVICON}"); + array_push($modules, "Backed up favicon.ico"); + } + if (!file_exists($this->CURRENT_FAVICON)) { + exec("echo light > $this->CURRENT_FAVICON"); + array_push($modules, "Wrote to /pineapple/modules/Themes/img/CURRENT_FAVICON"); + } + if (!file_exists($this->BACKUP_LOGO)) { + exec("cp /pineapple/img/logo.png $this->BACKUP_LOGO"); + array_push($modules, "Wrote to {$this->BACKUP_LOGO}"); + } + if (!file_exists($this->CURRENT_LOGO)) { + exec("echo light > $this->CURRENT_LOGO"); + array_push($modules, "Wrote to {$this->CURRENT_LOGO}"); + } + foreach ($this->ALL_MODULES as $module) + { + $current = $this->currentFile($module); + if (!$this->backupModuleIcon($current)) + { + array_push($modules, "Did not write to {$current}."); + } + else + { + array_push($modules, "Wrote to {$current}."); + } + } + $this->response = array( + "success" => $success, + "message" => $success ? + "Created a backup file for all files" : + "Failed to backup files! Tread lightly", + "modules" => $modules + ); + } + public function backupModuleIcon($currentFile) { + if (!file_exists($currentFile)) { + if (exec("echo light > $currentFile") != 0 || + exec("echo normal >> $currentFile") != 0) + { + return false; + } + return true; + } + return false; + } +} \ No newline at end of file diff --git a/Themes/css/1980.css b/Themes/css/1980.css new file mode 100644 index 0000000..896db2c --- /dev/null +++ b/Themes/css/1980.css @@ -0,0 +1,452 @@ +.truncated { + text-overflow: ellipsis; + overflow: hidden +} + +a { + color:lime; + font-family: monospace; +} + +i { + color: lime; + font: monospace; +} + +b, strong { + font-weight: 700; + font-family: monospace; +} + +.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { + font-family: monospace; + font-weight: 500; + line-height: 1.1; + color: inherit; +} + +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: 400; + line-height: 1; + color: lime; + text-align: center; + background-color: black; + border: 1px solid lime; + border-radius: 4px; +} + +.panel-default { + border-color: lime; +} + +.panel>.panel-body+.table, .panel>.panel-body+.table-responsive, .panel>.table+.panel-body, .panel>.table-responsive+.panel-body { + border-top: 1px solid lime; +} + +.dropdown-menu>li>a { + display:block; + padding: 3px 20px; + clear: both; + font-weight: 400; + line-height: 1.42857143; + color: lime; + white-space:nowrap; + background-color: black; + border: lime; +} + +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: lime; + background-color: black; + background-image: none; + border: 1px solid lime; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; +} + +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-bottom: 1px solid lime; + border-top: 1px solid lime; +} + +table { + background-color: black; +} + +.table-responsive { + min-height: .01%; + overflow-x: auto; + background-color: black; +} + +* { + color: lime; + border-color: lime; + border-top: lime; +} +.uppercase { + text-transform: uppercase; +} + +.table-layout-fixed { + table-layout: fixed; +} + +.module-icon { + display: inline; + height: 24px; + width: 24px; +} + +.fixed-addon-width { + min-width: 70px; + text-align: left; +} + +.fixed-addon-width-2 { + min-width: 90px; + text-align: left; +} + + +.fixed-addon-width-3 { + min-width: 110px; + text-align: left; +} + +.fixed-width-200 { + min-width: 200px; +} + +.caret-reversed { + border-top-width: 0; + border-bottom: 4px solid #000; +} + +.image-small-18 { + height: 18px; +} + +.center-text { + text-align: center; +} + +.scrollable-pre { + overflow: auto; + word-wrap: normal; + white-space: pre; +} + +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: lime; + word-break: break-all; + word-wrap: break-word; + background-color: #000000; + border: 1px solid lime; + border-radius: 4px; + font-family: monospace; +} + +.log-pre { + max-height: 300px; +} + +.btn-fixed-length { + width: 70px; +} + +.title-message { + margin-left: 10px; + padding-left: 5px; + padding-right: 5px; + height: 9px; + border-radius: 3px; +} + +.padding-left { + margin-left: 10px; +} + +.select-inline { + font-weight: normal; +} + +body { + background-color: #000000; +} + +.logout { + cursor: pointer; +} + +.module-nav li a { + margin-left: 30px; +} + +.module-nav li:hover { + background-color: #000; +} + +.nav>li>a:focus, .nav>li>a:hover { + text-decoration: none; + background-color: limegreen; +} +.sidebar .sidebar-nav.navbar-collapse { + padding-right: 0; + padding-left: 0; +} + + +.sidebar ul li { + cursor: pointer; + border-bottom: 1px solid #18ff00; + color:lime; +} + +.btn { + background-color: black; + font-family: monospace; +} + +.alert-info { + color: lime; + background-color: black; + border-color: lime; +} + +p { + color: lime; + font-family: monospace; +} + +.text-muted { + color:forestgreen; +} + +.h3 h3 { + color:lime; +} +.h2, h2 { + font-size: 30px; + color:lime; +} +.h1, h1 { + font-size: 30px; + color:lime; +} + +.btn-default { + color:lime; + border-color: lime; +} + +.btn-default:hover { + color: lime; + background-color: lime; + border-color: lime; +} + +.sidebar .active { + background-color: #13033a; +} + +@media(min-width:768px) { + .sidebar { + z-index: 1; + position: absolute; + width: 250px; + margin-top: 51px; + } + + .module-content { + position: inherit; + margin: 0 0 0 250px; + padding: 15px 30px; + border-left: 1px solid #000000; + } + + .navbar-top-links { + margin-left: 10px; + } +} + +.navbar-top-links { + margin-right: 5px; + margin-left: 10px; +} + +.navbar-top-links li { + display: inline-block; +} + +.form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { + background-color: #000; + opacity: 1; + color: lime; +} + +.panel-footer { + padding: 10px 15px; + background-color: #000000; + border-top: 1px solid #0F0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} + +textarea.form-control { + height: auto; + background-color: #000; + opacity: 1; + color: lime; + font-family: monospace; +} +.navbar { + margin-bottom: 0; +} + +.navbar-top-links .dropdown-menu li { + display: block; +} + +.module-content { + padding: 15px 15px; + background-color: #000; +} + +.pointer { + cursor: pointer; +} + +.dropdown-menu { + cursor: pointer; +} + +.dropdown-menu-top { + max-height: 300px !important; + overflow-y: auto !important; +} + +@media(max-width:768px) { + .dropdown-menu-top { + margin: auto !important; + position: absolute !important; + background-color: #000 !important; + word-wrap: break-word !important; + border: 1px solid #000 !important; + width: 300px !important; + max-width: 300px !important; + } + .dropdown-menu-top li a { + white-space: normal !important; + } + + .dropdown-menu-logout { + max-width: 50px !important; + } +} + +.login-logo { + margin: 0 auto; + padding-bottom: 10px; + max-width: 150px; +} + +.brand-logo { + content: url('/img/logo.png'); + max-height: 30px; + padding-bottom: 5px; +} + +.brand-text::after { + content: "pineapple"; + padding-top: 3px; + padding-left: 5px; + float: right; + color:lime; +} + +.alert-danger { + color: lime; + background-color: black; + border-color: lime; +} + +.panel-title { + background-color:black; + color: lime; +} + +.panel-default>.panel-heading+.panel-collapse>.panel-body { + border-top-color: lime; +} + +.panel-body { + background-color:black; + font-family:monospace; +} + +.panel-heading { + background-color:black; +} + +.panel-default>.panel-heading { + color: lime; + background-color: black; + border-color: lime; + border-bottom-color: lime; + border-bottom: lime; +} + +td { + background-color:black; +} +.nav { + background-color:black; +} +.sidebar-nav.navbar-collapse { + background-color:black; +} +.navbar-default{ + background-color:black; +} +.navbar-static-top { + background-color:black; + border-color: lime; +} +.input-group .form-control:first-child, .input-group-addon:first-child, .input-group-btn:first-child>.btn, .input-group-btn:first-child>.btn-group>.btn, .input-group-btn:first-child>.dropdown-toggle, .input-group-btn:last-child>.btn-group:not(:last-child)>.btn, .input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle) { + font-family: monospace; + background-color: lime; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + color: black; +} +.table>thead>tr>th { + vertical-align: bottom; + border-bottom: 2px solid lime; +} +.table>tbody>tr>td, .table>tbody>tr>th, .table>tfoot>tr>td, .table>tfoot>tr>th, .table>thead>tr>td, .table>thead>tr>th { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid lime; +} \ No newline at end of file diff --git a/Themes/css/default.css b/Themes/css/default.css new file mode 100644 index 0000000..9c42935 --- /dev/null +++ b/Themes/css/default.css @@ -0,0 +1,203 @@ +.truncated { + text-overflow: ellipsis; + overflow: hidden +} + +.uppercase { + text-transform: uppercase; +} + +.table-layout-fixed { + table-layout: fixed; +} + +.module-icon { + display: inline; + height: 24px; + width: 24px; +} + +.fixed-addon-width { + min-width: 70px; + text-align: left; +} + +.fixed-addon-width-2 { + min-width: 90px; + text-align: left; +} + +.fixed-addon-width-3 { + min-width: 110px; + text-align: left; +} + +.fixed-width-200 { + min-width: 200px; +} + +.caret-reversed { + border-top-width: 0; + border-bottom: 4px solid #000; +} + +.image-small-18 { + height: 18px; +} + +.center-text { + text-align: center; +} + +.scrollable-pre { + overflow: auto; + word-wrap: normal; + white-space: pre; +} + +.log-pre { + max-height: 300px; +} + +.btn-fixed-length { + width: 70px; +} + +.title-message { + margin-left: 10px; + padding-left: 5px; + padding-right: 5px; + height: 9px; + border-radius: 3px; +} + +.padding-left { + margin-left: 10px; +} + +.select-inline { + font-weight: normal; +} + +body { + background-color: #f8f8f8; +} + +.logout { + cursor: pointer; +} + +.module-nav li a { + margin-left: 30px; +} + +.module-nav li:hover { + background-color: #eee; +} + +.sidebar .sidebar-nav.navbar-collapse { + padding-right: 0; + padding-left: 0; +} + +.sidebar ul li { + cursor: pointer; + border-bottom: 1px solid #e7e7e7; +} + +.sidebar .active { + background-color: #eee; +} + +@media(min-width:768px) { + .sidebar { + z-index: 1; + position: absolute; + width: 250px; + margin-top: 51px; + } + + .module-content { + position: inherit; + margin: 0 0 0 250px; + padding: 15px 30px; + border-left: 1px solid #e7e7e7; + } + + .navbar-top-links { + margin-left: 10px; + } +} + +.navbar-top-links { + margin-right: 5px; + margin-left: 10px; +} + +.navbar-top-links li { + display: inline-block; +} + +.navbar { + margin-bottom: 0; +} + +.navbar-top-links .dropdown-menu li { + display: block; +} + +.module-content { + padding: 15px 15px; + background-color: #fff; +} + +.pointer { + cursor: pointer; +} + +.dropdown-menu { + cursor: pointer; +} + +.dropdown-menu-top { + max-height: 300px !important; + overflow-y: auto !important; +} + +@media(max-width:768px) { + .dropdown-menu-top { + margin: auto !important; + position: absolute !important; + background-color: #fff !important; + word-wrap: break-word !important; + border: 1px solid #ccc !important; + width: 300px !important; + max-width: 300px !important; + } + .dropdown-menu-top li a { + white-space: normal !important; + } + + .dropdown-menu-logout { + max-width: 50px !important; + } +} + +.login-logo { + margin: 0 auto; + padding-bottom: 10px; + max-width: 150px; +} + +.brand-logo { + content: url('/img/logo.png'); + max-height: 30px; + padding-bottom: 5px; +} + +.brand-text::after { + content: "WiFi Pineapple"; + padding-top: 3px; + padding-left: 5px; + float: right; +} \ No newline at end of file diff --git a/Themes/css/hak5.css b/Themes/css/hak5.css new file mode 100644 index 0000000..8b4bfe3 --- /dev/null +++ b/Themes/css/hak5.css @@ -0,0 +1,452 @@ +.truncated { + text-overflow: ellipsis; + overflow: hidden +} + +a { + color:blue; + font-family:webkit-pictograph; +} + +i { + color: blue; + font: webkit-pictograph; +} + +b, strong { + font-weight: 700; + font-family: webkit-pictograph; +} + +.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { + font-family: webkit-pictograph; + font-weight: 500; + line-height: 1.1; + color: blue; +} + +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: 400; + line-height: 1; + color: red; + text-align: center; + background-color: white; + border: 1px solid red; + border-radius: 4px; +} + +.panel-default { + border-color: red; +} + +.panel>.panel-body+.table, .panel>.panel-body+.table-responsive, .panel>.table+.panel-body, .panel>.table-responsive+.panel-body { + border-top: 1px solid red; +} + +.dropdown-menu>li>a { + display:block; + padding: 3px 20px; + clear: both; + font-weight: 400; + line-height: 1.42857143; + color: blue; + white-space:nowrap; + background-color: white; + border: red; +} + +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: red; + background-color: white; + background-image: none; + border: 1px solid red; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; +} + +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-bottom: 1px solid red; + border-top: 1px solid red; +} + +table { + background-color: white; +} + +.table-responsive { + min-height: .01%; + overflow-x: auto; + background-color: white; +} + +* { + color: red; + border-color: red; + border-top: red; +} +.uppercase { + text-transform: uppercase; +} + +.table-layout-fixed { + table-layout: fixed; +} + +.module-icon { + display: inline; + height: 24px; + width: 24px; +} + +.fixed-addon-width { + min-width: 70px; + text-align: left; +} + +.fixed-addon-width-2 { + min-width: 90px; + text-align: left; +} + + +.fixed-addon-width-3 { + min-width: 110px; + text-align: left; +} + +.fixed-width-200 { + min-width: 200px; +} + +.caret-reversed { + border-top-width: 0; + border-bottom: 4px solid white; +} + +.image-small-18 { + height: 18px; +} + +.center-text { + text-align: center; +} + +.scrollable-pre { + overflow: auto; + word-wrap: normal; + white-space: pre; +} + +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: red; + word-break: break-all; + word-wrap: break-word; + background-color: white; + border: 1px solid red; + border-radius: 4px; + font-family: webkit-pictograph; +} + +.log-pre { + max-height: 300px; +} + +.btn-fixed-length { + width: 70px; +} + +.title-message { + margin-left: 10px; + padding-left: 5px; + padding-right: 5px; + height: 9px; + border-radius: 3px; +} + +.padding-left { + margin-left: 10px; +} + +.select-inline { + font-weight: normal; +} + +body { + background-color: white; +} + +.logout { + cursor: pointer; +} + +.module-nav li a { + margin-left: 30px; +} + +.module-nav li:hover { + background-color: white; +} + +.nav>li>a:focus, .nav>li>a:hover { + text-decoration: none; + background-color: red; +} +.sidebar .sidebar-nav.navbar-collapse { + padding-right: 0; + padding-left: 0; +} + + +.sidebar ul li { + cursor: pointer; + border-bottom: 1px solid red; + color:blue; +} + +.btn { + background-color: white; + font-family: webkit-pictograph; +} + +.alert-info { + color: red; + background-color: white; + border-color: red; +} + +p { + color: red; + font-family: webkit-pictograph; +} + +.text-muted { + color:blue; +} + +.h3 h3 { + color:blue; +} +.h2, h2 { + font-size: 30px; + color:blue; +} +.h1, h1 { + font-size: 30px; + color:blue; +} + +.btn-default { + color:red; + border-color: red; +} + +.btn-default:hover { + color: red; + background-color: red; + border-color: red; +} + +.sidebar .active { + background-color: white; +} + +@media(min-width:768px) { + .sidebar { + z-index: 1; + position: absolute; + width: 250px; + margin-top: 51px; + } + + .module-content { + position: inherit; + margin: 0 0 0 250px; + padding: 15px 30px; + border-left: 1px solid white; + } + + .navbar-top-links { + margin-left: 10px; + } +} + +.navbar-top-links { + margin-right: 5px; + margin-left: 10px; +} + +.navbar-top-links li { + display: inline-block; +} + +.form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { + background-color: white; + opacity: 1; + color: red; +} + +.panel-footer { + padding: 10px 15px; + background-color: white; + border-top: 1px solid red; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} + +textarea.form-control { + height: auto; + background-color: white; + opacity: 1; + color: red; + font-family: webkit-pictograph; +} +.navbar { + margin-bottom: 0; +} + +.navbar-top-links .dropdown-menu li { + display: block; +} + +.module-content { + padding: 15px 15px; + background-color: white; +} + +.pointer { + cursor: pointer; +} + +.dropdown-menu { + cursor: pointer; +} + +.dropdown-menu-top { + max-height: 300px !important; + overflow-y: auto !important; +} + +@media(max-width:768px) { + .dropdown-menu-top { + margin: auto !important; + position: absolute !important; + background-color: white !important; + word-wrap: break-word !important; + border: 1px solid white !important; + width: 300px !important; + max-width: 300px !important; + } + .dropdown-menu-top li a { + white-space: normal !important; + } + + .dropdown-menu-logout { + max-width: 50px !important; + } +} + +.login-logo { + margin: 0 auto; + padding-bottom: 10px; + max-width: 150px; +} + +.brand-logo { + content: url('/img/logo.png'); + max-height: 30px; + padding-bottom: 5px; +} + +.brand-text::after { + content: "pineapple"; + padding-top: 3px; + padding-left: 5px; + float: right; + color:red; +} + +.alert-danger { + color: red; + background-color: white; + border-color: red; +} + +.panel-title { + background-color:white; + color: red; +} + +.panel-default>.panel-heading+.panel-collapse>.panel-body { + border-top-color: red; +} + +.panel-body { + background-color:white; + font-family: webkit-pictograph; +} + +.panel-heading { + background-color:white; +} + +.panel-default>.panel-heading { + color: blue; + background-color: white; + border-color: red; + border-bottom-color: red; + border-bottom: red; +} + +td { + background-color:white; +} +.nav { + background-color:white; +} +.sidebar-nav.navbar-collapse { + background-color:white; +} +.navbar-default{ + background-color:white; +} +.navbar-static-top { + background-color:white; + border-color: red; +} +.input-group .form-control:first-child, .input-group-addon:first-child, .input-group-btn:first-child>.btn, .input-group-btn:first-child>.btn-group>.btn, .input-group-btn:first-child>.dropdown-toggle, .input-group-btn:last-child>.btn-group:not(:last-child)>.btn, .input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle) { + font-family: webkit-pictograph; + background-color: red; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + color: white; +} +.table>thead>tr>th { + vertical-align: bottom; + border-bottom: 2px solid red; +} +.table>tbody>tr>td, .table>tbody>tr>th, .table>tfoot>tr>td, .table>tfoot>tr>th, .table>thead>tr>td, .table>thead>tr>th { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid red; +} \ No newline at end of file diff --git a/Themes/css/kbeflo-dark.css b/Themes/css/kbeflo-dark.css new file mode 100644 index 0000000..c41c245 --- /dev/null +++ b/Themes/css/kbeflo-dark.css @@ -0,0 +1,529 @@ +.truncated { + text-overflow: ellipsis; + overflow: hidden +} + +.uppercase { + text-transform: uppercase; +} + +.table-layout-fixed { + table-layout: fixed; +} + +.module-icon { + display: inline; + height: 24px; + width: 24px; +} + +.fixed-addon-width { + min-width: 70px; + text-align: left; +} + +.fixed-addon-width-2 { + min-width: 90px; + text-align: left; +} + +.fixed-addon-width-3 { + min-width: 110px; + text-align: left; +} + +.fixed-width-200 { + min-width: 200px; +} + +.caret-reversed { + border-top-width: 0; + border-bottom: 4px solid #000; +} + +.image-small-18 { + height: 18px; +} + +.center-text { + text-align: center; +} + +.scrollable-pre { + overflow: auto; + word-wrap: normal; + white-space: pre; +} + +.log-pre { + max-height: 300px; +} + +.btn-fixed-length { + width: 70px; +} + +.title-message { + margin-left: 10px; + padding-left: 5px; + padding-right: 5px; + height: 9px; + border-radius: 3px; +} + +.padding-left { + margin-left: 10px; +} + +.select-inline { + font-weight: normal; +} + +body { + background-color: #0c0c0c; +} + +.logout { + cursor: pointer; +} + +.module-nav li a { + margin-left: 30px; +} + +.module-nav li:hover { + background-color: #131313; +} + +.sidebar .sidebar-nav.navbar-collapse { + padding-right: 0; + padding-left: 0; +} + +.sidebar ul li { + cursor: pointer; + border-bottom: 1px solid #181818; +} + +.sidebar .active { + background-color: #131313; +} + +@media(min-width:768px) { + .sidebar { + z-index: 1; + position: absolute; + width: 250px; + margin-top: 51px; + } + + .module-content { + position: inherit; + margin: 0 0 0 250px; + padding: 15px 30px; + border-left: 1px solid #202020; + } + + .navbar-top-links { + margin-left: 10px; + } +} + +.navbar-top-links { + margin-right: 5px; + margin-left: 10px; +} + +.navbar-top-links li { + display: inline-block; +} + +.navbar { + margin-bottom: 0; +} + +.navbar-top-links .dropdown-menu li { + display: block; +} + +.module-content { + padding: 15px 15px; + background-color: #000; +} + +.pointer { + cursor: pointer; +} + +.dropdown-menu { + cursor: pointer; +} + +.dropdown-menu-top { + max-height: 300px !important; + overflow-y: auto !important; +} + +@media(max-width:768px) { + .dropdown-menu-top { + margin: auto !important; + position: absolute !important; + background-color: #fff !important; + word-wrap: break-word !important; + border: 1px solid #ccc !important; + width: 300px !important; + max-width: 300px !important; + } + .dropdown-menu-top li a { + white-space: normal !important; + } + + .dropdown-menu-logout { + max-width: 50px !important; + } +} + +.login-logo { + margin: 0 auto; + padding-bottom: 10px; + max-width: 150px; +} + +.brand-logo { + content: url('/img/logo.png'); + max-height: 30px; + padding-bottom: 5px; +} + +.brand-text::after { + content: "WiFi Pineapple"; + padding-top: 3px; + padding-left: 5px; + float: right; +} + +.navbar-default { + background-color: #0c0c0c; + border-color: #181818; +} + +.panel-footer { + background-color: #0e0e0e; + border-top: 1px solid #202020; +} + +.panel-default { + border-color: #202020; +} + +.panel-default>.panel-heading { + color: #c6c6c6; + background-color: #0e0e0e; + border-color: #202020; +} + +.panel-body { + color: #c6c6c6; + background-color: #000; +} + +.h2, h2 { + font-size: 30px; + color: #c6c6c6; +} + +.panel>.panel-body+.table, +.panel>.panel-body+.table-responsive, +.panel>.table+.panel-body, +.panel>.table-responsive+.panel-body { + border-top: 1px solid #202020; +} + +a { + color: #c6c6c6; + text-decoration: none; +} + +a:focus, a:hover { + color: #c6c6c6; + text-decoration: none; +} + +.nav>li>a:focus, .nav>li>a:hover { + text-decoration: none; + background-color: #131313; +} + +.btn-default { + color: #c6c6c6; + background-color: #010101; + border-color: #454545; +} + +.btn-default.active, .btn-default:active { + color: #c6c6c6; + background-color: #010101; + border-color: #212121; +} + +.btn-default:hover { + color: #c6c6c6; + background-color: #131313; + border-color: #767676; +} + +.btn-default.focus, .btn-default:focus { + color: #c6c6c6; + background-color: #010101; + border-color: #212121; +} + +.btn-default.active.focus, .btn-default.active:focus, .btn-default.active:hover, .btn-default:active.focus, .btn-default:active:focus, .btn-default:active:hover, .open>.dropdown-toggle.btn-default.focus, .open>.dropdown-toggle.btn-default:focus, .open>.dropdown-toggle.btn-default:hover { + color: #212121; + background-color: #010101; + border-color: #171717; +} + +.navbar-default .navbar-nav>li>a { + color: #404041; +} + +.navbar-default .navbar-nav>.open>a, .navbar-default .navbar-nav>.open>a:focus, .navbar-default .navbar-nav>.open>a:hover { + color: #404041; + background-color: #0c0c0c; +} + +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: 400; + line-height: 1; + color: #a2a2a2; + text-align: center; + background-color: #131313; + border: 1px solid #2e2e2e; + border-radius: 4px; +} + +.modal-body { + background-color: #000; + border: 1px solid #202020; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; +} + +.modal-header { + color: #c6c6c6; + border: 1px solid #202020; + border-bottom: none; + background-color: #000; + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} + +.form-control { + background-color: #111111; + border: 1px solid #333333; + color: #c6c6c6; +} + +.btn-success { + color: #c6c6c6; + background-color: #010101; + border-color: #454545; +} + +.btn-success.active, .btn-success:active { + color: #c6c6c6; + background-color: #010101; + border-color: #212121; +} + +.btn-success.focus, .btn-success:focus { + color: #c6c6c6; + background-color: #010101; + border-color: #212121; +} + +.btn-success:hover { + color: #c6c6c6; + background-color: #010101; + border-color: #212121; +} + +.btn-success.active.focus, .btn-success.active:focus, .btn-success.active:hover, .btn-success:active.focus, .btn-success:active:focus, .btn-success:active:hover, .open>.dropdown-toggle.btn-success.focus, .open>.dropdown-toggle.btn-success:focus, .open>.dropdown-toggle.success:hover { + color: #212121; + background-color: #010101; + border-color: #171717; +} + +.btn-default.disabled, .btn-default.disabled.active, .btn-default.disabled.focus, .btn-default.disabled:active, .btn-default.disabled:focus, .btn-default.disabled:hover, .btn-default[disabled], .btn-default[disabled].active, .btn-default[disabled].focus, .btn-default[disabled]:active, .btn-default[disabled]:focus, .btn-default[disabled]:hover, fieldset[disabled] .btn-default, fieldset[disabled] .btn-default.active, fieldset[disabled] .btn-default.focus, fieldset[disabled] .btn-default:active, fieldset[disabled] .btn-default:focus, fieldset[disabled] .btn-default:hover { + + background-color: #111; + border-color: #333; + color: #333; + +} + +.panel-group .panel-heading+.panel-collapse>.list-group, .panel-group .panel-heading+.panel-collapse>.panel-body { + border-top: 1px solid #212121; +} + +.form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { + background-color: #111111; + opacity: 1; +} + +pre { + color: #c6c6c6; + background-color: #111111; + border: 1px solid #333333; +} + +.alert-info { + color: #a94442; + background-color: #000; + border-color: #000; +} + +.alert-success { + color: #a94442; + background-color: #000; + border-color: #000; +} + +.table { + color: #c6c6c6; + background-color: #000; +} + +.table > thead > tr > th { + border-bottom: 2px solid #333; +} + +.table > tbody > tr.active > td, .table > tbody > tr.active > th, .table > tbody > tr > td.active, .table > tbody > tr > th.active, .table > tfoot > tr.active > td, .table > tfoot > tr.active > th, .table > tfoot > tr > td.active, .table > tfoot > tr > th.active, .table > thead > tr.active > td, .table > thead > tr.active > th, .table > thead > tr > td.active, .table > thead > tr > th.active { + color: #c6c6c6; + background-color: #000; + border-color: #333; +} + +.table > tbody > tr > td, .table > tbody > tr > th, .table > tfoot > tr > td, .table > tfoot > tr > th, .table > thead > tr > td, .table > thead > tr > th { + border-top: 1px solid #333; +} + +.progress-bar { + background-color: #202020; + +} + +.progress { + background-color: #111; +} + +.alert-danger { + color: #a94442; + background-color: #000; + border-color: #000; +} + +.table-striped > tbody > tr:nth-of-type(2n+1) { + background-color: #000; +} + +tr:hover td { + background: #000; +} + +.text-info { + color: #a94442; +} + +.open > .dropdown-menu { + background-color: #0c0c0c; + border-color: #181818; + color: #c6c6c6; +} + +.dropdown-menu > li > a { + color: #777; +} + +.dropdown-menu > li > a:focus, .dropdown-menu > li > a:hover { + color: #777; + background-color: #131313; +} + +.btn-info { + color: #c6c6c6; + background-color: #010101; + border-color: #454545; +} + +.btn-info:hover { + color: #c6c6c6; + background-color: #131313; + border-color: #767676; +} + +.btn-info.active, .btn-info:active { + color: #c6c6c6; + background-color: #010101; + border-color: #212121; +} + +.btn-info.focus, .btn-info:focus { + color: #c6c6c6; + background-color: #010101; + border-color: #212121; +} + +.btn-info.active.focus, .btn-info.active:focus, .btn-info.active:hover, .btn-info:active.focus, .btn-info:active:focus, .btn-info:active:hover, .open>.dropdown-toggle.btn-info.focus, .open>.dropdown-toggle.btn-info:focus, .open>.dropdown-toggle.btn-info:hover { + color: #212121; + background-color: #010101; + border-color: #171717; +} + +.btn-danger { + color: #c6c6c6; + background-color: #010101; + border-color: #454545; +} + +.btn-danger:hover { + color: #c6c6c6; + background-color: #131313; + border-color: #767676; +} + +.btn-danger.active, .btn-danger:active { + color: #c6c6c6; + background-color: #010101; + border-color: #212121; +} + +.btn-danger.focus, .btn-danger:focus { + color: #c6c6c6; + background-color: #010101; + border-color: #212121; +} + +.btn-danger.active.focus, .btn-danger.active:focus, .btn-danger.active:hover, .btn-danger:active.focus, .btn-danger:active:focus, .btn-danger:active:hover, .open>.dropdown-toggle.btn-danger.focus, .open>.dropdown-toggle.btn-danger:focus, .open>.dropdown-toggle.btn-danger:hover { + color: #212121; + background-color: #010101; + border-color: #171717; +} +.btn-switch { + position: relative; + display: block; + width: 50px; + height: 25px; + cursor: pointer; + background-color: darkgray; + border: 2px solid darkgray; + border-radius: 40px; + +} +.btn-switch--on { + background-color: #ccffff; + border: 2px solid #ccffff; +} \ No newline at end of file diff --git a/Themes/css/main.css b/Themes/css/main.css new file mode 100644 index 0000000..9c42935 --- /dev/null +++ b/Themes/css/main.css @@ -0,0 +1,203 @@ +.truncated { + text-overflow: ellipsis; + overflow: hidden +} + +.uppercase { + text-transform: uppercase; +} + +.table-layout-fixed { + table-layout: fixed; +} + +.module-icon { + display: inline; + height: 24px; + width: 24px; +} + +.fixed-addon-width { + min-width: 70px; + text-align: left; +} + +.fixed-addon-width-2 { + min-width: 90px; + text-align: left; +} + +.fixed-addon-width-3 { + min-width: 110px; + text-align: left; +} + +.fixed-width-200 { + min-width: 200px; +} + +.caret-reversed { + border-top-width: 0; + border-bottom: 4px solid #000; +} + +.image-small-18 { + height: 18px; +} + +.center-text { + text-align: center; +} + +.scrollable-pre { + overflow: auto; + word-wrap: normal; + white-space: pre; +} + +.log-pre { + max-height: 300px; +} + +.btn-fixed-length { + width: 70px; +} + +.title-message { + margin-left: 10px; + padding-left: 5px; + padding-right: 5px; + height: 9px; + border-radius: 3px; +} + +.padding-left { + margin-left: 10px; +} + +.select-inline { + font-weight: normal; +} + +body { + background-color: #f8f8f8; +} + +.logout { + cursor: pointer; +} + +.module-nav li a { + margin-left: 30px; +} + +.module-nav li:hover { + background-color: #eee; +} + +.sidebar .sidebar-nav.navbar-collapse { + padding-right: 0; + padding-left: 0; +} + +.sidebar ul li { + cursor: pointer; + border-bottom: 1px solid #e7e7e7; +} + +.sidebar .active { + background-color: #eee; +} + +@media(min-width:768px) { + .sidebar { + z-index: 1; + position: absolute; + width: 250px; + margin-top: 51px; + } + + .module-content { + position: inherit; + margin: 0 0 0 250px; + padding: 15px 30px; + border-left: 1px solid #e7e7e7; + } + + .navbar-top-links { + margin-left: 10px; + } +} + +.navbar-top-links { + margin-right: 5px; + margin-left: 10px; +} + +.navbar-top-links li { + display: inline-block; +} + +.navbar { + margin-bottom: 0; +} + +.navbar-top-links .dropdown-menu li { + display: block; +} + +.module-content { + padding: 15px 15px; + background-color: #fff; +} + +.pointer { + cursor: pointer; +} + +.dropdown-menu { + cursor: pointer; +} + +.dropdown-menu-top { + max-height: 300px !important; + overflow-y: auto !important; +} + +@media(max-width:768px) { + .dropdown-menu-top { + margin: auto !important; + position: absolute !important; + background-color: #fff !important; + word-wrap: break-word !important; + border: 1px solid #ccc !important; + width: 300px !important; + max-width: 300px !important; + } + .dropdown-menu-top li a { + white-space: normal !important; + } + + .dropdown-menu-logout { + max-width: 50px !important; + } +} + +.login-logo { + margin: 0 auto; + padding-bottom: 10px; + max-width: 150px; +} + +.brand-logo { + content: url('/img/logo.png'); + max-height: 30px; + padding-bottom: 5px; +} + +.brand-text::after { + content: "WiFi Pineapple"; + padding-top: 3px; + padding-left: 5px; + float: right; +} \ No newline at end of file diff --git a/Themes/css/malware.css b/Themes/css/malware.css new file mode 100644 index 0000000..febedcf --- /dev/null +++ b/Themes/css/malware.css @@ -0,0 +1,478 @@ +.truncated { + text-overflow: ellipsis; + overflow: hidden +} + +a { + color:red; + font-family: monospace; +} + +i { + color: red; + font: monospace; +} + +b, strong { + font-weight: 700; + font-family: monospace; +} + +.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { + font-family: monospace; + font-weight: 500; + line-height: 1.1; + color: inherit; +} + +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: 400; + line-height: 1; + color: red; + text-align: center; + background-color: black; + border: 1px solid red; + border-radius: 4px; +} + +.panel-default { + border-color: red; +} + +.panel>.panel-body+.table, .panel>.panel-body+.table-responsive, .panel>.table+.panel-body, .panel>.table-responsive+.panel-body { + border-top: 1px solid red; +} + +.dropdown-menu>li>a { + display:block; + padding: 3px 20px; + clear: both; + font-weight: 400; + line-height: 1.42857143; + color: red; + white-space:nowrap; + background-color: black; + border: red; +} + +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: red; + background-color: black; + background-image: none; + border: 1px solid red; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; +} + +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-bottom: 1px solid red; + border-top: 1px solid red; +} + +table { + background-color: black; +} + +.table-responsive { + min-height: .01%; + overflow-x: auto; + background-color: black; +} + +* { + color: red; + border-color: red; + border-top: red; +} +.uppercase { + text-transform: uppercase; +} + +.table-layout-fixed { + table-layout: fixed; +} + +.module-icon { + display: inline; + height: 24px; + width: 24px; +} + +.fixed-addon-width { + min-width: 70px; + text-align: left; +} + +.fixed-addon-width-2 { + min-width: 90px; + text-align: left; +} + + +.fixed-addon-width-3 { + min-width: 110px; + text-align: left; +} + +.fixed-width-200 { + min-width: 200px; +} + +.caret-reversed { + border-top-width: 0; + border-bottom: 4px solid #000; +} + +.image-small-18 { + height: 18px; +} + +.center-text { + text-align: center; +} + +.scrollable-pre { + overflow: auto; + word-wrap: normal; + white-space: pre; +} + +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: red; + word-break: break-all; + word-wrap: break-word; + background-color: #000000; + border: 1px solid red; + border-radius: 4px; + font-family: monospace; +} + +.log-pre { + max-height: 300px; +} + +.btn-fixed-length { + width: 70px; +} + +.title-message { + margin-left: 10px; + padding-left: 5px; + padding-right: 5px; + height: 9px; + border-radius: 3px; +} + +.padding-left { + margin-left: 10px; +} + +.select-inline { + font-weight: normal; +} + +body { + background-color: #000000; +} + +.logout { + cursor: pointer; +} + +.module-nav li a { + margin-left: 30px; +} + +.module-nav li:hover { + background-color: #000; +} + +.nav>li>a:focus, .nav>li>a:hover { + text-decoration: none; + background-color: darkred; +} +.sidebar .sidebar-nav.navbar-collapse { + padding-right: 0; + padding-left: 0; +} + +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #ff0000; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.1); + box-shadow: inset 0 1px 2px rgba(0,0,0,.1); +} + +.table>tbody>tr.active>td, .table>tbody>tr.active>th, .table>tbody>tr>td.active, .table>tbody>tr>th.active, .table>tfoot>tr.active>td, .table>tfoot>tr.active>th, .table>tfoot>tr>td.active, .table>tfoot>tr>th.active, .table>thead>tr.active>td, .table>thead>tr.active>th, .table>thead>tr>td.active, .table>thead>tr>th.active { + background-color: #000000; +} + +.sidebar ul li { + cursor: pointer; + border-bottom: 1px solid darkred; + color:red; +} + +.btn { + background-color: black; + font-family: monospace; +} + +.alert-info { + color: red; + background-color: black; + border-color: red; +} + +p { + color: red; + font-family: monospace; +} + +.text-muted { + color:darkred; +} + +.h3 h3 { + color:red; +} +.h2, h2 { + font-size: 30px; + color:red; +} +.h1, h1 { + font-size: 30px; + color:red; +} + +.btn-default { + color:red; + border-color: red; +} + +.btn-default:hover { + color: red; + background-color: red; + border-color: red; +} + +.sidebar .active { + background-color: #13033a; +} + +@media(min-width:768px) { + .sidebar { + z-index: 1; + position: absolute; + width: 250px; + margin-top: 51px; + } + + .module-content { + position: inherit; + margin: 0 0 0 250px; + padding: 15px 30px; + border-left: 1px solid #000000; + } + + .navbar-top-links { + margin-left: 10px; + } +} + +.navbar-top-links { + margin-right: 5px; + margin-left: 10px; +} + +.navbar-top-links li { + display: inline-block; +} + +.form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { + background-color: #000; + opacity: 1; + color: red; +} + +.panel-footer { + padding: 10px 15px; + background-color: #000000; + border-top: 1px solid darkred; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} + +textarea.form-control { + height: auto; + background-color: #000; + opacity: 1; + color: red; + font-family: monospace; +} +.navbar { + margin-bottom: 0; +} + +.navbar-top-links .dropdown-menu li { + display: block; +} + +.module-content { + padding: 15px 15px; + background-color: #000; +} + +.pointer { + cursor: pointer; +} + +.dropdown-menu { + cursor: pointer; +} + +.dropdown-menu-top { + max-height: 300px !important; + overflow-y: auto !important; +} + +@media(max-width:768px) { + .dropdown-menu-top { + margin: auto !important; + position: absolute !important; + background-color: #000 !important; + word-wrap: break-word !important; + border: 1px solid #000 !important; + width: 300px !important; + max-width: 300px !important; + } + .dropdown-menu-top li a { + white-space: normal !important; + } + + .dropdown-menu-logout { + max-width: 50px !important; + } +} + +.login-logo { + margin: 0 auto; + padding-bottom: 10px; + max-width: 150px; +} + +.brand-logo { + content: url('/img/logo.png'); + max-height: 30px; + padding-bottom: 5px; +} + +.brand-text::after { + content: "pineapple"; + padding-top: 3px; + padding-left: 5px; + float: right; + color:red; +} + +.alert-danger { + color: red; + background-color: black; + border-color: red; +} + +.panel-title { + background-color:black; + color: red; +} + +.panel-default>.panel-heading+.panel-collapse>.panel-body { + border-top-color: red; +} + +.panel-body { + background-color:black; + font-family:monospace; +} + +.panel-heading { + background-color:black; +} + +.panel-default>.panel-heading { + color: red; + background-color: black; + border-color: red; + border-bottom-color: red; + border-bottom: red; +} + +td { + background-color:black; +} +.nav { + background-color:black; +} +.sidebar-nav.navbar-collapse { + background-color:black; +} +.navbar-default{ + background-color:black; +} +.navbar-static-top { + background-color:black; + border-color: red; +} +.input-group .form-control:first-child, .input-group-addon:first-child, .input-group-btn:first-child>.btn, .input-group-btn:first-child>.btn-group>.btn, .input-group-btn:first-child>.dropdown-toggle, .input-group-btn:last-child>.btn-group:not(:last-child)>.btn, .input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle) { + font-family: monospace; + background-color: red; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + color: black; +} +.table>thead>tr>th { + vertical-align: bottom; + border-bottom: 2px solid red; +} +.table>tbody>tr>td, .table>tbody>tr>th, .table>tfoot>tr>td, .table>tfoot>tr>th, .table>thead>tr>td, .table>thead>tr>th { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid red; +} + +.modal-content { + position: relative; + background-color: black; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0,0,0,.2); + border-radius: 6px; + outline: 0; + -webkit-box-shadow: 0 3px 9px rgba(0,0,0,.5); + box-shadow: 0 3px 9px rgba(0,0,0,.5); +} \ No newline at end of file diff --git a/Themes/css/morning.css b/Themes/css/morning.css new file mode 100644 index 0000000..331bc20 --- /dev/null +++ b/Themes/css/morning.css @@ -0,0 +1,464 @@ +.truncated { + text-overflow: ellipsis; + overflow: hidden +} + +a { + color:cornflowerblue; + font-family:cursive; +} + +i { + color: cornflowerblue; + font: cursive; +} + +b, strong { + font-weight: 700; + font-family: cursive; +} + +.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { + font-family: cursive; + font-weight: 500; + line-height: 1.1; + color: cornflowerblue; +} + +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: 400; + line-height: 1; + color: rosybrown; + text-align: center; + background-color: wheat; + border: 1px solid rosybrown; + border-radius: 4px; +} + +.panel-default { + border-color: rosybrown; +} + +.panel>.panel-body+.table, .panel>.panel-body+.table-responsive, .panel>.table+.panel-body, .panel>.table-responsive+.panel-body { + border-top: 1px solid rosybrown; +} + +.dropdown-menu>li>a { + display:block; + padding: 3px 20px; + clear: both; + font-weight: 400; + line-height: 1.42857143; + color: cornflowerblue; + white-space:nowrap; + background-color: wheat; + border: rosybrown; +} + +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: rosybrown; + background-color: wheat; + background-image: none; + border: 1px solid rosybrown; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; +} + +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-bottom: 1px solid rosybrown; + border-top: 1px solid rosybrown; +} + +table { + background-color: wheat; +} + +.table-responsive { + min-height: .01%; + overflow-x: auto; + background-color: wheat; +} + +* { + color: rosybrown; + border-color: rosybrown; + border-top: rosybrown; +} +.uppercase { + text-transform: uppercase; +} + +.table-layout-fixed { + table-layout: fixed; +} + +.module-icon { + display: inline; + height: 24px; + width: 24px; +} + +.fixed-addon-width { + min-width: 70px; + text-align: left; +} + +.fixed-addon-width-2 { + min-width: 90px; + text-align: left; +} + + +.fixed-addon-width-3 { + min-width: 110px; + text-align: left; +} + +.fixed-width-200 { + min-width: 200px; +} + +.caret-reversed { + border-top-width: 0; + border-bottom: 4px solid wheat; +} + +.image-small-18 { + height: 18px; +} + +.center-text { + text-align: center; +} + +.scrollable-pre { + overflow: auto; + word-wrap: normal; + white-space: pre; +} + +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: rosybrown; + word-break: break-all; + word-wrap: break-word; + background-color: wheat; + border: 1px solid rosybrown; + border-radius: 4px; + font-family: cursive; +} + +.log-pre { + max-height: 300px; +} + +.btn-fixed-length { + width: 70px; +} + +.title-message { + margin-left: 10px; + padding-left: 5px; + padding-right: 5px; + height: 9px; + border-radius: 3px; +} + +.padding-left { + margin-left: 10px; +} + +.select-inline { + font-weight: normal; +} + +body { + background-color: wheat; +} + +.logout { + cursor: pointer; +} + +.module-nav li a { + margin-left: 30px; +} + +.module-nav li:hover { + background-color: wheat; +} + +.nav>li>a:focus, .nav>li>a:hover { + text-decoration: none; + background-color: rosybrown; +} +.sidebar .sidebar-nav.navbar-collapse { + padding-right: 0; + padding-left: 0; +} + + +.sidebar ul li { + cursor: pointer; + border-bottom: 1px solid rosybrown; + color:cornflowerblue; +} + +.btn { + background-color: wheat; + font-family: cursive; +} + +.alert-info { + color: rosybrown; + background-color: wheat; + border-color: rosybrown; +} + +p { + color: rosybrown; + font-family: cursive; +} + +.text-muted { + color:darkmagenta; +} + +.h3 h3 { + color:cornflowerblue; +} +.h2, h2 { + font-size: 30px; + color:cornflowerblue; +} +.h1, h1 { + font-size: 30px; + color:cornflowerblue; +} + +.btn-default { + color:rosybrown; + border-color: rosybrown; +} + +.btn-default:hover { + color: rosybrown; + background-color: rosybrown; + border-color: rosybrown; +} + +.sidebar .active { + background-color: wheat; +} + +@media(min-width:768px) { + .sidebar { + z-index: 1; + position: absolute; + width: 250px; + margin-top: 51px; + } + + .module-content { + position: inherit; + margin: 0 0 0 250px; + padding: 15px 30px; + border-left: 1px solid wheat; + } + + .navbar-top-links { + margin-left: 10px; + } +} + +.navbar-top-links { + margin-right: 5px; + margin-left: 10px; +} + +.navbar-top-links li { + display: inline-block; +} + +.form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { + background-color: wheat; + opacity: 1; + color: rosybrown; +} + +.panel-footer { + padding: 10px 15px; + background-color: wheat; + border-top: 1px solid rosybrown; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} + +textarea.form-control { + height: auto; + background-color: wheat; + opacity: 1; + color: rosybrown; + font-family: cursive; +} +.navbar { + margin-bottom: 0; +} + +.navbar-top-links .dropdown-menu li { + display: block; +} + +.module-content { + padding: 15px 15px; + background-color: wheat; +} + +.pointer { + cursor: pointer; +} + +.dropdown-menu { + cursor: pointer; +} + +.dropdown-menu-top { + max-height: 300px !important; + overflow-y: auto !important; +} + +@media(max-width:768px) { + .dropdown-menu-top { + margin: auto !important; + position: absolute !important; + background-color: wheat !important; + word-wrap: break-word !important; + border: 1px solid wheat !important; + width: 300px !important; + max-width: 300px !important; + } + .dropdown-menu-top li a { + white-space: normal !important; + } + + .dropdown-menu-logout { + max-width: 50px !important; + } +} + +.login-logo { + margin: 0 auto; + padding-bottom: 10px; + max-width: 150px; +} + +.brand-logo { + content: url('/img/logo.png'); + max-height: 30px; + padding-bottom: 5px; +} + +.brand-text::after { + content: "pineapple"; + padding-top: 3px; + padding-left: 5px; + float: right; + color:rosybrown; +} + +.alert-danger { + color: rosybrown; + background-color: wheat; + border-color: rosybrown; +} + +.panel-title { + background-color:wheat; + color: rosybrown; +} + +.panel-default>.panel-heading+.panel-collapse>.panel-body { + border-top-color: rosybrown; +} + +.panel-body { + background-color:wheat; + font-family: cursive; +} + +.panel-heading { + background-color:wheat; +} + +.panel-default>.panel-heading { + color: cornflowerblue; + background-color: wheat; + border-color: rosybrown; + border-bottom-color: rosybrown; + border-bottom: rosybrown; +} + +td { + background-color:wheat; +} +.nav { + background-color:wheat; +} +.sidebar-nav.navbar-collapse { + background-color:wheat; +} +.navbar-default{ + background-color:wheat; +} +.navbar-static-top { + background-color:wheat; + border-color: rosybrown; +} +.input-group .form-control:first-child, .input-group-addon:first-child, .input-group-btn:first-child>.btn, .input-group-btn:first-child>.btn-group>.btn, .input-group-btn:first-child>.dropdown-toggle, .input-group-btn:last-child>.btn-group:not(:last-child)>.btn, .input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle) { + font-family: cursive; + background-color: rosybrown; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + color: wheat; +} +.table>thead>tr>th { + vertical-align: bottom; + border-bottom: 2px solid rosybrown; +} +.table>tbody>tr>td, .table>tbody>tr>th, .table>tfoot>tr>td, .table>tfoot>tr>th, .table>thead>tr>td, .table>thead>tr>th { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid rosybrown; +} +.modal-content { + position: relative; + background-color: wheat; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0,0,0,.2); + border-radius: 6px; + outline: 0; + -webkit-box-shadow: 0 3px 9px rgba(0,0,0,.5); + box-shadow: 0 3px 9px rgba(0,0,0,.5); +} \ No newline at end of file diff --git a/Themes/css/neon.css b/Themes/css/neon.css new file mode 100644 index 0000000..3cc8433 --- /dev/null +++ b/Themes/css/neon.css @@ -0,0 +1,457 @@ +.truncated { + text-overflow: ellipsis; + overflow: hidden +} + +a { + color:magenta; + font-family: monospace; +} + +i { + color: lime; + font: monospace; +} + +b, strong { + font-weight: 700; + font-family: monospace; +} + +.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { + font-family: monospace; + font-weight: 500; + line-height: 1.1; + color: inherit; +} + +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: 400; + line-height: 1; + color: lime; + text-align: center; + background-color: purple; + border: 1px solid lime; + border-radius: 4px; +} + +.panel-default { + border-color: lime; +} + +.panel>.panel-body+.table, .panel>.panel-body+.table-responsive, .panel>.table+.panel-body, .panel>.table-responsive+.panel-body { + border-top: 1px solid lime; +} + +.dropdown-menu>li>a { + display:block; + padding: 3px 20px; + clear: both; + font-weight: 400; + line-height: 1.42857143; + color: lime; + white-space:nowrap; + background-color: purple; + border: lime; +} + +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: lime; + background-color: purple; + background-image: none; + border: 1px solid lime; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; +} + +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-bottom: 1px solid lime; + border-top: 1px solid lime; +} + +table { + background-color: purple; +} + +.table-responsive { + min-height: .01%; + overflow-x: auto; + background-color: purple; +} + +* { + color: lime; + border-color: lime; +} +.uppercase { + text-transform: uppercase; +} + +.table-layout-fixed { + table-layout: fixed; +} + +.module-icon { + display: inline; + height: 24px; + width: 24px; +} + +.fixed-addon-width { + min-width: 70px; + text-align: left; +} + +.fixed-addon-width-2 { + min-width: 90px; + text-align: left; +} + + +.fixed-addon-width-3 { + min-width: 110px; + text-align: left; +} + +.fixed-width-200 { + min-width: 200px; +} + +.caret-reversed { + border-top-width: 0; + border-bottom: 4px solid #000; +} + +.image-small-18 { + height: 18px; +} + +.center-text { + text-align: center; +} + +.scrollable-pre { + overflow: auto; + word-wrap: normal; + white-space: pre; +} + +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: lime; + word-break: break-all; + word-wrap: break-word; + background-color: #000000; + border: 1px solid lime; + border-radius: 4px; + font-family: monospace; +} + +.log-pre { + max-height: 300px; +} + +.btn-fixed-length { + width: 70px; +} + +.title-message { + margin-left: 10px; + padding-left: 5px; + padding-right: 5px; + height: 9px; + border-radius: 3px; +} + +.padding-left { + margin-left: 10px; +} + +.select-inline { + font-weight: normal; +} + +body { + background-color: #000000; +} + +.logout { + cursor: pointer; +} + +.module-nav li a { + margin-left: 30px; +} + +.module-nav li:hover { + background-color: #000; +} + +.nav>li>a:focus, .nav>li>a:hover { + text-decoration: none; + background-color: limegreen; +} +.sidebar .sidebar-nav.navbar-collapse { + padding-right: 0; + padding-left: 0; +} + + +.sidebar ul li { + cursor: pointer; + border-bottom: 1px solid #18ff00; + color:lime; +} + +.btn { + background-color: purple; + font-family: monospace; +} + +.alert-info { + color: lime; + background-color: purple; + border-color: lime; +} + +p { + color: lime; + font-family: monospace; +} + +.text-muted { + color:violet; +} + +.h3 h3 { + color:lime; +} +.h2, h2 { + font-size: 30px; + color:lime; +} +.h1, h1 { + font-size: 30px; + color:lime; +} + +.btn-default { + color:lime; + border-color: lime; +} + +.btn-default:hover { + color: magenta; + background-color: lime; + border-color: #adadad; +} + +.sidebar .active { + background-color: #13033a; +} + +@media(min-width:768px) { + .sidebar { + z-index: 1; + position: absolute; + width: 250px; + margin-top: 51px; + } + + .module-content { + position: inherit; + margin: 0 0 0 250px; + padding: 15px 30px; + border-left: 1px solid #000000; + } + + .navbar-top-links { + margin-left: 10px; + } +} + +.navbar-top-links { + margin-right: 5px; + margin-left: 10px; +} + +.navbar-top-links li { + display: inline-block; +} + +.form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { + background-color: #000; + opacity: 1; + color: lime; +} + +.panel-footer { + padding: 10px 15px; + background-color: #000000; + border-top: 1px solid #0F0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} + +textarea.form-control { + height: auto; + background-color: #000; + opacity: 1; + color: lime; + font-family: monospace; +} +.navbar { + margin-bottom: 0; +} + +.navbar-top-links .dropdown-menu li { + display: block; +} + +.module-content { + padding: 15px 15px; + background-color: #000; +} + +.pointer { + cursor: pointer; +} + +.dropdown-menu { + cursor: pointer; +} + +.dropdown-menu-top { + max-height: 300px !important; + overflow-y: auto !important; +} + +@media(max-width:768px) { + .dropdown-menu-top { + margin: auto !important; + position: absolute !important; + background-color: #000 !important; + word-wrap: break-word !important; + border: 1px solid #000 !important; + width: 300px !important; + max-width: 300px !important; + } + .dropdown-menu-top li a { + white-space: normal !important; + } + + .dropdown-menu-logout { + max-width: 50px !important; + } +} + +.login-logo { + margin: 0 auto; + padding-bottom: 10px; + max-width: 150px; +} + +.brand-logo { + content: url('/img/logo.png'); + max-height: 30px; + padding-bottom: 5px; +} + +.brand-text::after { + content: "Neon Pineapple"; + padding-top: 3px; + padding-left: 5px; + float: right; + color:lime; +} + +.alert-danger { + color: lime; + background-color: purple; + border-color: lime; +} + +.panel-title { + background-color:purple; + color: lime; +} + +.panel-body { + background-color:black; + font-family:monospace; +} + +.panel-heading { + background-color:purple; +} + +.panel-default>.panel-heading { + color: lime; + background-color: purple; + border-color: lime; +} + +td { + background-color:black; +} +.nav { + background-color:black; +} +.sidebar-nav.navbar-collapse { + background-color:black; +} +.navbar-default{ + background-color:black; +} +.navbar-static-top { + background-color:black; + border-color: lime; +} +.input-group .form-control:first-child, .input-group-addon:first-child, .input-group-btn:first-child>.btn, .input-group-btn:first-child>.btn-group>.btn, .input-group-btn:first-child>.dropdown-toggle, .input-group-btn:last-child>.btn-group:not(:last-child)>.btn, .input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle) { + font-family: monospace; + background-color: lime; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + color: magenta; +} +.table>thead>tr>th { + vertical-align: bottom; + border-bottom: 2px solid lime; +} +.table>tbody>tr>td, .table>tbody>tr>th, .table>tfoot>tr>td, .table>tfoot>tr>th, .table>thead>tr>td, .table>thead>tr>th { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid lime; +} +.modal-content { + position: relative; + background-color: black; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0,0,0,.2); + border-radius: 6px; + outline: 0; + -webkit-box-shadow: 0 3px 9px rgba(0,0,0,.5); + box-shadow: 0 3px 9px rgba(0,0,0,.5); +} \ No newline at end of file diff --git a/Themes/css/spring.css b/Themes/css/spring.css new file mode 100644 index 0000000..39a43d8 --- /dev/null +++ b/Themes/css/spring.css @@ -0,0 +1,479 @@ +.truncated { + text-overflow: ellipsis; + overflow: hidden +} + +a { + color:#00e673; + font-family: sans-serif; +} + +i { + color: #00e673; + font: sans-serif; +} + +b, strong { + font-weight: 700; + font-family: sans-serif; +} + +.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { + font-family: sans-serif; + font-weight: 500; + line-height: 1.1; + color: inherit; +} + +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: 400; + line-height: 1; + color: #00e673; + text-align: center; + background-color: #00e673; + border: 1px solid #00e673; + border-radius: 4px; +} + +.panel-default { + border-color: #00e673; +} + +.panel>.panel-body+.table, .panel>.panel-body+.table-responsive, .panel>.table+.panel-body, .panel>.table-responsive+.panel-body { + border-top: 1px solid #00e673; +} + +.dropdown-menu>li>a { + display:block; + padding: 3px 20px; + clear: both; + font-weight: 400; + line-height: 1.42857143; + color: #00e673; + white-space:nowrap; + background-color: #6666ff; + border: #00e673; +} + +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: #00e673; + background-color: #6666ff; + background-image: none; + border: 1px solid #00e673; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; +} + +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-bottom: 1px solid #00e673; + border-top: 1px solid #00e673; +} + +table { + background-color: #6666ff; +} + +.table-responsive { + min-height: .01%; + overflow-x: auto; + background-color: #6666ff; +} + +* { + color: #00e673; + border-color: #00e673; + border-top: #00e673; +} +.uppercase { + text-transform: uppercase; +} + +.table-layout-fixed { + table-layout: fixed; +} + +.module-icon { + display: inline; + height: 24px; + width: 24px; +} + +.fixed-addon-width { + min-width: 70px; + text-align: left; +} + +.fixed-addon-width-2 { + min-width: 90px; + text-align: left; +} + + +.fixed-addon-width-3 { + min-width: 110px; + text-align: left; +} + +.fixed-width-200 { + min-width: 200px; +} + +.caret-reversed { + border-top-width: 0; + border-bottom: 4px solid #6666ff; +} + +.image-small-18 { + height: 18px; +} + +.center-text { + text-align: center; +} + +.scrollable-pre { + overflow: auto; + word-wrap: normal; + white-space: pre; +} + +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: #00e673; + word-break: break-all; + word-wrap: break-word; + background-color: #6666ff; + border: 1px solid #00e673; + border-radius: 4px; + font-family: sans-serif; +} + +.log-pre { + max-height: 300px; +} + +.btn-fixed-length { + width: 70px; +} + +.title-message { + margin-left: 10px; + padding-left: 5px; + padding-right: 5px; + height: 9px; + border-radius: 3px; +} + +.padding-left { + margin-left: 10px; +} + +.select-inline { + font-weight: normal; +} + +body { + background-color: #6666ff; +} + +.logout { + cursor: pointer; +} + +.module-nav li a { + margin-left: 30px; +} + +.module-nav li:hover { + background-color: #6666ff; +} + +.nav>li>a:focus, .nav>li>a:hover { + text-decoration: none; + background-color: lightblue; +} +.sidebar .sidebar-nav.navbar-collapse { + padding-right: 0; + padding-left: 0; +} + +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #ff0000; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.1); + box-shadow: inset 0 1px 2px rgba(0,0,0,.1); +} + +.table>tbody>tr.active>td, .table>tbody>tr.active>th, .table>tbody>tr>td.active, .table>tbody>tr>th.active, .table>tfoot>tr.active>td, .table>tfoot>tr.active>th, .table>tfoot>tr>td.active, .table>tfoot>tr>th.active, .table>thead>tr.active>td, .table>thead>tr.active>th, .table>thead>tr>td.active, .table>thead>tr>th.active { + background-color: #6666ff; +} + +.sidebar ul li { + cursor: pointer; + border-bottom: 1px solid dark#00e673; + color:#00e673; +} + +.btn { + background-color: #6666ff; + font-family: sans-serif; +} + +.alert-info { + color: #00e673; + background-color: springgreen; + border-color: #00e673; +} + +p { + color: #00e673; + font-family: sans-serif; +} + +.text-muted { + color:#00e673; +} + +.h3 h3 { + color:#00e673; +} +.h2, h2 { + font-size: 30px; + color:#00e673; +} +.h1, h1 { + font-size: 30px; + color:#00e673; +} + +.btn-default { + color:#00e673; + border-color: #00e673; +} + +.btn-default:hover { + color: #00e673; + background-color: #00e673; + border-color: #00e673; +} + +.sidebar .active { + background-color: #00e673; + color:black +} + +@media(min-width:768px) { + .sidebar { + z-index: 1; + position: absolute; + width: 250px; + margin-top: 51px; + } + + .module-content { + position: inherit; + margin: 0 0 0 250px; + padding: 15px 30px; + border-left: 1px solid #6666ff; + } + + .navbar-top-links { + margin-left: 10px; + } +} + +.navbar-top-links { + margin-right: 5px; + margin-left: 10px; +} + +.navbar-top-links li { + display: inline-block; +} + +.form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { + background-color: #6666ff; + opacity: 1; + color: #00e673; +} + +.panel-footer { + padding: 10px 15px; + background-color: #6666ff; + border-top: 1px solid dark#00e673; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} + +textarea.form-control { + height: auto; + background-color: #6666ff; + opacity: 1; + color: #00e673; + font-family: sans-serif; +} +.navbar { + margin-bottom: 0; +} + +.navbar-top-links .dropdown-menu li { + display: block; +} + +.module-content { + padding: 15px 15px; + background-color: #6666ff; +} + +.pointer { + cursor: pointer; +} + +.dropdown-menu { + cursor: pointer; +} + +.dropdown-menu-top { + max-height: 300px !important; + overflow-y: auto !important; +} + +@media(max-width:768px) { + .dropdown-menu-top { + margin: auto !important; + position: absolute !important; + background-color: #000038 !important; + word-wrap: break-word !important; + border: 1px solid #000038 !important; + width: 300px !important; + max-width: 300px !important; + } + .dropdown-menu-top li a { + white-space: normal !important; + } + + .dropdown-menu-logout { + max-width: 50px !important; + } +} + +.login-logo { + margin: 0 auto; + padding-bottom: 10px; + max-width: 150px; +} + +.brand-logo { + content: url('/img/logo.png'); + max-height: 30px; + padding-bottom: 5px; +} + +.brand-text::after { + content: "pineapple"; + padding-top: 3px; + padding-left: 5px; + float: right; + color:#00e673; +} + +.alert-danger { + color: #00e673; + background-color: #6666ff; + border-color: #00e673; +} + +.panel-title { + background-color:#6666ff; + color: #00e673; +} + +.panel-default>.panel-heading+.panel-collapse>.panel-body { + border-top-color: #00e673; +} + +.panel-body { + background-color:#6666ff; + font-family:sans-serif; +} + +.panel-heading { + background-color:#6666ff; +} + +.panel-default>.panel-heading { + color: #00e673; + background-color: #6666ff; + border-color: #00e673; + border-bottom-color: #00e673; + border-bottom: #00e673; +} + +td { + background-color:#6666ff; +} +.nav { + background-color:#6666ff; +} +.sidebar-nav.navbar-collapse { + background-color:#6666ff; +} +.navbar-default{ + background-color:#6666ff; +} +.navbar-static-top { + background-color:#6666ff; + border-color: #00e673; +} +.input-group .form-control:first-child, .input-group-addon:first-child, .input-group-btn:first-child>.btn, .input-group-btn:first-child>.btn-group>.btn, .input-group-btn:first-child>.dropdown-toggle, .input-group-btn:last-child>.btn-group:not(:last-child)>.btn, .input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle) { + font-family: sans-serif; + background-color: #00e673; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + color: #6666ff; +} +.table>thead>tr>th { + vertical-align: bottom; + border-bottom: 2px solid #00e673; +} +.table>tbody>tr>td, .table>tbody>tr>th, .table>tfoot>tr>td, .table>tfoot>tr>th, .table>thead>tr>td, .table>thead>tr>th { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #00e673; +} + +.modal-content { + position: relative; + background-color: #6666ff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0,0,0,.2); + border-radius: 6px; + outline: 0; + -webkit-box-shadow: 0 3px 9px rgba(0,0,0,.5); + box-shadow: 0 3px 9px rgba(0,0,0,.5); +} \ No newline at end of file diff --git a/Themes/css/starstuff.css b/Themes/css/starstuff.css new file mode 100644 index 0000000..6f0d2bd --- /dev/null +++ b/Themes/css/starstuff.css @@ -0,0 +1,478 @@ +.truncated { + text-overflow: ellipsis; + overflow: hidden +} + +a { + color:gold; + font-family: sans-serif; +} + +i { + color: gold; + font: sans-serif; +} + +b, strong { + font-weight: 700; + font-family: sans-serif; +} + +.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { + font-family: sans-serif; + font-weight: 500; + line-height: 1.1; + color: inherit; +} + +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: 400; + line-height: 1; + color: gold; + text-align: center; + background-color: #000038; + border: 1px solid gold; + border-radius: 4px; +} + +.panel-default { + border-color: gold; +} + +.panel>.panel-body+.table, .panel>.panel-body+.table-responsive, .panel>.table+.panel-body, .panel>.table-responsive+.panel-body { + border-top: 1px solid gold; +} + +.dropdown-menu>li>a { + display:block; + padding: 3px 20px; + clear: both; + font-weight: 400; + line-height: 1.42857143; + color: gold; + white-space:nowrap; + background-color: #000038; + border: gold; +} + +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: gold; + background-color: #000038; + background-image: none; + border: 1px solid gold; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; +} + +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-bottom: 1px solid gold; + border-top: 1px solid gold; +} + +table { + background-color: #000038; +} + +.table-responsive { + min-height: .01%; + overflow-x: auto; + background-color: #000038; +} + +* { + color: gold; + border-color: gold; + border-top: gold; +} +.uppercase { + text-transform: uppercase; +} + +.table-layout-fixed { + table-layout: fixed; +} + +.module-icon { + display: inline; + height: 24px; + width: 24px; +} + +.fixed-addon-width { + min-width: 70px; + text-align: left; +} + +.fixed-addon-width-2 { + min-width: 90px; + text-align: left; +} + + +.fixed-addon-width-3 { + min-width: 110px; + text-align: left; +} + +.fixed-width-200 { + min-width: 200px; +} + +.caret-reversed { + border-top-width: 0; + border-bottom: 4px solid #000038; +} + +.image-small-18 { + height: 18px; +} + +.center-text { + text-align: center; +} + +.scrollable-pre { + overflow: auto; + word-wrap: normal; + white-space: pre; +} + +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: gold; + word-break: break-all; + word-wrap: break-word; + background-color: #000038; + border: 1px solid gold; + border-radius: 4px; + font-family: sans-serif; +} + +.log-pre { + max-height: 300px; +} + +.btn-fixed-length { + width: 70px; +} + +.title-message { + margin-left: 10px; + padding-left: 5px; + padding-right: 5px; + height: 9px; + border-radius: 3px; +} + +.padding-left { + margin-left: 10px; +} + +.select-inline { + font-weight: normal; +} + +body { + background-color: #000038; +} + +.logout { + cursor: pointer; +} + +.module-nav li a { + margin-left: 30px; +} + +.module-nav li:hover { + background-color: #000038; +} + +.nav>li>a:focus, .nav>li>a:hover { + text-decoration: none; + background-color: lightblue; +} +.sidebar .sidebar-nav.navbar-collapse { + padding-right: 0; + padding-left: 0; +} + +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #ff0000; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.1); + box-shadow: inset 0 1px 2px rgba(0,0,0,.1); +} + +.table>tbody>tr.active>td, .table>tbody>tr.active>th, .table>tbody>tr>td.active, .table>tbody>tr>th.active, .table>tfoot>tr.active>td, .table>tfoot>tr.active>th, .table>tfoot>tr>td.active, .table>tfoot>tr>th.active, .table>thead>tr.active>td, .table>thead>tr.active>th, .table>thead>tr>td.active, .table>thead>tr>th.active { + background-color: #000038; +} + +.sidebar ul li { + cursor: pointer; + border-bottom: 1px solid darkgold; + color:gold; +} + +.btn { + background-color: #000038; + font-family: sans-serif; +} + +.alert-info { + color: gold; + background-color: blue; + border-color: gold; +} + +p { + color: gold; + font-family: sans-serif; +} + +.text-muted { + color:darkgold; +} + +.h3 h3 { + color:gold; +} +.h2, h2 { + font-size: 30px; + color:gold; +} +.h1, h1 { + font-size: 30px; + color:gold; +} + +.btn-default { + color:gold; + border-color: gold; +} + +.btn-default:hover { + color: gold; + background-color: gold; + border-color: gold; +} + +.sidebar .active { + background-color: #13033a; +} + +@media(min-width:768px) { + .sidebar { + z-index: 1; + position: absolute; + width: 250px; + margin-top: 51px; + } + + .module-content { + position: inherit; + margin: 0 0 0 250px; + padding: 15px 30px; + border-left: 1px solid #000038; + } + + .navbar-top-links { + margin-left: 10px; + } +} + +.navbar-top-links { + margin-right: 5px; + margin-left: 10px; +} + +.navbar-top-links li { + display: inline-block; +} + +.form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { + background-color: #000038; + opacity: 1; + color: gold; +} + +.panel-footer { + padding: 10px 15px; + background-color: #000038; + border-top: 1px solid darkgold; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} + +textarea.form-control { + height: auto; + background-color: #000038; + opacity: 1; + color: gold; + font-family: sans-serif; +} +.navbar { + margin-bottom: 0; +} + +.navbar-top-links .dropdown-menu li { + display: block; +} + +.module-content { + padding: 15px 15px; + background-color: #000038; +} + +.pointer { + cursor: pointer; +} + +.dropdown-menu { + cursor: pointer; +} + +.dropdown-menu-top { + max-height: 300px !important; + overflow-y: auto !important; +} + +@media(max-width:768px) { + .dropdown-menu-top { + margin: auto !important; + position: absolute !important; + background-color: #000038 !important; + word-wrap: break-word !important; + border: 1px solid #000038 !important; + width: 300px !important; + max-width: 300px !important; + } + .dropdown-menu-top li a { + white-space: normal !important; + } + + .dropdown-menu-logout { + max-width: 50px !important; + } +} + +.login-logo { + margin: 0 auto; + padding-bottom: 10px; + max-width: 150px; +} + +.brand-logo { + content: url('/img/logo.png'); + max-height: 30px; + padding-bottom: 5px; +} + +.brand-text::after { + content: "pineapple"; + padding-top: 3px; + padding-left: 5px; + float: right; + color:gold; +} + +.alert-danger { + color: gold; + background-color: #000038; + border-color: gold; +} + +.panel-title { + background-color:#000038; + color: gold; +} + +.panel-default>.panel-heading+.panel-collapse>.panel-body { + border-top-color: gold; +} + +.panel-body { + background-color:#000038; + font-family:sans-serif; +} + +.panel-heading { + background-color:#000038; +} + +.panel-default>.panel-heading { + color: gold; + background-color: #000038; + border-color: gold; + border-bottom-color: gold; + border-bottom: gold; +} + +td { + background-color:#000038; +} +.nav { + background-color:#000038; +} +.sidebar-nav.navbar-collapse { + background-color:#000038; +} +.navbar-default{ + background-color:#000038; +} +.navbar-static-top { + background-color:#000038; + border-color: gold; +} +.input-group .form-control:first-child, .input-group-addon:first-child, .input-group-btn:first-child>.btn, .input-group-btn:first-child>.btn-group>.btn, .input-group-btn:first-child>.dropdown-toggle, .input-group-btn:last-child>.btn-group:not(:last-child)>.btn, .input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle) { + font-family: sans-serif; + background-color: gold; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + color: #000038; +} +.table>thead>tr>th { + vertical-align: bottom; + border-bottom: 2px solid gold; +} +.table>tbody>tr>td, .table>tbody>tr>th, .table>tfoot>tr>td, .table>tfoot>tr>th, .table>thead>tr>td, .table>thead>tr>th { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid gold; +} + +.modal-content { + position: relative; + background-color: #000038; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0,0,0,.2); + border-radius: 6px; + outline: 0; + -webkit-box-shadow: 0 3px 9px rgba(0,0,0,.5); + box-shadow: 0 3px 9px rgba(0,0,0,.5); +} \ No newline at end of file diff --git a/Themes/img/favicon-dark.ico b/Themes/img/favicon-dark.ico new file mode 100644 index 0000000000000000000000000000000000000000..afb37ed9021682534019f6b2d457eeb80cbdc355 GIT binary patch literal 533 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6n3BBRT^Rni_n+Ah2>S z4={E+nQh0wz!>i7;uxZFUiYHE_hm+g;}4&2|63v*GD}%C@bv_dj!4CpxRwRMP3(#9 zJzl-?)>tw9fnmqHIHMO2!u}mRVbFBo*`0516Rw|s{zHWEV(E*`$!8)AmaqFz?;3x6 z+S8t;)1r=To}(*UE4V2Cdey0(&zT*pr&Y_clshy=Bi5=UUO_QmvAUQh^kMk%6Iwu7Rnpp+$(HrIm@1 qm4TVAfr*uY!Q6*kacg+PDmVpb1B0ilpUXO@geCz0!{87A literal 0 HcmV?d00001 diff --git a/Themes/img/logo-dark.png b/Themes/img/logo-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..8e5f7711016ab1317e9d275f461629c672c6a473 GIT binary patch literal 26507 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 literal 0 HcmV?d00001 diff --git a/Themes/img/throbber-dark.gif b/Themes/img/throbber-dark.gif new file mode 100644 index 0000000000000000000000000000000000000000..7877db2f0ac1b3f63e8e0a110406424cb794f46f GIT binary patch literal 1739 zcmciDX-v~+90%}!?^6Cy3Z>lAKUl2D(Sc$CVFhfZ+#6Sm6e*TN4lM&}GbB>vD8-_j zHc){wjsl{MA}YfPh#;uw1jP+OKxL?N!7ZAQC9uM_=w9u`lD&9dKELGm$@lp_``9eH zJD&`YfnPB|XLmOUfZ)G+ZEP_CEQEnS5UW{)KHlk0K}x-3kL%3anf9{@ z$ltGf4>1|>JF(egiAJ3K33LH`6iX$BjWJ0B5Rtrq{@Ih$Zi1+a4^))Rl_ck;tu{?& z+HKjoKb}v9as(nM$`h8Hu^Y|_%?E;VlHioAV&BSAyn|!8YamJnDeBv{XDYbPc+199 zye{kgG3PH(6lICmx5ps7>_&Lv9Znr_j>wxAx_abdVVtG1cY1d2+5EzS4)FJ;4rl-e z5CMXAEr$5B>9r$;ZsUb?qDVF{4-v;n36ByQDrzox zP_uLo*~GGhTpeS9lt^lTjiEXle18qq)efbJxBa*lWc*vID*;Hm?1G98)}{E42wU1+ zTaHZC+AA_F`L7524RJ8WE(=qgj?3YdRA$exuc@CyKhC`p2 zMj6daT0ejBa_Oh#SLU1We;R#UZ|z_6q+GfqTJN1Km46ND##{fq?67ga(83e-B_>hKJ8DpDf}v%zopDHaUaFJ3 zBZgvY2NxohHe{0;G90-t(%K5cVGBvt29!9lRaKMts5XZ}YGf@6LkexX)B!cH8lgJs z8P+hm3vDeacT2+aCkESk>+pbb5hXmse(|4v#@E9!JPi zGyN53jq0EFWB2Lhx7gNzAqXSJ#Eo6fmWICBA%Y4IH(aVTd<3e58;G3rToziNHye2e zflmy;hh~Jl6#;jlnQ^VbF7#_+HD=q1*u~mJ_l{1VJv!{U*mq>uos(UhOR2Ntm%E}t zoljEue+WnRZ3rL3HGWStLMi3nNRWM*VCt4s)FNX{yUq&Y0L9xV5S8ytbxV5%BGi%2;# zmr~XmLaH_okXO1L%o{${lXi|@nX5T|yit~>EzH}PNa_t7EP-#Gk**P?G*W=F_m;>R z$5(cQbcA;hKr|Y>TVP)wb(P9U=YPkj2jMrDa0DsW59jYcWVj?N3FuFfTCy*;j8##? zo&5iygw}o&#@h0W5ZgDe(awKhnYjd(@l-L(*rJ1_>gl{g0b2$iHAe|2mftTfj;`2@ y;MIJM)C|Jv5NZ___w`;&`GN@MTj5<&!~V`km-hbB!`a%tx>vO0LAebK@caWW1$^=V literal 0 HcmV?d00001 diff --git a/Themes/js/module.js b/Themes/js/module.js new file mode 100644 index 0000000..d6d787c --- /dev/null +++ b/Themes/js/module.js @@ -0,0 +1,558 @@ +registerController("ThemesController", ['$api', '$scope','$window','$route', '$http', function ($api, $scope, $window, $route, $http) { + + getThemes(); + getCurrentTheme(); + backupFiles(); + + $scope.debug = true; + + $scope.themes = []; + $scope.themeToDelete = null; + $scope.themeDeleteValidation = ''; + $scope.messages = []; + $scope.newThemeName = ''; + $scope.throbber = true; + $scope.running = false; + $scope.current = ''; + $scope.library = true; + $scope.editor = true; + $scope.workshopTheme = {themeName: "", file: "", code: "", title: ""}; + $scope.editThemeFile = {themeName: "", file: "", code: ""}; + $scope.colors = ['dark', 'light', 'red', 'blue', 'green', 'purple', 'orange', 'yellow', 'pink']; + $scope.brightness = ['light', 'normal', 'dark']; + $scope.working = false; + + // Dark and White + $scope.throbbercontrast = true; // true == light -> false == dark + $scope.logocontrast = true; + $scope.faviconcontrast = true; + $scope.reconcontrast = true; + $scope.logocontrastText = 'light'; + $scope.faviconcontrastText = 'light'; + $scope.throbbercontrastText = 'light'; + + // Collor and brightness + $scope.allcontrastText = 'light'; + $scope.allcontrastBrightness = 'normal'; + $scope.dashboardcontrastText = 'light'; + $scope.dashboardcontrastBrightness = 'normal'; + $scope.reconcontrastText = 'light'; + $scope.reconcontrastBrightness = 'normal'; + $scope.profilingcontrastText = 'light'; + $scope.profilingcontrastBrightness = 'normal'; + $scope.clientscontrastText = 'light'; + $scope.clientscontrastBrightness = 'normal'; + $scope.modulescontrastText = 'light'; + $scope.modulescontrastBrightness = 'normal'; + $scope.filterscontrastText = 'light'; + $scope.filterscontrastBrightness = 'normal'; + $scope.pineapcontrastText = 'light'; + $scope.pineapcontrastBrightness = 'normal'; + $scope.trackingcontrastText = 'light'; + $scope.trackingcontrastBrightness = 'normal'; + $scope.loggingcontrastText = 'light'; + $scope.loggingcontrastBrightness = 'normal'; + $scope.reportingcontrastText = 'light'; + $scope.reportingcontrastBrightness = 'normal'; + $scope.networkingcontrastText = 'light'; + $scope.networkingcontrastBrightness = 'normal'; + $scope.configurationcontrastText = 'light'; + $scope.configurationcontrastBrightness = 'normal'; + $scope.advancedcontrastText = 'light'; + $scope.advancedcontrastBrightness = 'normal'; + $scope.helpcontrastText = 'light'; + $scope.helpcontrastBrightness = 'normal'; + + $scope.switchOn = { + "position" : "relative", + "display" : "block", + "width" : "50px", + "height" : "25px", + "cursor" : "pointer", + "border" : "2px solid darkgray", + "background-color" : "darkgray", + "border-radius" : "40px" + } + + $scope.switchOff = { + "position" : "relative", + "display" : "block", + "width" : "50px", + "height" : "25px", + "cursor" : "pointer", + "border" : "2px solid darkgray", + "background-color" : "white", + "border-radius" : "40px" + } + $scope.selectOptions = { + "position" : "relative", + "display" : "block", + "width" : "100px", + "height" : "25px", + "cursor" : "pointer", + "border" : "2px solid darkgray", + "background-color" : "white", + "color" : "black", + "border-radius" : "40px" + } + $scope.changeThrobber = function(){ + $scope.throbbercontrast = !$scope.throbbercontrast; + $scope.throbbercontrastText = 'light'; + if (!$scope.throbbercontrast) { + $scope.throbbercontrastText = 'dark'; + } + $api.request({ + module: "Themes", + action: "replaceImage", + img: 'Throbber', + light: $scope.throbbercontrast + }, function(response) { + $scope.sendMessage("Throbber", "set to " + $scope.throbbercontrastText); + log("changeThrobber", response.message); + }); + }; + + $scope.changeLogo = function(){ + $scope.logocontrast = !$scope.logocontrast; + $scope.logocontrastText = 'light'; + if (!$scope.logocontrast) { + $scope.logocontrastText = 'dark'; + } + $api.request({ + module: "Themes", + action: "replaceImage", + img: 'Logo', + light: $scope.logocontrast + }, function(response) { + $scope.sendMessage("Logo", "set to " + $scope.logocontrastText); + log("changeLogo", response.message); + }); + }; + + $scope.changeFavicon = function(){ + $scope.faviconcontrast = !$scope.faviconcontrast; + $scope.faviconcontrastText = 'light'; + if (!$scope.faviconcontrast) { + $scope.faviconcontrastText = 'dark'; + } + $api.request({ + module: "Themes", + action: "replaceImage", + img: 'Icon', + light: $scope.faviconcontrast + }, function(response) { + $scope.sendMessage("Icon", "set to " + $scope.faviconcontrastText); + log("changeFavicon", response.message); + }); + }; + $scope.changeAllIcons = function(){ + $api.request({ + module: "Themes", + action: "replaceImage", + img: 'All', + color: $scope.allcontrastText, + brightness: $scope.allcontrastBrightness + }, function(response) { + for (msg in response) { + $scope.sendMessage("All Icons", "set to " + $scope.allcontrastText + "(" + allcontrastBrightness + ")"); + log("changeAllIcons", "Success? " + response.success + " " + response.message); + } + }); + }; + $scope.changeDashboard = function(){ + $api.request({ + module: "Themes", + action: "replaceImage", + img: 'Dashboard', + color: $scope.dashboardcontrastText, + brightness: $scope.dashboardcontrastBrightness + }, function(response) { + $scope.sendMessage("Dashboard Icon", "set to " + $scope.dashboardcontrastText + " (" + $scope.dashboardcontrastBrightness + ")"); + log("changeDashboard", "Success? " + response.success + " " + response.message); + }); + }; + $scope.changeRecon = function(){ + $api.request({ + module: "Themes", + action: "replaceImage", + img: 'Recon', + color: $scope.reconcontrastText, + brightness: $scope.reconcontrastBrightness + }, function(response) { + $scope.sendMessage("Recon Icon", "set to " + $scope.reconcontrastText + " (" + $scope.reconcontrastBrightness + ")"); + log("changeDashboard", "Success? " + response.success + " " + response.message); + }); + }; + $scope.changeProfiling = function(){ + $api.request({ + module: "Themes", + action: "replaceImage", + img: 'Profiling', + color: $scope.profilingcontrastText, + brightness: $scope.profilingcontrastBrightness + }, function(response) { + $scope.sendMessage("Profiling Icon", "set to " + $scope.profilingcontrastText + " (" + $scope.profilingcontrastBrightness + ")"); + log("changeDashboard", "Success? " + response.success + " " + response.message); + }); + }; + $scope.changeClients = function(){ + $api.request({ + module: "Themes", + action: "replaceImage", + img: 'Clients', + color: $scope.clientscontrastText, + brightness: $scope.clientscontrastBrightness + }, function(response) { + $scope.sendMessage("Clients Icon", "set to " + $scope.clientscontrastText + " (" + $scope.clientscontrastBrightness + ")"); + log("changeDashboard", "Success? " + response.success + " " + response.message); + }); + }; + $scope.changeModules = function(){ + $api.request({ + module: "Themes", + action: "replaceImage", + img: 'ModuleManager', + color: $scope.modulescontrastText, + brightness: $scope.modulescontrastBrightness + }, function(response) { + $scope.sendMessage("ModuleManager Icon", "set to " + $scope.modulescontrastText + " (" + $scope.modulescontrastBrightness + ")"); + log("changeDashboard", "Success? " + response.success + " " + response.message); + }); + }; + $scope.changeFilters = function(){ + $api.request({ + module: "Themes", + action: "replaceImage", + img: 'Filters', + color: $scope.filterscontrastText, + brightness: $scope.filterscontrastBrightness + }, function(response) { + $scope.sendMessage("Filters Icon", "set to " + $scope.filterscontrastText + " (" + $scope.filterscontrastBrightness + ")"); + log("changeDashboard", "Success? " + response.success + " " + response.message); + }); + }; + $scope.changePineap = function(){ + $api.request({ + module: "Themes", + action: "replaceImage", + img: 'PineAP', + color: $scope.pineapcontrastText, + brightness: $scope.pineapcontrastBrightness + }, function(response) { + $scope.sendMessage("Filters Icon", "set to " + $scope.pineapcontrastText + " (" + $scope.pineapcontrastBrightness + ")"); + log("changeDashboard", "Success? " + response.success + " " + response.message); + }); + }; + $scope.changeTracking = function(){ + $api.request({ + module: "Themes", + action: "replaceImage", + img: 'Tracking', + color: $scope.trackingcontrastText, + brightness: $scope.trackingcontrastBrightness + }, function(response) { + $scope.sendMessage("Tracking Icon", "set to " + $scope.trackingcontrastText + " (" + $scope.trackingcontrastBrightness + ")"); + log("changeDashboard", "Success? " + response.success + " " + response.message); + }); + }; + $scope.changeLogging = function(){ + $api.request({ + module: "Themes", + action: "replaceImage", + img: 'Logging', + color: $scope.loggingcontrastText, + brightness: $scope.loggingcontrastBrightness + }, function(response) { + $scope.sendMessage("Logging Icon", "set to " + $scope.loggingcontrastText + " (" + $scope.loggingcontrastBrightness + ")"); + log("changeDashboard", "Success? " + response.success + " " + response.message); + }); + }; + $scope.changeReporting = function(){ + $api.request({ + module: "Themes", + action: "replaceImage", + img: 'Reporting', + color: $scope.reportingcontrastText, + brightness: $scope.reportingcontrastBrightness + }, function(response) { + $scope.sendMessage("Reporting Icon", "set to " + $scope.reportingcontrastText + " (" + $scope.reportingcontrastBrightness + ")"); + log("changeDashboard", "Success? " + response.success + " " + response.message); + }); + }; + $scope.changeNetworking = function(){ + $api.request({ + module: "Themes", + action: "replaceImage", + img: 'Networking', + color: $scope.networkingcontrastText, + brightness: $scope.networkingcontrastBrightness + }, function(response) { + $scope.sendMessage("Networking Icon", "set to " + $scope.networkingcontrastText + " (" + $scope.networkingcontrastBrightness + ")"); + log("changeDashboard", "Success? " + response.success + " " + response.message); + }); + }; + $scope.changeConfiguration = function(){ + $api.request({ + module: "Themes", + action: "replaceImage", + img: 'Configuration', + color: $scope.configurationcontrastText, + brightness: $scope.configurationcontrastBrightness + }, function(response) { + $scope.sendMessage("Configuration Icon", "set to " + $scope.configurationcontrastText + " (" + $scope.configurationcontrastBrightness + ")"); + log("changeDashboard", "Success? " + response.success + " " + response.message); + }); + }; + $scope.changeAdvanced = function(){ + $api.request({ + module: "Themes", + action: "replaceImage", + img: 'Advanced', + color: $scope.advancedcontrastText, + brightness: $scope.advancedcontrastBrightness + }, function(response) { + $scope.sendMessage("Advanced Icon", "set to " + $scope.advancedcontrastText + " (" + $scope.advancedcontrastBrightness + ")"); + log("changeDashboard", "Success? " + response.success + " " + response.message); + }); + }; + $scope.changeHelp = function(){ + $api.request({ + module: "Themes", + action: "replaceImage", + img: 'Help', + color: $scope.helpcontrastText, + brightness: $scope.helpcontrastBrightness + }, function(response) { + $scope.sendMessage("Help Icon", "set to " + $scope.helpcontrastText + " (" + $scope.helpcontrastBrightness + ")"); + log("changeDashboard", "Success? " + response.success + " " + response.message); + }); + }; + function log(fn, message) { + if ($scope.debug === true) { + console.log("fn[" + fn + "]-> " + message); + } + }; + function backupFiles() { + $api.request({ + module: "Themes", + action: "backupFiles" + }, function(response) { + log('backupFiles', response.message); + for (i=0; i + +
+
+ +
+
+ + + + +
{{ control.title }} +
Current Theme: {{ current }}
+
+ Refresh

+ + + +
+
+
+
+
+ +
+
+

No Messages...

+ Clear + All + + + + +
+
+
{{ message.title }} Dismiss
+

{{ message.msg }}

+
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
IconColorBrightness
Throbber {{ throbbercontrastText }} N/A
Logo {{ logocontrastText }} N/A
Favicon {{ faviconcontrastText }} N/A
Dashboard {{ dashboardcontrastText }} {{ dashboardcontrastBrightness }}
Recon {{ reconcontrastText }} {{ reconcontrastBrightness }}
Profiling {{ profilingcontrastText }} {{ profilingcontrastBrightness }}
Clients {{ clientscontrastText }} {{ clientscontrastBrightness }}
Modules {{ modulescontrastText }} {{ modulescontrastBrightness }}
Filters {{ filterscontrastText }} {{ filterscontrastBrightness }}
PineAP {{ pineapcontrastText }} {{ pineapcontrastBrightness }}
Tracking {{ trackingcontrastText }} {{ trackingcontrastBrightness }}
Logging {{ loggingcontrastText }} {{ loggingcontrastBrightness }}
Reporting {{ reportingcontrastText }} {{ reportingcontrastBrightness }}
Networking {{ networkingcontrastText }} {{ networkingcontrastBrightness }}
Configuration {{ configurationcontrastText }} {{ configurationcontrastBrightness }}
Advanced {{ advancedcontrastText }} {{ advancedcontrastBrightness }}
Help {{ helpcontrastText }} {{ helpcontrastBrightness }}
+
+
+
+
+ +
+
+ +
+
+
Themes
+
+
+
+
+ + + + +
+
+
+
+ + + + + + + + + + + + + + + +
Theme NameLocationActivateDelete
{{ theme.title + }}{{ theme.storage }}Activate + Delete +
+
+
+ +
+

+ No Themes in Library to Display. +

+
+ +
+ +
+

Theme Editor | {{ workshopTheme.themeName }}

+ +
+ + + + + + + + + +
CSS Editor
+
+ +
+
+
+
+ +
+
+
Icons
+
+
+
+
+

Generic

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeImageLightDark
+ Throbber + + + +
+
+
+
+
+
+ Logo + + + +
+
+
+
+
+
+ Icon + + + +
+
+
+
+
+
+
+

Modules

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeImageColorBrightness
+ ALL + + + + + + + +
+ Dashboard + + + + + + + + +
Recon + + +
Profiling + + +
Clients + + +
Modules + + +
Filters + + +
PineAP + + +
Tracking + + +
Logging + + +
Reporting + + +
Networking + + +
Configuration + + +
Advanced + + +
Help + + +
+ +
+
+
+ + +
+
+
How To
+
+
+
+
    +
  • Share your new theme
  • +
      +
    • Navigate to the wifi pineapple modules repository
    • +
    • Add your theme .css file in the css directory of this module
    • +
    • Create a new pull request
    • +
    • Update the version number
    • +
    • Update the module version in module.info
    • +
    +
+
    +
  • Fix Images appearing broken/unable to be loaded
  • +
      +
    • Refresh the page
    • +
    • If the problem persists, see "Submit a Bug"
    • +
    +
+
    +
  • Fix a messy looking page after a save/refresh
  • +
      +
    • Reselect 'Activate' beside your theme
    • +
    • Refresh the page
    • +
    +
+
    +
  • Select the proper Image type
  • +
      +
    • Beside each Image type are two options "Light" and "Dark"
    • +
    • "Dark" images are intended for dark backgrounds
    • +
    • "Light" images are intended for light backgrounds
    • +
    +
+
    +
  • Use a theme once without a module
  • +
      +
    • Navigate to https://github.com/kbeflo/pineapple-themes
    • +
    • Select a theme and run the install.sh script
    • +
    +
+
    +
  • Submit a Bug
  • +
      +
    • Navigate to https://github.com/hak5/wifipineapple-modules/issues
    • +
    • Select the "New Issue" button
    • +
    • Tag @trashbo4t in your issue description
    • +
    +
+
+
+
+ + +
+ +
+
+
    +
  • 1.0
  • +
      +
    • Pending release of 1.0
    • +
    +
+
+
+
+
+
+ +
diff --git a/Themes/module.info b/Themes/module.info new file mode 100644 index 0000000..54f518f --- /dev/null +++ b/Themes/module.info @@ -0,0 +1,6 @@ +{ + "title": "Themes", + "description": "Download, create, and share custom themes! With cross domain persistence and custom icon color adjustment", + "version": "1.0", + "author": "trashbo4t" +} diff --git a/Themes/module_icon.svg b/Themes/module_icon.svg new file mode 100644 index 0000000..bdef138 --- /dev/null +++ b/Themes/module_icon.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 5068e0faa97fd980e98ea9a4ac4d9b02a16ba1f1 Mon Sep 17 00:00:00 2001 From: trashbo4t Date: Sun, 20 May 2018 05:40:18 -0400 Subject: [PATCH 15/28] auto refresh screen on image update/activate --- Themes/api/module.php | 14 +-- Themes/js/module.js | 275 +++++++++++++++++++++--------------------- Themes/module.html | 3 +- Themes/module.info | 4 +- 4 files changed, 150 insertions(+), 146 deletions(-) diff --git a/Themes/api/module.php b/Themes/api/module.php index a0ee183..c173cfb8 100644 --- a/Themes/api/module.php +++ b/Themes/api/module.php @@ -248,9 +248,9 @@ class Themes extends Module return true; } /* - * searchAndReplaceFile - * $s -> substring to find - * return: true or false showing succcessful string replacement + * searchAndReplaceFile + * $s -> substring to find + * return: true or false showing succcessful string replacement */ public function searchAndReplaceFile($f, $s) { @@ -258,8 +258,8 @@ class Themes extends Module return (exec("sed -i 's/fill:\(.*\);/fill:#{$s};/g' $f") == 0); } /* - * setCurrentTheme - * $theme -> modify CURRENT_CSS file with new theme + * setCurrentTheme + * $theme -> modify CURRENT_CSS file with new theme */ public function setCurrentTheme($theme) { @@ -267,8 +267,8 @@ class Themes extends Module exec('echo '.$theme.' > /pineapple/modules/Themes/css/CURRENT_CSS'); } /* - * getCurrentTheme - * return current theme, and all parameters for icon colors/brightness + * getCurrentTheme + * return current theme, and all parameters for icon colors/brightness */ public function getCurrentTheme() { diff --git a/Themes/js/module.js b/Themes/js/module.js index d6d787c..7f5bb04 100644 --- a/Themes/js/module.js +++ b/Themes/js/module.js @@ -4,34 +4,31 @@ registerController("ThemesController", ['$api', '$scope','$window','$route', '$h getCurrentTheme(); backupFiles(); - $scope.debug = true; - - $scope.themes = []; - $scope.themeToDelete = null; + $scope.debug = false; + $scope.themes = []; + $scope.themeToDelete = null; $scope.themeDeleteValidation = ''; - $scope.messages = []; - $scope.newThemeName = ''; - $scope.throbber = true; - $scope.running = false; - $scope.current = ''; - $scope.library = true; - $scope.editor = true; - $scope.workshopTheme = {themeName: "", file: "", code: "", title: ""}; - $scope.editThemeFile = {themeName: "", file: "", code: ""}; - $scope.colors = ['dark', 'light', 'red', 'blue', 'green', 'purple', 'orange', 'yellow', 'pink']; - $scope.brightness = ['light', 'normal', 'dark']; - $scope.working = false; - + $scope.messages = []; + $scope.newThemeName = ''; + $scope.throbber = true; + $scope.running = false; + $scope.current = ''; + $scope.library = true; + $scope.editor = true; + $scope.workshopTheme = {themeName: "", file: "", code: "", title: ""}; + $scope.editThemeFile = {themeName: "", file: "", code: ""}; + $scope.colors = ['dark', 'light', 'red', 'blue', 'green', 'purple', 'orange', 'yellow', 'pink']; + $scope.brightness = ['light', 'normal', 'dark']; + $scope.working = false; // Dark and White - $scope.throbbercontrast = true; // true == light -> false == dark - $scope.logocontrast = true; - $scope.faviconcontrast = true; - $scope.reconcontrast = true; - $scope.logocontrastText = 'light'; - $scope.faviconcontrastText = 'light'; + $scope.throbbercontrast = true; // true == light -> false == dark + $scope.logocontrast = true; + $scope.faviconcontrast = true; + $scope.reconcontrast = true; + $scope.logocontrastText = 'light'; + $scope.faviconcontrastText = 'light'; $scope.throbbercontrastText = 'light'; - - // Collor and brightness + // Color and brightness $scope.allcontrastText = 'light'; $scope.allcontrastBrightness = 'normal'; $scope.dashboardcontrastText = 'light'; @@ -51,8 +48,8 @@ registerController("ThemesController", ['$api', '$scope','$window','$route', '$h $scope.trackingcontrastText = 'light'; $scope.trackingcontrastBrightness = 'normal'; $scope.loggingcontrastText = 'light'; - $scope.loggingcontrastBrightness = 'normal'; - $scope.reportingcontrastText = 'light'; + $scope.loggingcontrastBrightness = 'normal'; + $scope.reportingcontrastText = 'light'; $scope.reportingcontrastBrightness = 'normal'; $scope.networkingcontrastText = 'light'; $scope.networkingcontrastBrightness = 'normal'; @@ -60,9 +57,8 @@ registerController("ThemesController", ['$api', '$scope','$window','$route', '$h $scope.configurationcontrastBrightness = 'normal'; $scope.advancedcontrastText = 'light'; $scope.advancedcontrastBrightness = 'normal'; - $scope.helpcontrastText = 'light'; - $scope.helpcontrastBrightness = 'normal'; - + $scope.helpcontrastText = 'light'; + $scope.helpcontrastBrightness = 'normal'; $scope.switchOn = { "position" : "relative", "display" : "block", @@ -73,7 +69,6 @@ registerController("ThemesController", ['$api', '$scope','$window','$route', '$h "background-color" : "darkgray", "border-radius" : "40px" } - $scope.switchOff = { "position" : "relative", "display" : "block", @@ -109,9 +104,9 @@ registerController("ThemesController", ['$api', '$scope','$window','$route', '$h }, function(response) { $scope.sendMessage("Throbber", "set to " + $scope.throbbercontrastText); log("changeThrobber", response.message); + $window.location.reload(); }); }; - $scope.changeLogo = function(){ $scope.logocontrast = !$scope.logocontrast; $scope.logocontrastText = 'light'; @@ -126,6 +121,7 @@ registerController("ThemesController", ['$api', '$scope','$window','$route', '$h }, function(response) { $scope.sendMessage("Logo", "set to " + $scope.logocontrastText); log("changeLogo", response.message); + $window.location.reload(); }); }; @@ -143,6 +139,7 @@ registerController("ThemesController", ['$api', '$scope','$window','$route', '$h }, function(response) { $scope.sendMessage("Icon", "set to " + $scope.faviconcontrastText); log("changeFavicon", response.message); + $window.location.reload(); }); }; $scope.changeAllIcons = function(){ @@ -154,9 +151,11 @@ registerController("ThemesController", ['$api', '$scope','$window','$route', '$h brightness: $scope.allcontrastBrightness }, function(response) { for (msg in response) { - $scope.sendMessage("All Icons", "set to " + $scope.allcontrastText + "(" + allcontrastBrightness + ")"); + $scope.sendMessage("All Icons", "set to " + $scope.allcontrastText + "(" + $scope.allcontrastBrightness + ")"); log("changeAllIcons", "Success? " + response.success + " " + response.message); } + $route.reload(); + $window.location.reload(); }); }; $scope.changeDashboard = function(){ @@ -169,6 +168,7 @@ registerController("ThemesController", ['$api', '$scope','$window','$route', '$h }, function(response) { $scope.sendMessage("Dashboard Icon", "set to " + $scope.dashboardcontrastText + " (" + $scope.dashboardcontrastBrightness + ")"); log("changeDashboard", "Success? " + response.success + " " + response.message); + $window.location.reload(); }); }; $scope.changeRecon = function(){ @@ -181,6 +181,7 @@ registerController("ThemesController", ['$api', '$scope','$window','$route', '$h }, function(response) { $scope.sendMessage("Recon Icon", "set to " + $scope.reconcontrastText + " (" + $scope.reconcontrastBrightness + ")"); log("changeDashboard", "Success? " + response.success + " " + response.message); + $window.location.reload(); }); }; $scope.changeProfiling = function(){ @@ -193,6 +194,7 @@ registerController("ThemesController", ['$api', '$scope','$window','$route', '$h }, function(response) { $scope.sendMessage("Profiling Icon", "set to " + $scope.profilingcontrastText + " (" + $scope.profilingcontrastBrightness + ")"); log("changeDashboard", "Success? " + response.success + " " + response.message); + $window.location.reload(); }); }; $scope.changeClients = function(){ @@ -205,6 +207,7 @@ registerController("ThemesController", ['$api', '$scope','$window','$route', '$h }, function(response) { $scope.sendMessage("Clients Icon", "set to " + $scope.clientscontrastText + " (" + $scope.clientscontrastBrightness + ")"); log("changeDashboard", "Success? " + response.success + " " + response.message); + $window.location.reload(); }); }; $scope.changeModules = function(){ @@ -217,6 +220,7 @@ registerController("ThemesController", ['$api', '$scope','$window','$route', '$h }, function(response) { $scope.sendMessage("ModuleManager Icon", "set to " + $scope.modulescontrastText + " (" + $scope.modulescontrastBrightness + ")"); log("changeDashboard", "Success? " + response.success + " " + response.message); + $window.location.reload(); }); }; $scope.changeFilters = function(){ @@ -229,6 +233,7 @@ registerController("ThemesController", ['$api', '$scope','$window','$route', '$h }, function(response) { $scope.sendMessage("Filters Icon", "set to " + $scope.filterscontrastText + " (" + $scope.filterscontrastBrightness + ")"); log("changeDashboard", "Success? " + response.success + " " + response.message); + $window.location.reload(); }); }; $scope.changePineap = function(){ @@ -241,6 +246,7 @@ registerController("ThemesController", ['$api', '$scope','$window','$route', '$h }, function(response) { $scope.sendMessage("Filters Icon", "set to " + $scope.pineapcontrastText + " (" + $scope.pineapcontrastBrightness + ")"); log("changeDashboard", "Success? " + response.success + " " + response.message); + $window.location.reload(); }); }; $scope.changeTracking = function(){ @@ -253,6 +259,7 @@ registerController("ThemesController", ['$api', '$scope','$window','$route', '$h }, function(response) { $scope.sendMessage("Tracking Icon", "set to " + $scope.trackingcontrastText + " (" + $scope.trackingcontrastBrightness + ")"); log("changeDashboard", "Success? " + response.success + " " + response.message); + $window.location.reload(); }); }; $scope.changeLogging = function(){ @@ -265,6 +272,7 @@ registerController("ThemesController", ['$api', '$scope','$window','$route', '$h }, function(response) { $scope.sendMessage("Logging Icon", "set to " + $scope.loggingcontrastText + " (" + $scope.loggingcontrastBrightness + ")"); log("changeDashboard", "Success? " + response.success + " " + response.message); + $window.location.reload(); }); }; $scope.changeReporting = function(){ @@ -277,6 +285,7 @@ registerController("ThemesController", ['$api', '$scope','$window','$route', '$h }, function(response) { $scope.sendMessage("Reporting Icon", "set to " + $scope.reportingcontrastText + " (" + $scope.reportingcontrastBrightness + ")"); log("changeDashboard", "Success? " + response.success + " " + response.message); + $window.location.reload(); }); }; $scope.changeNetworking = function(){ @@ -289,6 +298,7 @@ registerController("ThemesController", ['$api', '$scope','$window','$route', '$h }, function(response) { $scope.sendMessage("Networking Icon", "set to " + $scope.networkingcontrastText + " (" + $scope.networkingcontrastBrightness + ")"); log("changeDashboard", "Success? " + response.success + " " + response.message); + $window.location.reload(); }); }; $scope.changeConfiguration = function(){ @@ -301,6 +311,7 @@ registerController("ThemesController", ['$api', '$scope','$window','$route', '$h }, function(response) { $scope.sendMessage("Configuration Icon", "set to " + $scope.configurationcontrastText + " (" + $scope.configurationcontrastBrightness + ")"); log("changeDashboard", "Success? " + response.success + " " + response.message); + $window.location.reload(); }); }; $scope.changeAdvanced = function(){ @@ -313,6 +324,7 @@ registerController("ThemesController", ['$api', '$scope','$window','$route', '$h }, function(response) { $scope.sendMessage("Advanced Icon", "set to " + $scope.advancedcontrastText + " (" + $scope.advancedcontrastBrightness + ")"); log("changeDashboard", "Success? " + response.success + " " + response.message); + $window.location.reload(); }); }; $scope.changeHelp = function(){ @@ -325,6 +337,7 @@ registerController("ThemesController", ['$api', '$scope','$window','$route', '$h }, function(response) { $scope.sendMessage("Help Icon", "set to " + $scope.helpcontrastText + " (" + $scope.helpcontrastBrightness + ")"); log("changeDashboard", "Success? " + response.success + " " + response.message); + $window.location.reload(); }); }; function log(fn, message) { @@ -334,84 +347,83 @@ registerController("ThemesController", ['$api', '$scope','$window','$route', '$h }; function backupFiles() { $api.request({ - module: "Themes", - action: "backupFiles" - }, function(response) { - log('backupFiles', response.message); - for (i=0; iConfiguration {{ configurationcontrastText }} {{ configurationcontrastBrightness }} Advanced {{ advancedcontrastText }} {{ advancedcontrastBrightness }} Help {{ helpcontrastText }} {{ helpcontrastBrightness }} - - + diff --git a/Themes/module.info b/Themes/module.info index 54f518f..b886385 100644 --- a/Themes/module.info +++ b/Themes/module.info @@ -1,6 +1,6 @@ { "title": "Themes", - "description": "Download, create, and share custom themes! With cross domain persistence and custom icon color adjustment", + "description": "Create or download custom themes!", "version": "1.0", "author": "trashbo4t" -} +} \ No newline at end of file From 4d9d24b8b1ff09c216ed4bd626ea8a88ce6edf36 Mon Sep 17 00:00:00 2001 From: trashbo4t Date: Sun, 20 May 2018 06:24:21 -0400 Subject: [PATCH 16/28] New Themes and minor html tweaks --- Themes/css/evilrose.css | 478 +++++++++++++++++++++++++++++++ Themes/css/hackerblues.css | 478 +++++++++++++++++++++++++++++++ Themes/css/halflife-inverted.css | 478 +++++++++++++++++++++++++++++++ Themes/css/halflife.css | 478 +++++++++++++++++++++++++++++++ Themes/js/module.js | 1 + Themes/module.html | 46 +-- 6 files changed, 1928 insertions(+), 31 deletions(-) create mode 100644 Themes/css/evilrose.css create mode 100644 Themes/css/hackerblues.css create mode 100644 Themes/css/halflife-inverted.css create mode 100644 Themes/css/halflife.css diff --git a/Themes/css/evilrose.css b/Themes/css/evilrose.css new file mode 100644 index 0000000..cf7fd00 --- /dev/null +++ b/Themes/css/evilrose.css @@ -0,0 +1,478 @@ +.truncated { + text-overflow: ellipsis; + overflow: hidden +} + +a { + color:#ff0066; + font-family: monospace; +} + +i { + color: #ff0066; + font: monospace; +} + +b, strong { + font-weight: 700; + font-family: monospace; +} + +.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { + font-family: monospace; + font-weight: 500; + line-height: 1.1; + color: inherit; +} + +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: 400; + line-height: 1; + color: #ff0066; + text-align: center; + background-color: black; + border: 1px solid #ff0066; + border-radius: 4px; +} + +.panel-default { + border-color: #ff0066; +} + +.panel>.panel-body+.table, .panel>.panel-body+.table-responsive, .panel>.table+.panel-body, .panel>.table-responsive+.panel-body { + border-top: 1px solid #ff0066; +} + +.dropdown-menu>li>a { + display:block; + padding: 3px 20px; + clear: both; + font-weight: 400; + line-height: 1.42857143; + color: #ff0066; + white-space:nowrap; + background-color: black; + border: #ff0066; +} + +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: #ff0066; + background-color: black; + background-image: none; + border: 1px solid #ff0066; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; +} + +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-bottom: 1px solid #ff0066; + border-top: 1px solid #ff0066; +} + +table { + background-color: black; +} + +.table-responsive { + min-height: .01%; + overflow-x: auto; + background-color: black; +} + +* { + color: #ff0066; + border-color: #ff0066; + border-top: #ff0066; +} +.uppercase { + text-transform: uppercase; +} + +.table-layout-fixed { + table-layout: fixed; +} + +.module-icon { + display: inline; + height: 24px; + width: 24px; +} + +.fixed-addon-width { + min-width: 70px; + text-align: left; +} + +.fixed-addon-width-2 { + min-width: 90px; + text-align: left; +} + + +.fixed-addon-width-3 { + min-width: 110px; + text-align: left; +} + +.fixed-width-200 { + min-width: 200px; +} + +.caret-reversed { + border-top-width: 0; + border-bottom: 4px solid black; +} + +.image-small-18 { + height: 18px; +} + +.center-text { + text-align: center; +} + +.scrollable-pre { + overflow: auto; + word-wrap: normal; + white-space: pre; +} + +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: #ff0066; + word-break: break-all; + word-wrap: break-word; + background-color: black; + border: 1px solid #ff0066; + border-radius: 4px; + font-family: monospace; +} + +.log-pre { + max-height: 300px; +} + +.btn-fixed-length { + width: 70px; +} + +.title-message { + margin-left: 10px; + padding-left: 5px; + padding-right: 5px; + height: 9px; + border-radius: 3px; +} + +.padding-left { + margin-left: 10px; +} + +.select-inline { + font-weight: normal; +} + +body { + background-color: black; +} + +.logout { + cursor: pointer; +} + +.module-nav li a { + margin-left: 30px; +} + +.module-nav li:hover { + background-color: black; +} + +.nav>li>a:focus, .nav>li>a:hover { + text-decoration: none; + background-color: #ff66a3; +} +.sidebar .sidebar-nav.navbar-collapse { + padding-right: 0; + padding-left: 0; +} + +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #ff0000; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.1); + box-shadow: inset 0 1px 2px rgba(0,0,0,.1); +} + +.table>tbody>tr.active>td, .table>tbody>tr.active>th, .table>tbody>tr>td.active, .table>tbody>tr>th.active, .table>tfoot>tr.active>td, .table>tfoot>tr.active>th, .table>tfoot>tr>td.active, .table>tfoot>tr>th.active, .table>thead>tr.active>td, .table>thead>tr.active>th, .table>thead>tr>td.active, .table>thead>tr>th.active { + background-color: black; +} + +.sidebar ul li { + cursor: pointer; + border-bottom: 1px solid #ff66a3; + color:#ff0066; +} + +.btn { + background-color: black; + font-family: monospace; +} + +.alert-info { + color: #ff0066; + background-color: black; + border-color: #ff0066; +} + +p { + color: #ff0066; + font-family: monospace; +} + +.text-muted { + color:#ff66a3; +} + +.h3 h3 { + color:#ff0066; +} +.h2, h2 { + font-size: 30px; + color:#ff0066; +} +.h1, h1 { + font-size: 30px; + color:#ff0066; +} + +.btn-default { + color:#ff0066; + border-color: #ff0066; +} + +.btn-default:hover { + color: #ff0066; + background-color: #ff0066; + border-color: #ff0066; +} + +.sidebar .active { + background-color: #13033a; +} + +@media(min-width:768px) { + .sidebar { + z-index: 1; + position: absolute; + width: 250px; + margin-top: 51px; + } + + .module-content { + position: inherit; + margin: 0 0 0 250px; + padding: 15px 30px; + border-left: 1px solid black; + } + + .navbar-top-links { + margin-left: 10px; + } +} + +.navbar-top-links { + margin-right: 5px; + margin-left: 10px; +} + +.navbar-top-links li { + display: inline-block; +} + +.form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { + background-color: black; + opacity: 1; + color: #ff0066; +} + +.panel-footer { + padding: 10px 15px; + background-color: black; + border-top: 1px solid #ff66a3; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} + +textarea.form-control { + height: auto; + background-color: black; + opacity: 1; + color: #ff0066; + font-family: monospace; +} +.navbar { + margin-bottom: 0; +} + +.navbar-top-links .dropdown-menu li { + display: block; +} + +.module-content { + padding: 15px 15px; + background-color: black; +} + +.pointer { + cursor: pointer; +} + +.dropdown-menu { + cursor: pointer; +} + +.dropdown-menu-top { + max-height: 300px !important; + overflow-y: auto !important; +} + +@media(max-width:768px) { + .dropdown-menu-top { + margin: auto !important; + position: absolute !important; + background-color: #000 !important; + word-wrap: break-word !important; + border: 1px solid #000 !important; + width: 300px !important; + max-width: 300px !important; + } + .dropdown-menu-top li a { + white-space: normal !important; + } + + .dropdown-menu-logout { + max-width: 50px !important; + } +} + +.login-logo { + margin: 0 auto; + padding-bottom: 10px; + max-width: 150px; +} + +.brand-logo { + content: url('/img/logo.png'); + max-height: 30px; + padding-bottom: 5px; +} + +.brand-text::after { + content: "pineapple"; + padding-top: 3px; + padding-left: 5px; + float: right; + color:#ff0066; +} + +.alert-danger { + color: #ff0066; + background-color: black; + border-color: #ff0066; +} + +.panel-title { + background-color:black; + color: #ff0066; +} + +.panel-default>.panel-heading+.panel-collapse>.panel-body { + border-top-color: #ff0066; +} + +.panel-body { + background-color:black; + font-family:monospace; +} + +.panel-heading { + background-color:black; +} + +.panel-default>.panel-heading { + color: #ff0066; + background-color: black; + border-color: #ff0066; + border-bottom-color: #ff0066; + border-bottom: #ff0066; +} + +td { + background-color:black; +} +.nav { + background-color:black; +} +.sidebar-nav.navbar-collapse { + background-color:black; +} +.navbar-default{ + background-color:black; +} +.navbar-static-top { + background-color:black; + border-color: #ff0066; +} +.input-group .form-control:first-child, .input-group-addon:first-child, .input-group-btn:first-child>.btn, .input-group-btn:first-child>.btn-group>.btn, .input-group-btn:first-child>.dropdown-toggle, .input-group-btn:last-child>.btn-group:not(:last-child)>.btn, .input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle) { + font-family: monospace; + background-color: #ff0066; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + color: black; +} +.table>thead>tr>th { + vertical-align: bottom; + border-bottom: 2px solid #ff0066; +} +.table>tbody>tr>td, .table>tbody>tr>th, .table>tfoot>tr>td, .table>tfoot>tr>th, .table>thead>tr>td, .table>thead>tr>th { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #ff0066; +} + +.modal-content { + position: relative; + background-color: black; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0,0,0,.2); + border-radius: 6px; + outline: 0; + -webkit-box-shadow: 0 3px 9px rgba(0,0,0,.5); + box-shadow: 0 3px 9px rgba(0,0,0,.5); +} \ No newline at end of file diff --git a/Themes/css/hackerblues.css b/Themes/css/hackerblues.css new file mode 100644 index 0000000..0845169 --- /dev/null +++ b/Themes/css/hackerblues.css @@ -0,0 +1,478 @@ +.truncated { + text-overflow: ellipsis; + overflow: hidden +} + +a { + color:#0066ff; + font-family: monospace; +} + +i { + color: #0066ff; + font: monospace; +} + +b, strong { + font-weight: 700; + font-family: monospace; +} + +.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { + font-family: monospace; + font-weight: 500; + line-height: 1.1; + color: inherit; +} + +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: 400; + line-height: 1; + color: #0066ff; + text-align: center; + background-color: black; + border: 1px solid #0066ff; + border-radius: 4px; +} + +.panel-default { + border-color: #0066ff; +} + +.panel>.panel-body+.table, .panel>.panel-body+.table-responsive, .panel>.table+.panel-body, .panel>.table-responsive+.panel-body { + border-top: 1px solid #0066ff; +} + +.dropdown-menu>li>a { + display:block; + padding: 3px 20px; + clear: both; + font-weight: 400; + line-height: 1.42857143; + color: #0066ff; + white-space:nowrap; + background-color: black; + border: #0066ff; +} + +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: #0066ff; + background-color: black; + background-image: none; + border: 1px solid #0066ff; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; +} + +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-bottom: 1px solid #0066ff; + border-top: 1px solid #0066ff; +} + +table { + background-color: black; +} + +.table-responsive { + min-height: .01%; + overflow-x: auto; + background-color: black; +} + +* { + color: #0066ff; + border-color: #0066ff; + border-top: #0066ff; +} +.uppercase { + text-transform: uppercase; +} + +.table-layout-fixed { + table-layout: fixed; +} + +.module-icon { + display: inline; + height: 24px; + width: 24px; +} + +.fixed-addon-width { + min-width: 70px; + text-align: left; +} + +.fixed-addon-width-2 { + min-width: 90px; + text-align: left; +} + + +.fixed-addon-width-3 { + min-width: 110px; + text-align: left; +} + +.fixed-width-200 { + min-width: 200px; +} + +.caret-reversed { + border-top-width: 0; + border-bottom: 4px solid black; +} + +.image-small-18 { + height: 18px; +} + +.center-text { + text-align: center; +} + +.scrollable-pre { + overflow: auto; + word-wrap: normal; + white-space: pre; +} + +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: #0066ff; + word-break: break-all; + word-wrap: break-word; + background-color: black; + border: 1px solid #0066ff; + border-radius: 4px; + font-family: monospace; +} + +.log-pre { + max-height: 300px; +} + +.btn-fixed-length { + width: 70px; +} + +.title-message { + margin-left: 10px; + padding-left: 5px; + padding-right: 5px; + height: 9px; + border-radius: 3px; +} + +.padding-left { + margin-left: 10px; +} + +.select-inline { + font-weight: normal; +} + +body { + background-color: black; +} + +.logout { + cursor: pointer; +} + +.module-nav li a { + margin-left: 30px; +} + +.module-nav li:hover { + background-color: black; +} + +.nav>li>a:focus, .nav>li>a:hover { + text-decoration: none; + background-color: #00004d; +} +.sidebar .sidebar-nav.navbar-collapse { + padding-right: 0; + padding-left: 0; +} + +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #ff0000; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.1); + box-shadow: inset 0 1px 2px rgba(0,0,0,.1); +} + +.table>tbody>tr.active>td, .table>tbody>tr.active>th, .table>tbody>tr>td.active, .table>tbody>tr>th.active, .table>tfoot>tr.active>td, .table>tfoot>tr.active>th, .table>tfoot>tr>td.active, .table>tfoot>tr>th.active, .table>thead>tr.active>td, .table>thead>tr.active>th, .table>thead>tr>td.active, .table>thead>tr>th.active { + background-color: black; +} + +.sidebar ul li { + cursor: pointer; + border-bottom: 1px solid #00004d; + color:#0066ff; +} + +.btn { + background-color: black; + font-family: monospace; +} + +.alert-info { + color: #0066ff; + background-color: black; + border-color: #0066ff; +} + +p { + color: #0066ff; + font-family: monospace; +} + +.text-muted { + color:blue; +} + +.h3 h3 { + color:#0066ff; +} +.h2, h2 { + font-size: 30px; + color:#0066ff; +} +.h1, h1 { + font-size: 30px; + color:#0066ff; +} + +.btn-default { + color:#0066ff; + border-color: #0066ff; +} + +.btn-default:hover { + color: #0066ff; + background-color: #0066ff; + border-color: #0066ff; +} + +.sidebar .active { + background-color: #13033a; +} + +@media(min-width:768px) { + .sidebar { + z-index: 1; + position: absolute; + width: 250px; + margin-top: 51px; + } + + .module-content { + position: inherit; + margin: 0 0 0 250px; + padding: 15px 30px; + border-left: 1px solid black; + } + + .navbar-top-links { + margin-left: 10px; + } +} + +.navbar-top-links { + margin-right: 5px; + margin-left: 10px; +} + +.navbar-top-links li { + display: inline-block; +} + +.form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { + background-color: black; + opacity: 1; + color: #0066ff; +} + +.panel-footer { + padding: 10px 15px; + background-color: black; + border-top: 1px solid #00004d; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} + +textarea.form-control { + height: auto; + background-color: black; + opacity: 1; + color: #0066ff; + font-family: monospace; +} +.navbar { + margin-bottom: 0; +} + +.navbar-top-links .dropdown-menu li { + display: block; +} + +.module-content { + padding: 15px 15px; + background-color: black; +} + +.pointer { + cursor: pointer; +} + +.dropdown-menu { + cursor: pointer; +} + +.dropdown-menu-top { + max-height: 300px !important; + overflow-y: auto !important; +} + +@media(max-width:768px) { + .dropdown-menu-top { + margin: auto !important; + position: absolute !important; + background-color: #000 !important; + word-wrap: break-word !important; + border: 1px solid #000 !important; + width: 300px !important; + max-width: 300px !important; + } + .dropdown-menu-top li a { + white-space: normal !important; + } + + .dropdown-menu-logout { + max-width: 50px !important; + } +} + +.login-logo { + margin: 0 auto; + padding-bottom: 10px; + max-width: 150px; +} + +.brand-logo { + content: url('/img/logo.png'); + max-height: 30px; + padding-bottom: 5px; +} + +.brand-text::after { + content: "pineapple"; + padding-top: 3px; + padding-left: 5px; + float: right; + color:#0066ff; +} + +.alert-danger { + color: #0066ff; + background-color: black; + border-color: #0066ff; +} + +.panel-title { + background-color:black; + color: #0066ff; +} + +.panel-default>.panel-heading+.panel-collapse>.panel-body { + border-top-color: #0066ff; +} + +.panel-body { + background-color:black; + font-family:monospace; +} + +.panel-heading { + background-color:black; +} + +.panel-default>.panel-heading { + color: #0066ff; + background-color: black; + border-color: #0066ff; + border-bottom-color: #0066ff; + border-bottom: #0066ff; +} + +td { + background-color:black; +} +.nav { + background-color:black; +} +.sidebar-nav.navbar-collapse { + background-color:black; +} +.navbar-default{ + background-color:black; +} +.navbar-static-top { + background-color:black; + border-color: #0066ff; +} +.input-group .form-control:first-child, .input-group-addon:first-child, .input-group-btn:first-child>.btn, .input-group-btn:first-child>.btn-group>.btn, .input-group-btn:first-child>.dropdown-toggle, .input-group-btn:last-child>.btn-group:not(:last-child)>.btn, .input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle) { + font-family: monospace; + background-color: #0066ff; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + color: black; +} +.table>thead>tr>th { + vertical-align: bottom; + border-bottom: 2px solid #0066ff; +} +.table>tbody>tr>td, .table>tbody>tr>th, .table>tfoot>tr>td, .table>tfoot>tr>th, .table>thead>tr>td, .table>thead>tr>th { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #0066ff; +} + +.modal-content { + position: relative; + background-color: black; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0,0,0,.2); + border-radius: 6px; + outline: 0; + -webkit-box-shadow: 0 3px 9px rgba(0,0,0,.5); + box-shadow: 0 3px 9px rgba(0,0,0,.5); +} \ No newline at end of file diff --git a/Themes/css/halflife-inverted.css b/Themes/css/halflife-inverted.css new file mode 100644 index 0000000..517cc38 --- /dev/null +++ b/Themes/css/halflife-inverted.css @@ -0,0 +1,478 @@ +.truncated { + text-overflow: ellipsis; + overflow: hidden +} + +a { + color:#991f00 ; + font-family: monospace; +} + +i { + color: #991f00 ; + font: monospace; +} + +b, strong { + font-weight: 700; + font-family: monospace; +} + +.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { + font-family: monospace; + font-weight: 500; + line-height: 1.1; + color: inherit; +} + +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: 400; + line-height: 1; + color: #991f00 ; + text-align: center; + background-color: black; + border: 1px solid #991f00 ; + border-radius: 4px; +} + +.panel-default { + border-color: #991f00 ; +} + +.panel>.panel-body+.table, .panel>.panel-body+.table-responsive, .panel>.table+.panel-body, .panel>.table-responsive+.panel-body { + border-top: 1px solid #991f00 ; +} + +.dropdown-menu>li>a { + display:block; + padding: 3px 20px; + clear: both; + font-weight: 400; + line-height: 1.42857143; + color: #991f00 ; + white-space:nowrap; + background-color: black; + border: #991f00 ; +} + +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: #991f00 ; + background-color: black; + background-image: none; + border: 1px solid #991f00 ; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; +} + +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-bottom: 1px solid #991f00 ; + border-top: 1px solid #991f00 ; +} + +table { + background-color: black; +} + +.table-responsive { + min-height: .01%; + overflow-x: auto; + background-color: black; +} + +* { + color: #991f00 ; + border-color: #991f00 ; + border-top: #991f00 ; +} +.uppercase { + text-transform: uppercase; +} + +.table-layout-fixed { + table-layout: fixed; +} + +.module-icon { + display: inline; + height: 24px; + width: 24px; +} + +.fixed-addon-width { + min-width: 70px; + text-align: left; +} + +.fixed-addon-width-2 { + min-width: 90px; + text-align: left; +} + + +.fixed-addon-width-3 { + min-width: 110px; + text-align: left; +} + +.fixed-width-200 { + min-width: 200px; +} + +.caret-reversed { + border-top-width: 0; + border-bottom: 4px solid black; +} + +.image-small-18 { + height: 18px; +} + +.center-text { + text-align: center; +} + +.scrollable-pre { + overflow: auto; + word-wrap: normal; + white-space: pre; +} + +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: #991f00 ; + word-break: break-all; + word-wrap: break-word; + background-color: black; + border: 1px solid #991f00 ; + border-radius: 4px; + font-family: monospace; +} + +.log-pre { + max-height: 300px; +} + +.btn-fixed-length { + width: 70px; +} + +.title-message { + margin-left: 10px; + padding-left: 5px; + padding-right: 5px; + height: 9px; + border-radius: 3px; +} + +.padding-left { + margin-left: 10px; +} + +.select-inline { + font-weight: normal; +} + +body { + background-color: black; +} + +.logout { + cursor: pointer; +} + +.module-nav li a { + margin-left: 30px; +} + +.module-nav li:hover { + background-color: black; +} + +.nav>li>a:focus, .nav>li>a:hover { + text-decoration: none; + background-color: #e62e00; +} +.sidebar .sidebar-nav.navbar-collapse { + padding-right: 0; + padding-left: 0; +} + +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: black; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.1); + box-shadow: inset 0 1px 2px rgba(0,0,0,.1); +} + +.table>tbody>tr.active>td, .table>tbody>tr.active>th, .table>tbody>tr>td.active, .table>tbody>tr>th.active, .table>tfoot>tr.active>td, .table>tfoot>tr.active>th, .table>tfoot>tr>td.active, .table>tfoot>tr>th.active, .table>thead>tr.active>td, .table>thead>tr.active>th, .table>thead>tr>td.active, .table>thead>tr>th.active { + background-color: black; +} + +.sidebar ul li { + cursor: pointer; + border-bottom: 1px solid #e62e00; + color:#991f00 ; +} + +.btn { + background-color: black; + font-family: monospace; +} + +.alert-info { + color: #991f00 ; + background-color: black; + border-color: #991f00 ; +} + +p { + color: #991f00 ; + font-family: monospace; +} + +.text-muted { + color:#e62e00; +} + +.h3 h3 { + color:#991f00 ; +} +.h2, h2 { + font-size: 30px; + color:#991f00 ; +} +.h1, h1 { + font-size: 30px; + color:#991f00 ; +} + +.btn-default { + color:#991f00 ; + border-color: #991f00 ; +} + +.btn-default:hover { + color: #991f00 ; + background-color: #991f00 ; + border-color: #991f00 ; +} + +.sidebar .active { + background-color: #13033a; +} + +@media(min-width:768px) { + .sidebar { + z-index: 1; + position: absolute; + width: 250px; + margin-top: 51px; + } + + .module-content { + position: inherit; + margin: 0 0 0 250px; + padding: 15px 30px; + border-left: 1px solid black; + } + + .navbar-top-links { + margin-left: 10px; + } +} + +.navbar-top-links { + margin-right: 5px; + margin-left: 10px; +} + +.navbar-top-links li { + display: inline-block; +} + +.form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { + background-color: black; + opacity: 1; + color: #991f00 ; +} + +.panel-footer { + padding: 10px 15px; + background-color: black; + border-top: 1px solid #e62e00; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} + +textarea.form-control { + height: auto; + background-color: black; + opacity: 1; + color: #991f00 ; + font-family: monospace; +} +.navbar { + margin-bottom: 0; +} + +.navbar-top-links .dropdown-menu li { + display: block; +} + +.module-content { + padding: 15px 15px; + background-color: black; +} + +.pointer { + cursor: pointer; +} + +.dropdown-menu { + cursor: pointer; +} + +.dropdown-menu-top { + max-height: 300px !important; + overflow-y: auto !important; +} + +@media(max-width:768px) { + .dropdown-menu-top { + margin: auto !important; + position: absolute !important; + background-color: black !important; + word-wrap: break-word !important; + border: 1px solid black !important; + width: 300px !important; + max-width: 300px !important; + } + .dropdown-menu-top li a { + white-space: normal !important; + } + + .dropdown-menu-logout { + max-width: 50px !important; + } +} + +.login-logo { + margin: 0 auto; + padding-bottom: 10px; + max-width: 150px; +} + +.brand-logo { + content: url('/img/logo.png'); + max-height: 30px; + padding-bottom: 5px; +} + +.brand-text::after { + content: "pineapple"; + padding-top: 3px; + padding-left: 5px; + float: right; + color:#991f00 ; +} + +.alert-danger { + color: #991f00 ; + background-color: black; + border-color: #991f00 ; +} + +.panel-title { + background-color:black; + color: #991f00 ; +} + +.panel-default>.panel-heading+.panel-collapse>.panel-body { + border-top-color: #991f00 ; +} + +.panel-body { + background-color:black; + font-family:monospace; +} + +.panel-heading { + background-color:black; +} + +.panel-default>.panel-heading { + color: #991f00 ; + background-color: black; + border-color: #991f00 ; + border-bottom-color: #991f00 ; + border-bottom: #991f00 ; +} + +td { + background-color:black; +} +.nav { + background-color:black; +} +.sidebar-nav.navbar-collapse { + background-color:black; +} +.navbar-default{ + background-color:black; +} +.navbar-static-top { + background-color:black; + border-color: #991f00 ; +} +.input-group .form-control:first-child, .input-group-addon:first-child, .input-group-btn:first-child>.btn, .input-group-btn:first-child>.btn-group>.btn, .input-group-btn:first-child>.dropdown-toggle, .input-group-btn:last-child>.btn-group:not(:last-child)>.btn, .input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle) { + font-family: monospace; + background-color: #991f00 ; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + color: black; +} +.table>thead>tr>th { + vertical-align: bottom; + border-bottom: 2px solid #991f00 ; +} +.table>tbody>tr>td, .table>tbody>tr>th, .table>tfoot>tr>td, .table>tfoot>tr>th, .table>thead>tr>td, .table>thead>tr>th { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #991f00 ; +} + +.modal-content { + position: relative; + background-color: black; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0,0,0,.2); + border-radius: 6px; + outline: 0; + -webkit-box-shadow: 0 3px 9px rgba(0,0,0,.5); + box-shadow: 0 3px 9px rgba(0,0,0,.5); +} \ No newline at end of file diff --git a/Themes/css/halflife.css b/Themes/css/halflife.css new file mode 100644 index 0000000..41d94c1 --- /dev/null +++ b/Themes/css/halflife.css @@ -0,0 +1,478 @@ +.truncated { + text-overflow: ellipsis; + overflow: hidden +} + +a { + color:black; + font-family: monospace; +} + +i { + color: black; + font: monospace; +} + +b, strong { + font-weight: 700; + font-family: monospace; +} + +.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { + font-family: monospace; + font-weight: 500; + line-height: 1.1; + color: inherit; +} + +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: 400; + line-height: 1; + color: black; + text-align: center; + background-color: #991f00; + border: 1px solid black; + border-radius: 4px; +} + +.panel-default { + border-color: black; +} + +.panel>.panel-body+.table, .panel>.panel-body+.table-responsive, .panel>.table+.panel-body, .panel>.table-responsive+.panel-body { + border-top: 1px solid black; +} + +.dropdown-menu>li>a { + display:block; + padding: 3px 20px; + clear: both; + font-weight: 400; + line-height: 1.42857143; + color: black; + white-space:nowrap; + background-color: #991f00; + border: black; +} + +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: black; + background-color: #991f00; + background-image: none; + border: 1px solid black; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; +} + +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-bottom: 1px solid black; + border-top: 1px solid black; +} + +table { + background-color: #991f00; +} + +.table-responsive { + min-height: .01%; + overflow-x: auto; + background-color: #991f00; +} + +* { + color: black; + border-color: black; + border-top: black; +} +.uppercase { + text-transform: uppercase; +} + +.table-layout-fixed { + table-layout: fixed; +} + +.module-icon { + display: inline; + height: 24px; + width: 24px; +} + +.fixed-addon-width { + min-width: 70px; + text-align: left; +} + +.fixed-addon-width-2 { + min-width: 90px; + text-align: left; +} + + +.fixed-addon-width-3 { + min-width: 110px; + text-align: left; +} + +.fixed-width-200 { + min-width: 200px; +} + +.caret-reversed { + border-top-width: 0; + border-bottom: 4px solid #991f00; +} + +.image-small-18 { + height: 18px; +} + +.center-text { + text-align: center; +} + +.scrollable-pre { + overflow: auto; + word-wrap: normal; + white-space: pre; +} + +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: black; + word-break: break-all; + word-wrap: break-word; + background-color: #991f00; + border: 1px solid black; + border-radius: 4px; + font-family: monospace; +} + +.log-pre { + max-height: 300px; +} + +.btn-fixed-length { + width: 70px; +} + +.title-message { + margin-left: 10px; + padding-left: 5px; + padding-right: 5px; + height: 9px; + border-radius: 3px; +} + +.padding-left { + margin-left: 10px; +} + +.select-inline { + font-weight: normal; +} + +body { + background-color: #991f00; +} + +.logout { + cursor: pointer; +} + +.module-nav li a { + margin-left: 30px; +} + +.module-nav li:hover { + background-color: #991f00; +} + +.nav>li>a:focus, .nav>li>a:hover { + text-decoration: none; + background-color: #e62e00; +} +.sidebar .sidebar-nav.navbar-collapse { + padding-right: 0; + padding-left: 0; +} + +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #991f00; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.1); + box-shadow: inset 0 1px 2px rgba(0,0,0,.1); +} + +.table>tbody>tr.active>td, .table>tbody>tr.active>th, .table>tbody>tr>td.active, .table>tbody>tr>th.active, .table>tfoot>tr.active>td, .table>tfoot>tr.active>th, .table>tfoot>tr>td.active, .table>tfoot>tr>th.active, .table>thead>tr.active>td, .table>thead>tr.active>th, .table>thead>tr>td.active, .table>thead>tr>th.active { + background-color: #991f00; +} + +.sidebar ul li { + cursor: pointer; + border-bottom: 1px solid #e62e00; + color:black; +} + +.btn { + background-color: #991f00; + font-family: monospace; +} + +.alert-info { + color: black; + background-color: #991f00; + border-color: black; +} + +p { + color: black; + font-family: monospace; +} + +.text-muted { + color:#e62e00; +} + +.h3 h3 { + color:black; +} +.h2, h2 { + font-size: 30px; + color:black; +} +.h1, h1 { + font-size: 30px; + color:black; +} + +.btn-default { + color:black; + border-color: black; +} + +.btn-default:hover { + color: black; + background-color: black; + border-color: black; +} + +.sidebar .active { + background-color: #13033a; +} + +@media(min-width:768px) { + .sidebar { + z-index: 1; + position: absolute; + width: 250px; + margin-top: 51px; + } + + .module-content { + position: inherit; + margin: 0 0 0 250px; + padding: 15px 30px; + border-left: 1px solid #991f00; + } + + .navbar-top-links { + margin-left: 10px; + } +} + +.navbar-top-links { + margin-right: 5px; + margin-left: 10px; +} + +.navbar-top-links li { + display: inline-block; +} + +.form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { + background-color: #991f00; + opacity: 1; + color: black; +} + +.panel-footer { + padding: 10px 15px; + background-color: #991f00; + border-top: 1px solid #e62e00; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} + +textarea.form-control { + height: auto; + background-color: #991f00; + opacity: 1; + color: black; + font-family: monospace; +} +.navbar { + margin-bottom: 0; +} + +.navbar-top-links .dropdown-menu li { + display: block; +} + +.module-content { + padding: 15px 15px; + background-color: #991f00; +} + +.pointer { + cursor: pointer; +} + +.dropdown-menu { + cursor: pointer; +} + +.dropdown-menu-top { + max-height: 300px !important; + overflow-y: auto !important; +} + +@media(max-width:768px) { + .dropdown-menu-top { + margin: auto !important; + position: absolute !important; + background-color: #991f00 !important; + word-wrap: break-word !important; + border: 1px solid #991f00 !important; + width: 300px !important; + max-width: 300px !important; + } + .dropdown-menu-top li a { + white-space: normal !important; + } + + .dropdown-menu-logout { + max-width: 50px !important; + } +} + +.login-logo { + margin: 0 auto; + padding-bottom: 10px; + max-width: 150px; +} + +.brand-logo { + content: url('/img/logo.png'); + max-height: 30px; + padding-bottom: 5px; +} + +.brand-text::after { + content: "pineapple"; + padding-top: 3px; + padding-left: 5px; + float: right; + color:black; +} + +.alert-danger { + color: black; + background-color: #991f00; + border-color: black; +} + +.panel-title { + background-color:#991f00; + color: black; +} + +.panel-default>.panel-heading+.panel-collapse>.panel-body { + border-top-color: black; +} + +.panel-body { + background-color:#991f00; + font-family:monospace; +} + +.panel-heading { + background-color:#991f00; +} + +.panel-default>.panel-heading { + color: black; + background-color: #991f00; + border-color: black; + border-bottom-color: black; + border-bottom: black; +} + +td { + background-color:#991f00; +} +.nav { + background-color:#991f00; +} +.sidebar-nav.navbar-collapse { + background-color:#991f00; +} +.navbar-default{ + background-color:#991f00; +} +.navbar-static-top { + background-color:#991f00; + border-color: black; +} +.input-group .form-control:first-child, .input-group-addon:first-child, .input-group-btn:first-child>.btn, .input-group-btn:first-child>.btn-group>.btn, .input-group-btn:first-child>.dropdown-toggle, .input-group-btn:last-child>.btn-group:not(:last-child)>.btn, .input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle) { + font-family: monospace; + background-color: black; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + color: #991f00; +} +.table>thead>tr>th { + vertical-align: bottom; + border-bottom: 2px solid black; +} +.table>tbody>tr>td, .table>tbody>tr>th, .table>tfoot>tr>td, .table>tfoot>tr>th, .table>thead>tr>td, .table>thead>tr>th { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid black; +} + +.modal-content { + position: relative; + background-color: #991f00; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0,0,0,.2); + border-radius: 6px; + outline: 0; + -webkit-box-shadow: 0 3px 9px rgba(0,0,0,.5); + box-shadow: 0 3px 9px rgba(0,0,0,.5); +} \ No newline at end of file diff --git a/Themes/js/module.js b/Themes/js/module.js index 7f5bb04..973e109 100644 --- a/Themes/js/module.js +++ b/Themes/js/module.js @@ -437,6 +437,7 @@ registerController("ThemesController", ['$api', '$scope','$window','$route', '$h $scope.throbber = false; log("restoreDefault", "Successful? "+ response.success + ". " + response.message); $scope.working = "Done!"; + $window.location.reload(); }); }; $scope.sendMessage = function (t, m) { diff --git a/Themes/module.html b/Themes/module.html index 370340a..5b46ccb 100644 --- a/Themes/module.html +++ b/Themes/module.html @@ -8,20 +8,25 @@
- +
- + + +
{{ control.title }} -
Current Theme: {{ current }}
-
- Refresh

- - + +
+
Current Theme: {{ current }}
+
-
-
+ +
@@ -211,9 +216,6 @@ ng-click="changeThrobber()">
- - - @@ -238,11 +240,7 @@ ng-click="changeLogo()">
- - - - Icon @@ -266,9 +264,6 @@ ng-click="changeFavicon()"> - - - @@ -296,7 +291,6 @@ ng-options="x for x in colors" ng-value="{{ allcontrastText }}" > - {{ () }} @@ -305,7 +299,6 @@ ng-options="x for x in brightness" ng-value="{{ allcontrastBrightness }}" > - {{ () }} @@ -329,7 +322,6 @@ ng-options="x for x in colors" ng-value="{{ dashboardcontrastText }}" > - {{ () }} @@ -338,7 +330,6 @@ ng-options="x for x in brightness" ng-value="{{ dashboardcontrastBrightness }}" > - {{ () }} @@ -357,7 +348,6 @@ ng-options="x for x in colors" ng-value="{{ reconcontrastText }}" > - {{ () }} - - -
-
-
- - - - - - - - - - - - - - - +
+ +
+
+
Theme NameLocationActivateDelete
{{ theme.title - }}{{ theme.storage }}Activate - Delete -
+ + + + + + + + + + + + + + + + + + + + + + + +
IconColorBrightness
Throbber {{ throbbercontrastText }} N/A
Logo {{ logocontrastText }} N/A
Favicon {{ faviconcontrastText }} N/A
Dashboard {{ dashboardcontrastText }} {{ dashboardcontrastBrightness }}
Recon {{ reconcontrastText }} {{ reconcontrastBrightness }}
Profiling {{ profilingcontrastText }} {{ profilingcontrastBrightness }}
Clients {{ clientscontrastText }} {{ clientscontrastBrightness }}
Modules {{ modulescontrastText }} {{ modulescontrastBrightness }}
Filters {{ filterscontrastText }} {{ filterscontrastBrightness }}
PineAP {{ pineapcontrastText }} {{ pineapcontrastBrightness }}
Tracking {{ trackingcontrastText }} {{ trackingcontrastBrightness }}
Logging {{ loggingcontrastText }} {{ loggingcontrastBrightness }}
Reporting {{ reportingcontrastText }} {{ reportingcontrastBrightness }}
Networking {{ networkingcontrastText }} {{ networkingcontrastBrightness }}
Configuration {{ configurationcontrastText }} {{ configurationcontrastBrightness }}
Advanced {{ advancedcontrastText }} {{ advancedcontrastBrightness }}
Help {{ helpcontrastText }} {{ helpcontrastBrightness }}
-
- -
-

- No Themes in Library to Display. -

-
- -
- -
-

Theme Editor | {{ workshopTheme.themeName }}

- -
- - - - - - - - - -
CSS Editor
+
+
+ + +
+
+ +
+
+
Themes
+
+
+
+
+ + + + +
+
+
+
+ + + + + + + + + + + + + + + +
Theme NameLocationActivateDelete
+ {{ theme.title.substring(0, theme.title.length - 4); }}{{ theme.storage }}Activate + Delete +
+
+
+ +
+

+ No Themes in Library to Display. +

+
+ +
+ +
+

Theme Editor | {{ workshopTheme.themeName }}

+ +
+ + + + + + + + + +
CSS Editor
+
+
-
-
- -
-
-
Icons
-
-
-
-
-

Generic

-
- + +
+
+
Icons
+
+
+
+
+

Generic

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeImageLight ThemeDark Theme
+ Throbber + + + +
+
+
+
+
+
+ Logo + + + +
+
+
+
+
+
+ Icon + + + +
+
+
+
+
+
+
+

Modules

+
+ - - + + + - - - - - - - - - - - - - - - - - - -
Type ImageLightDarkColorBrightness
- Throbber - - - -
-
-
-
-
-
- Logo - - - -
-
-
-
-
-
- Icon - - - -
-
-
-
-
-
-
-

Modules

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + + + + + + + + + + + + + + + + + + + + - - - + + - - - - - + + - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeImageColorBrightness
- ALL - - - - - - - -
- Dashboard - - - - - - - - -
Recon + + + + + +
+ Dashboard + + + + + + + + +
Recon + + +
Profiling +
Profiling
ClientsModules
Modules
Filters - - -
Filters - - -
PineAP - - -
Tracking -
Logging - -
Reporting
PineAP
Networking - - -
Configuration - - -
Advanced - - -
Help - - -
- + + + Tracking + + + + + + + + + + Logging + + + + + + + + + + Reporting + + + + + + + + + + Networking + + + + + + + + + + Configuration + + + + + + + + + + Advanced + + + + + + + + + + Help + + + + + + + + + + + +
+
+
+ + +
+
+
How To
+
+
+
+
    +
  • Share your new theme
  • +
      +
    • Navigate to the wifi pineapple modules repository
    • +
    • Add your theme .css file in the css directory of this module
    • +
    • Create a new pull request
    • +
    • Update the version number
    • +
    • Update the module version in module.info
    • +
    +
+
    +
  • Fix Images appearing broken/unable to be loaded
  • +
      +
    • Refresh the page
    • +
    • If the problem persists, see "Submit a Bug"
    • +
    +
+
    +
  • Fix a messy looking page after a save/refresh
  • +
      +
    • Reselect 'Activate' beside your theme
    • +
    • Refresh the page
    • +
    +
+
    +
  • Select the proper Image type
  • +
      +
    • Beside each Image type are two options "Light" and "Dark"
    • +
    • "Dark" images are intended for dark backgrounds
    • +
    • "Light" images are intended for light backgrounds
    • +
    +
+
    +
  • Use a theme once without a module
  • +
      +
    • Navigate to https://github.com/kbeflo/pineapple-themes
    • +
    • Select a theme and run the install.sh script
    • +
    +
+
    +
  • Submit a Bug
  • +
      +
    • Navigate to https://github.com/hak5/wifipineapple-modules/issues
    • +
    • Select the "New Issue" button
    • +
    • Tag @trashbo4t in your issue description
    • +
    +
+
-
- - -
-
-
How To
+
+ +
+
+
    +
  • Creating a New Theme
  • +
      +
    • + The set of .css files bundled with the Themes module are already configured to handle + undefined colors from the standard bootstrap .css file in the default settings. +
    • +
    • + A reccommended method to ensure larger coverage is to create a new .css file with the theme creator + then copy another .css files code. +
    • +
    • + Once you copied the code do a search and replace for the colors of your choosing! +
    • +
    +
+
    +
  • Working with .css files in a browser
  • +
      +
    • Modyfing a .css file with active updating is possible in modern browsers.
    • +
    • Select 'ctrl'+'shift'+'I', then select the sourses tab.
    • +
    • Select the 'css' folder, then the main.css file
    • +
    • Modify the main.css file until you like the appearance of the page.
    • +
    • From there select all of the code ('ctrl'+'a') and paste it into your theme files CSS Editor box
    • +
    • Then select "Save" and "Activate"
    • +
    +
+
    +
  • Dark Theme vs Light Theme Icons
  • +
      +
    • If your background is dark, its best to choose a "Dark Theme" icon.
    • +
    • Overlooked transparency pixels will be harder to see this way!
    • +
    +
+ +
+
-
-
-
    -
  • Share your new theme
  • + +
    + +
    +
      -
    • Navigate to the wifi pineapple modules repository
    • -
    • Add your theme .css file in the css directory of this module
    • -
    • Create a new pull request
    • -
    • Update the version number
    • -
    • Update the module version in module.info
    • +
    • 1.0
    • +
        +
      • Pending release of 1.0
      • +
    -
-
    -
  • Fix Images appearing broken/unable to be loaded
  • -
      -
    • Refresh the page
    • -
    • If the problem persists, see "Submit a Bug"
    • -
    -
-
    -
  • Fix a messy looking page after a save/refresh
  • -
      -
    • Reselect 'Activate' beside your theme
    • -
    • Refresh the page
    • -
    -
-
    -
  • Select the proper Image type
  • -
      -
    • Beside each Image type are two options "Light" and "Dark"
    • -
    • "Dark" images are intended for dark backgrounds
    • -
    • "Light" images are intended for light backgrounds
    • -
    -
-
    -
  • Use a theme once without a module
  • -
      -
    • Navigate to https://github.com/kbeflo/pineapple-themes
    • -
    • Select a theme and run the install.sh script
    • -
    -
-
    -
  • Submit a Bug
  • -
      -
    • Navigate to https://github.com/hak5/wifipineapple-modules/issues
    • -
    • Select the "New Issue" button
    • -
    • Tag @trashbo4t in your issue description
    • -
    -
-
+
+
- - -
- -
-
-
    -
  • 1.0
  • -
      -
    • Pending release of 1.0
    • -
    -
-
-
-
- - - + + \ No newline at end of file diff --git a/Themes/module.info b/Themes/module.info index b886385..391beff 100644 --- a/Themes/module.info +++ b/Themes/module.info @@ -1,6 +1,6 @@ { "title": "Themes", - "description": "Create or download custom themes!", + "description": "Create, download, and share custom themes", "version": "1.0", "author": "trashbo4t" -} \ No newline at end of file +} From 50115f233538df0ffdf5d21a934514dcff32eb8a Mon Sep 17 00:00:00 2001 From: trashbo4t Date: Sun, 20 May 2018 08:50:01 -0400 Subject: [PATCH 20/28] Update 1980.css --- Themes/css/1980.css | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Themes/css/1980.css b/Themes/css/1980.css index 15d083d..e99d58d 100644 --- a/Themes/css/1980.css +++ b/Themes/css/1980.css @@ -464,7 +464,7 @@ td { left: 0; right: 0; bottom: 0; - background-color: #ccc; + background-color: darkgray; -webkit-transition: .4s; transition: .4s; } @@ -475,15 +475,15 @@ td { width: 12px; left: 4px; bottom: 4px; - background-color: white; + background-color: black; -webkit-transition: .4s; transition: .4s; } input:checked + .slider { - background-color: #2196F3; + background-color: lime; } input:focus + .slider { - box-shadow: 0 0 1px #2196F3; + box-shadow: 0 0 1px lime; } input:checked + .slider:before { -webkit-transform: translateX(26px); @@ -496,4 +496,4 @@ input:checked + .slider:before { } .slider.round:before { border-radius: 50%; -} \ No newline at end of file +} From 668309128de5841e425ffce20b09ba80785fdaa7 Mon Sep 17 00:00:00 2001 From: Marc Date: Wed, 23 May 2018 23:00:24 +0100 Subject: [PATCH 21/28] Correct module.info --- Themes/module.info | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Themes/module.info b/Themes/module.info index 391beff..4eb03b9 100644 --- a/Themes/module.info +++ b/Themes/module.info @@ -1,6 +1,10 @@ { + "author": "trashbo4t", + "description": "Create, download, and share custom themes.", + "devices": [ + "nano", + "tetra" + ], "title": "Themes", - "description": "Create, download, and share custom themes", - "version": "1.0", - "author": "trashbo4t" + "version": "1.0" } From 39b8d39794787f96c82b266bca8f8158c8510177 Mon Sep 17 00:00:00 2001 From: Marc Date: Fri, 1 Jun 2018 14:53:20 +0100 Subject: [PATCH 22/28] LEDController: Update to work with FW 2.1.0 --- LEDController/api/module.php | 152 +++++++++++++++++------------------ LEDController/module.info | 2 +- 2 files changed, 77 insertions(+), 77 deletions(-) diff --git a/LEDController/api/module.php b/LEDController/api/module.php index c1df365..d06a73e 100644 --- a/LEDController/api/module.php +++ b/LEDController/api/module.php @@ -56,18 +56,18 @@ class LEDController extends Module private function getTetraYellow() { - $trigger = $this->uciGet('system.@led[2].trigger'); + $trigger = $this->uciGet('system.led_eth0.trigger'); if ($trigger == 'none') { - $default = $this->uciGet('system.@led[2].default'); + $default = $this->uciGet('system.led_eth0.default'); if ($default == 0) { $this->response = array('enabled' => false, 'trigger' => $trigger); } elseif ($default == 1) { $this->response = array('enabled' => true, 'trigger' => $trigger); } } elseif ($trigger == 'netdev') { - $mode = $this->uciGet('system.@led[2].mode'); - $interface = $this->uciGet('system.@led[2].dev'); + $mode = $this->uciGet('system.led_eth0.mode'); + $interface = $this->uciGet('system.led_eth0.dev'); if ($mode == 'link tx rx') { $this->response = array('enabled' => true, 'trigger' => $trigger, 'mode' => 'link tx rx', 'interface' => $interface); @@ -79,8 +79,8 @@ class LEDController extends Module 'mode' => 'link rx', 'interface' => $interface); } } elseif ($trigger == 'timer') { - $delayOn = $this->uciGet('system.@led[2].delayon'); - $delayOff = $this->uciGet('system.@led[2].delayoff'); + $delayOn = $this->uciGet('system.led_eth0.delayon'); + $delayOff = $this->uciGet('system.led_eth0.delayoff'); $this->response = array('enabled' => true, 'trigger' => $trigger, 'delayOn' => $delayOn, 'delayOff' => $delayOff); } else { @@ -99,23 +99,23 @@ class LEDController extends Module if ($enabled == true) { if ($trigger == 'none') { - $this->uciSet('system.@led[2].trigger', 'none'); - $this->uciSet('system.@led[2].default', '1'); + $this->uciSet('system.led_eth0.trigger', 'none'); + $this->uciSet('system.led_eth0.default', '1'); $this->restartLEDs(); } elseif ($trigger == 'netdev') { - $this->uciSet('system.@led[2].trigger', 'netdev'); - $this->uciSet('system.@led[2].mode', "$mode"); - $this->uciSet('system.@led[2].dev', "$interface"); + $this->uciSet('system.led_eth0.trigger', 'netdev'); + $this->uciSet('system.led_eth0.mode', "$mode"); + $this->uciSet('system.led_eth0.dev', "$interface"); $this->restartLEDs(); } elseif ($trigger == 'timer') { - $this->uciSet('system.@led[2].trigger', 'timer'); - $this->uciSet('system.@led[2].delayon', "$delayOn"); - $this->uciSet('system.@led[2].delayoff', "$delayOff"); + $this->uciSet('system.led_eth0.trigger', 'timer'); + $this->uciSet('system.led_eth0.delayon', "$delayOn"); + $this->uciSet('system.led_eth0.delayoff', "$delayOff"); $this->restartLEDs(); } } elseif ($enabled == false) { - $this->uciSet('system.@led[2].trigger', 'none'); - $this->uciSet('system.@led[2].default', '0'); + $this->uciSet('system.led_eth0.trigger', 'none'); + $this->uciSet('system.led_eth0.default', '0'); $this->restartLEDs(); } @@ -126,18 +126,18 @@ class LEDController extends Module private function getTetraBlue() { - $trigger = $this->uciGet('system.@led[0].trigger'); + $trigger = $this->uciGet('system.led_wlan0.trigger'); if ($trigger == 'none') { - $default = $this->uciGet('system.@led[0].default'); + $default = $this->uciGet('system.led_wlan0.default'); if ($default == 0) { $this->response = array('enabled' => false, 'trigger' => $trigger); } elseif ($default == 1) { $this->response = array('enabled' => true, 'trigger' => $trigger); } } elseif ($trigger == 'netdev') { - $mode = $this->uciGet('system.@led[0].mode'); - $interface = $this->uciGet('system.@led[0].dev'); + $mode = $this->uciGet('system.led_wlan0.mode'); + $interface = $this->uciGet('system.led_wlan0.dev'); if ($mode == 'link tx rx') { $this->response = array('enabled' => true, 'trigger' => $trigger, 'mode' => 'link tx rx', 'interface' => $interface); @@ -149,8 +149,8 @@ class LEDController extends Module 'mode' => 'link rx', 'interface' => $interface); } } elseif ($trigger == 'timer') { - $delayOn = $this->uciGet('system.@led[0].delayon'); - $delayOff = $this->uciGet('system.@led[0].delayoff'); + $delayOn = $this->uciGet('system.led_wlan0.delayon'); + $delayOff = $this->uciGet('system.led_wlan0.delayoff'); $this->response = array('enabled' => true, 'trigger' => $trigger, 'delayOn' => $delayOn, 'delayOff' => $delayOff); } else { @@ -169,23 +169,23 @@ class LEDController extends Module if ($enabled == true) { if ($trigger == 'none') { - $this->uciSet('system.@led[0].trigger', 'none'); - $this->uciSet('system.@led[0].default', '1'); + $this->uciSet('system.led_wlan0.trigger', 'none'); + $this->uciSet('system.led_wlan0.default', '1'); $this->restartLEDs(); } elseif ($trigger == 'netdev') { - $this->uciSet('system.@led[0].trigger', 'netdev'); - $this->uciSet('system.@led[0].mode', "$mode"); - $this->uciSet('system.@led[0].dev', "$interface"); + $this->uciSet('system.led_wlan0.trigger', 'netdev'); + $this->uciSet('system.led_wlan0.mode', "$mode"); + $this->uciSet('system.led_wlan0.dev', "$interface"); $this->restartLEDs(); } elseif ($trigger == 'timer') { - $this->uciSet('system.@led[0].trigger', 'timer'); - $this->uciSet('system.@led[0].delayon', "$delayOn"); - $this->uciSet('system.@led[0].delayoff', "$delayOff"); + $this->uciSet('system.led_wlan0.trigger', 'timer'); + $this->uciSet('system.led_wlan0.delayon', "$delayOn"); + $this->uciSet('system.led_wlan0.delayoff', "$delayOff"); $this->restartLEDs(); } } elseif ($enabled == false) { - $this->uciSet('system.@led[0].trigger', 'none'); - $this->uciSet('system.@led[0].default', '0'); + $this->uciSet('system.led_wlan0.trigger', 'none'); + $this->uciSet('system.led_wlan0.default', '0'); $this->restartLEDs(); } @@ -196,18 +196,18 @@ class LEDController extends Module private function getTetraRed() { - $trigger = $this->uciGet('system.@led[1].trigger'); + $trigger = $this->uciGet('system.led_wlan1mon.trigger'); if ($trigger == 'none') { - $default = $this->uciGet('system.@led[1].default'); + $default = $this->uciGet('system.led_wlan1mon.default'); if ($default == 0) { $this->response = array('enabled' => false, 'trigger' => $trigger); } elseif ($default == 1) { $this->response = array('enabled' => true, 'trigger' => $trigger); } } elseif ($trigger == 'netdev') { - $mode = $this->uciGet('system.@led[1].mode'); - $interface = $this->uciGet('system.@led[1].dev'); + $mode = $this->uciGet('system.led_wlan1mon.mode'); + $interface = $this->uciGet('system.led_wlan1mon.dev'); if ($mode == 'link tx rx') { $this->response = array('enabled' => true, 'trigger' => $trigger, 'mode' => 'link tx rx', 'interface' => $interface); @@ -219,8 +219,8 @@ class LEDController extends Module 'mode' => 'link rx', 'interface' => $interface); } } elseif ($trigger == 'timer') { - $delayOn = $this->uciGet('system.@led[1].delayon'); - $delayOff = $this->uciGet('system.@led[1].delayoff'); + $delayOn = $this->uciGet('system.led_wlan1mon.delayon'); + $delayOff = $this->uciGet('system.led_wlan1mon.delayoff'); $this->response = array('enabled' => true, 'trigger' => $trigger, 'delayOn' => $delayOn, 'delayOff' => $delayOff); } else { @@ -239,23 +239,23 @@ class LEDController extends Module if ($enabled == true) { if ($trigger == 'none') { - $this->uciSet('system.@led[1].trigger', 'none'); - $this->uciSet('system.@led[1].default', '1'); + $this->uciSet('system.led_wlan1mon.trigger', 'none'); + $this->uciSet('system.led_wlan1mon.default', '1'); $this->restartLEDs(); } elseif ($trigger == 'netdev') { - $this->uciSet('system.@led[1].trigger', 'netdev'); - $this->uciSet('system.@led[1].mode', "$mode"); - $this->uciSet('system.@led[1].dev', "$interface"); + $this->uciSet('system.led_wlan1mon.trigger', 'netdev'); + $this->uciSet('system.led_wlan1mon.mode', "$mode"); + $this->uciSet('system.led_wlan1mon.dev', "$interface"); $this->restartLEDs(); } elseif ($trigger == 'timer') { - $this->uciSet('system.@led[1].trigger', 'timer'); - $this->uciSet('system.@led[1].delayon', "$delayOn"); - $this->uciSet('system.@led[1].delayoff', "$delayOff"); + $this->uciSet('system.led_wlan1mon.trigger', 'timer'); + $this->uciSet('system.led_wlan1mon.delayon', "$delayOn"); + $this->uciSet('system.led_wlan1mon.delayoff', "$delayOff"); $this->restartLEDs(); } } elseif ($enabled == false) { - $this->uciSet('system.@led[1].trigger', 'none'); - $this->uciSet('system.@led[1].default', '0'); + $this->uciSet('system.led_wlan1mon.trigger', 'none'); + $this->uciSet('system.led_wlan1mon.default', '0'); $this->restartLEDs(); } @@ -266,18 +266,18 @@ class LEDController extends Module private function getNanoBlue() { - $trigger = $this->uciGet('system.@led[0].trigger'); + $trigger = $this->uciGet('system.led_wlan0.trigger'); if ($trigger == 'none') { - $default = $this->uciGet('system.@led[0].default'); + $default = $this->uciGet('system.led_wlan0.default'); if ($default == 0) { $this->response = array('enabled' => false, 'trigger' => $trigger); } elseif ($default == 1) { $this->response = array('enabled' => true, 'trigger' => $trigger); } } elseif ($trigger == 'netdev') { - $mode = $this->uciGet('system.@led[0].mode'); - $interface = $this->uciGet('system.@led[0].dev'); + $mode = $this->uciGet('system.led_wlan0.mode'); + $interface = $this->uciGet('system.led_wlan0.dev'); if ($mode == 'link tx rx') { $this->response = array('enabled' => true, 'trigger' => $trigger, 'mode' => 'link tx rx', 'interface' => $interface); @@ -289,8 +289,8 @@ class LEDController extends Module 'mode' => 'link rx', 'interface' => $interface); } } elseif ($trigger == 'timer') { - $delayOn = $this->uciGet('system.@led[0].delayon'); - $delayOff = $this->uciGet('system.@led[0].delayoff'); + $delayOn = $this->uciGet('system.led_wlan0.delayon'); + $delayOff = $this->uciGet('system.led_wlan0.delayoff'); $this->response = array('enabled' => true, 'trigger' => $trigger, 'delayOn' => $delayOn, 'delayOff' => $delayOff); } else { @@ -309,23 +309,23 @@ class LEDController extends Module if ($enabled == true) { if ($trigger == 'none') { - $this->uciSet('system.@led[0].trigger', 'none'); - $this->uciSet('system.@led[0].default', '1'); + $this->uciSet('system.led_wlan0.trigger', 'none'); + $this->uciSet('system.led_wlan0.default', '1'); $this->restartLEDs(); } elseif ($trigger == 'netdev') { - $this->uciSet('system.@led[0].trigger', 'netdev'); - $this->uciSet('system.@led[0].mode', "$mode"); - $this->uciSet('system.@led[0].dev', "$interface"); + $this->uciSet('system.led_wlan0.trigger', 'netdev'); + $this->uciSet('system.led_wlan0.mode', "$mode"); + $this->uciSet('system.led_wlan0.dev', "$interface"); $this->restartLEDs(); } elseif ($trigger == 'timer') { - $this->uciSet('system.@led[0].trigger', 'timer'); - $this->uciSet('system.@led[0].delayon', "$delayOn"); - $this->uciSet('system.@led[0].delayoff', "$delayOff"); + $this->uciSet('system.led_wlan0.trigger', 'timer'); + $this->uciSet('system.led_wlan0.delayon', "$delayOn"); + $this->uciSet('system.led_wlan0.delayoff', "$delayOff"); $this->restartLEDs(); } } elseif ($enabled == false) { - $this->uciSet('system.@led[0].trigger', 'none'); - $this->uciSet('system.@led[0].default', '0'); + $this->uciSet('system.led_wlan0.trigger', 'none'); + $this->uciSet('system.led_wlan0.default', '0'); $this->restartLEDs(); } @@ -339,21 +339,21 @@ class LEDController extends Module $device = $this->getDevice(); if ($device == 'tetra') { - $this->uciSet('system.@led[0].trigger', 'netdev'); - $this->uciSet('system.@led[0].mode', 'link tx rx'); - $this->uciSet('system.@led[0].dev', 'wlan0'); - $this->uciSet('system.@led[1].trigger', 'netdev'); - $this->uciSet('system.@led[1].mode', 'link tx rx'); - $this->uciSet('system.@led[1].dev', 'wlan1mon'); - $this->uciSet('system.@led[2].trigger', 'netdev'); - $this->uciSet('system.@led[2].mode', 'link tx rx'); - $this->uciSet('system.@led[2].dev', 'eth0'); + $this->uciSet('system.led_wlan0.trigger', 'netdev'); + $this->uciSet('system.led_wlan0.mode', 'link tx rx'); + $this->uciSet('system.led_wlan0.dev', 'wlan0'); + $this->uciSet('system.led_wlan1mon.trigger', 'netdev'); + $this->uciSet('system.led_wlan1mon.mode', 'link tx rx'); + $this->uciSet('system.led_wlan1mon.dev', 'wlan1mon'); + $this->uciSet('system.led_eth0.trigger', 'netdev'); + $this->uciSet('system.led_eth0.mode', 'link tx rx'); + $this->uciSet('system.led_eth0.dev', 'eth0'); $this->restartLEDs(); $this->response = array('success' => true); } else { - $this->uciSet('system.@led[0].trigger', 'netdev'); - $this->uciSet('system.@led[0].mode', 'link tx rx'); - $this->uciSet('system.@led[0].dev', 'wlan0'); + $this->uciSet('system.led_wlan0.trigger', 'netdev'); + $this->uciSet('system.led_wlan0.mode', 'link tx rx'); + $this->uciSet('system.led_wlan0.dev', 'wlan0'); $this->restartLEDs(); $this->response = array('success' => true); } diff --git a/LEDController/module.info b/LEDController/module.info index bbef7af..7164c34 100644 --- a/LEDController/module.info +++ b/LEDController/module.info @@ -6,5 +6,5 @@ "tetra" ], "title": "LED Controller", - "version": "1.1" + "version": "1.2" } \ No newline at end of file From b3e817573436a9235c7071af801d19ba17045e4d Mon Sep 17 00:00:00 2001 From: Marc Date: Tue, 5 Jun 2018 00:49:59 +0100 Subject: [PATCH 23/28] LED Controller: Bump version number. --- LEDController/module.html | 2 +- LEDController/module.info | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/LEDController/module.html b/LEDController/module.html index a5489ea..a9b3452 100644 --- a/LEDController/module.html +++ b/LEDController/module.html @@ -6,7 +6,7 @@

LED Controller

-
Version 1.1
+
Version 1.3

Written by Foxtrot

This module allows you to control the LEDs behaviour on both the NANO and the TETRA.

diff --git a/LEDController/module.info b/LEDController/module.info index 7164c34..c24f7c1 100644 --- a/LEDController/module.info +++ b/LEDController/module.info @@ -6,5 +6,5 @@ "tetra" ], "title": "LED Controller", - "version": "1.2" -} \ No newline at end of file + "version": "1.3" +} From 4f5fcd536bfcdd633f4b9a9e4f0512ac702552a8 Mon Sep 17 00:00:00 2001 From: Marc Date: Wed, 6 Jun 2018 00:01:58 +0100 Subject: [PATCH 24/28] Commander: Bump version -> 2.1 --- Commander/Python/commander.py | 3 ++- Commander/module.info | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Commander/Python/commander.py b/Commander/Python/commander.py index 0cb5fc4..9ac6f9a 100644 --- a/Commander/Python/commander.py +++ b/Commander/Python/commander.py @@ -20,6 +20,7 @@ import errno class Commander(object): print "[*] WiFi Pineapple Commander Module" + print "[*] peace to: sebkinne & tesla" def run(self): while True: @@ -118,4 +119,4 @@ if __name__ == '__main__': commander.parseConfig() commander.printConfig() commander.connect() - commander.run() \ No newline at end of file + commander.run() diff --git a/Commander/module.info b/Commander/module.info index eb0d190..71bda3f 100644 --- a/Commander/module.info +++ b/Commander/module.info @@ -6,5 +6,5 @@ "tetra" ], "title": "Commander", - "version": "2.0" -} \ No newline at end of file + "version": "2.1" +} From 03aeb6158caf1bbaf17bf6d048edc5c78c2036f2 Mon Sep 17 00:00:00 2001 From: Bradley D Date: Thu, 28 Jun 2018 18:47:32 -0600 Subject: [PATCH 25/28] Site Survey: create capture directory (#28) --- SiteSurvey/module.info | 2 +- SiteSurvey/scripts/capture.sh | 5 +++++ SiteSurvey/scripts/dependencies.sh | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/SiteSurvey/module.info b/SiteSurvey/module.info index 6b1f6e2..c41ca29 100644 --- a/SiteSurvey/module.info +++ b/SiteSurvey/module.info @@ -6,5 +6,5 @@ "tetra" ], "title": "Site Survey", - "version": "1.3" + "version": "1.4" } diff --git a/SiteSurvey/scripts/capture.sh b/SiteSurvey/scripts/capture.sh index 6449ba7..1e97c1d 100755 --- a/SiteSurvey/scripts/capture.sh +++ b/SiteSurvey/scripts/capture.sh @@ -55,6 +55,11 @@ if [ "$1" = "start" ]; then echo ${BSSID} > ${LOCK} + # make sure the folder exists + if [ ! -d /pineapple/modules/SiteSurvey/capture ]; then + mkdir /pineapple/modules/SiteSurvey/capture + fi + airodump-ng -c ${CHANNEL} --bssid ${BSSID} -w /pineapple/modules/SiteSurvey/capture/capture_${MYTIME} ${MYMONITOR} &> /dev/null & echo -e "Capture is running..." >> ${LOG} diff --git a/SiteSurvey/scripts/dependencies.sh b/SiteSurvey/scripts/dependencies.sh index f8e0def..857c997 100755 --- a/SiteSurvey/scripts/dependencies.sh +++ b/SiteSurvey/scripts/dependencies.sh @@ -23,6 +23,8 @@ if [ "$1" = "install" ]; then uci set sitesurvey.module.installed=1 uci commit sitesurvey.module.installed + mkdir /pineapple/modules/SiteSurvey/capture + elif [ "$1" = "remove" ]; then rm -rf /etc/config/sitesurvey fi From 30826cc50ec370a82752a6ce290f0728099515b3 Mon Sep 17 00:00:00 2001 From: Josh Date: Thu, 28 Jun 2018 21:01:22 -0400 Subject: [PATCH 26/28] EvilPortal: Update to 3.0 (#29) :tada: :tada: --- EvilPortal/api/module.php | 948 +++++++++++------- EvilPortal/executable/executable | 188 ++-- EvilPortal/includes/api/Portal.php | 51 +- EvilPortal/includes/skeleton/.disable | 4 + EvilPortal/includes/skeleton/.enable | 4 + EvilPortal/includes/skeleton/MyPortal.php | 15 +- EvilPortal/includes/skeleton/helper.php | 45 + EvilPortal/includes/skeleton/index.php | 24 +- EvilPortal/includes/skeleton/portalinfo.json | 4 + .../includes/targeted_skeleton/.disable | 4 + EvilPortal/includes/targeted_skeleton/.enable | 4 + .../includes/targeted_skeleton/MyPortal.php | 33 + .../includes/targeted_skeleton/default.php | 35 + .../includes/targeted_skeleton/helper.php | 45 + .../includes/targeted_skeleton/index.php | 81 ++ .../targeted_skeleton/jquery-2.2.1.min.js | 4 + .../targeted_skeleton/portalinfo.json | 34 + EvilPortal/js/module.js | 843 +++++++++++----- EvilPortal/module.html | 381 +++++-- EvilPortal/module.info | 12 +- 20 files changed, 1894 insertions(+), 865 deletions(-) mode change 100644 => 100755 EvilPortal/executable/executable create mode 100644 EvilPortal/includes/skeleton/.disable create mode 100644 EvilPortal/includes/skeleton/.enable create mode 100644 EvilPortal/includes/skeleton/helper.php create mode 100644 EvilPortal/includes/skeleton/portalinfo.json create mode 100644 EvilPortal/includes/targeted_skeleton/.disable create mode 100644 EvilPortal/includes/targeted_skeleton/.enable create mode 100644 EvilPortal/includes/targeted_skeleton/MyPortal.php create mode 100644 EvilPortal/includes/targeted_skeleton/default.php create mode 100644 EvilPortal/includes/targeted_skeleton/helper.php create mode 100644 EvilPortal/includes/targeted_skeleton/index.php create mode 100644 EvilPortal/includes/targeted_skeleton/jquery-2.2.1.min.js create mode 100644 EvilPortal/includes/targeted_skeleton/portalinfo.json diff --git a/EvilPortal/api/module.php b/EvilPortal/api/module.php index c60fe03..20a4dc4 100644 --- a/EvilPortal/api/module.php +++ b/EvilPortal/api/module.php @@ -6,325 +6,534 @@ class EvilPortal extends Module // CONSTANTS private $CLIENTS_FILE = '/tmp/EVILPORTAL_CLIENTS.txt'; private $ALLOWED_FILE = '/pineapple/modules/EvilPortal/data/allowed.txt'; - + private $STORAGE_LOCATIONS = array("sd" => "/sd/portals/", "internal" => "/root/portals/"); + private $BASE_EP_COMMAND = 'module EvilPortal'; // CONSTANTS + /** + * An implementation of the route method from Module. + * This method routes the request to the method that handles it. + */ public function route() { + $this->createPortalFolders(); // if the portal folders (/root/portals | /sd/portals) do not exist create them. + switch ($this->request->action) { - case 'getControlValues': - $this->getControlValues(); + + case 'status': + $this->response = array( + "running" => $this->checkEvilPortalRunning(), + "startOnBoot" => $this->checkAutoStart(), + "sdAvailable" => $this->isSDAvailable() + ); break; - case 'startStop': - $this->handleRunning(); - break; - - case 'enableDisable': - $this->handleEnable(); - break; - - case 'portalList': - $this->handleGetPortalList(); - break; - - case 'portalFiles': - $this->getPortalFiles(); + case 'writeFileContent': + $this->response = $this->writeFileContents($this->request->filePath, $this->request->content, $this->request->append); break; case 'deletePortal': - $this->handleDeletePortal(); + case 'deleteDirectory': + case 'deleteFile': + $this->response = $this->deleteFileOrDirectory($this->request->filePath); break; - case 'deletePortalFile': - $this->deletePortalFile(); + case 'getDirectoryContent': + case 'getFileContent': + $this->response = $this->getFileOrDirectoryContents($this->request->filePath); break; - case 'activatePortal': - $this->activatePortal(); - break; - - case 'deactivatePortal': - $this->deactivatePortal(); - break; - - case 'getPortalCode': - $this->getPortalCode(); - break; - - case 'submitPortalCode': - $this->submitPortalCode(); - break; - - case 'getList': - $this->getList(); - break; - - case 'addToList': - $this->addToList(); - break; - - case 'removeFromList': - $this->removeFromList(); + case 'listAvailablePortals': + $this->response = $this->getListOfPortals(); break; case 'createNewPortal': - $this->handleCreateNewPortal(); + $this->response = $this->createNewPortal($this->request->name, $this->request->type, $this->request->storage); + break; + + case 'movePortal': + $this->response = $this->movePortal($this->request->name, $this->request->storage); + break; + + case 'activatePortal': + $this->response = $this->activatePortal($this->request->name, $this->request->storage); + break; + + case 'deactivatePortal': + $this->response = $this->deactivatePortal($this->request->name, $this->request->storage); + break; + + case 'getRules': + $this->response = $this->getPortalRules($this->request->name, $this->request->storage); + break; + + case 'saveRules': + $this->response = $this->savePortalRules($this->request->name, $this->request->storage, $this->request->rules); + break; + + case 'toggleOnBoot': + $this->response = $this->autoStartEvilPortal(); + break; + + case 'toggleCaptivePortal': + $this->response = $this->toggleCaptivePortal(); + break; + + case 'removeClientFromList': + $this->response = $this->removeFromList($this->request->clientIP, $this->request->listName); + break; + + case 'authorizeClient': + $this->authorizeClient($this->request->clientIP); + $this->response = array("success" => true); break; } } - public function getPortalCode() + /** + * Create the folders that portals are stored in if they don't exist + */ + private function createPortalFolders() { - $portalName = $this->request->name; - $portalFile = $this->request->portalFile; - $storage = $this->request->storage; - - if ($storage != "active") { - $dir = ($storage == "sd" ? "/sd/portals/" : "/root/portals/"); - } else { - $dir = "/etc/nodogsplash/htdocs/"; + if (!is_dir($this->STORAGE_LOCATIONS["internal"])) { + mkdir($this->STORAGE_LOCATIONS["internal"]); } - $message = ""; - $code = ""; - - if (file_exists($dir . $portalName . "/" . $portalFile)) { - $code = file_get_contents($dir . $portalName . "/" . $portalFile); - $message = $portalFile . " is ready for editting."; - } else { - $message = "Error finding " . $dir . $portalName . "/" . $portalFile . "."; + if (!is_dir($this->STORAGE_LOCATIONS["sd"]) and $this->isSDAvailable()) { + mkdir($this->STORAGE_LOCATIONS["sd"]); } - - $this->response = array("message" => $message, "code" => $code); - } - public function getPortalFiles() + /** + * Decide if a file for a given portal is "deletable" or not. + * If it is not then the UI should not display a delete option for the file. + * @param $file: The name of the file + * @return bool: Is the file deletable or not + */ + private function isFileDeletable($file) { - $portalName = $this->request->name; - $storage = $this->request->storage; + if (substr($file, -strlen(".ep")) == ".ep") + return false; + return !in_array($file, array("MyPortal.php", "default.php", "helper.php", "index.php")); + } - $dir = ($storage == "sd" ? "/sd/portals/" : "/root/portals/"); - $allFiles = array(); - if (file_exists($dir . $portalName)) { - $portal_files = scandir($dir . $portalName); - foreach ($portal_files as $file) { - if (is_file($dir . $portalName . "/" . $file) && !$this->endsWith($file, ".ep")) { - array_push($allFiles, $file); - } + /** + * Get the contents of a specified file or directory + * + * If this method is being called as the result of an HTTP request, make sure that "file" is specified as a + * parameter of the request and includes the full path to the file that should have its contents returned. + * + * @param $file : The file or directory to get contents of + * @return array + */ + private function getFileOrDirectoryContents($file) + { + + if (!file_exists($file)) { + $message = "No such file or directory {$file}."; + $contents = null; + $success = false; + } else if (is_file($file)) { + $message = "Found file {$file} and retrieved contents"; + $contents = array( + "name" => basename($file), + "path" => $file, + "size" => filesize($file), + "fileContent" => file_get_contents($file) + ); + $success = true; + } else if (is_dir($file)) { + $contents = array(); + $message = "Returning directory contents for {$file}"; + foreach (preg_grep('/^([^.])/', scandir($file)) as $object) { + // skip .ep files because they shouldn't be edited directly. + if (substr($object, -strlen(".ep")) == ".ep") + continue; + + $obj = array("name" => $object, "directory" => is_dir("{$file}/{$object}"), + "path" => realpath("{$file}/{$object}"), + "permissions" => substr(sprintf('%o', fileperms("{$file}/{$object}")), -4), + "size" => filesize("{$file}/{$object}"), + "deletable" => $this->isFileDeletable($object)); + array_push($contents, $obj); + } + $success = true; + } else { + $contents = null; + $success = false; + $message = "Unknown case. This should never happen."; + } + return array("success" => $success, "message" => $message, "content" => $contents); + } + + /** + * Write given content to a given file. + * @param $file : The file to write content to + * @param $content : The content to write to the file + * @param $append : Should the data be appended to the end of the file (true) or over-write the file (false) + * @return array + */ + private function writeFileContents($file, $content, $append) + { + if ($append) + file_put_contents($file, $content, FILE_APPEND); + else + file_put_contents($file, $content); + return array("success" => true, "message" => null); + } + + /** + * Delete a given file or directory and check if it has been deleted. + * If the file was deleted the success will be true otherwise success is false + * @param $filePath + * @return array + */ + private function deleteFileOrDirectory($filePath) + { + if ($this->isFileDeletable(basename($filePath))) { + exec(escapeshellcmd("rm -rf {$filePath}")); + + $success = (!file_exists($filePath)); + $message = (file_exists($filePath)) ? "Error deleting file {$filePath}." : "{$filePath} has been deleted."; + } else { + $success = false; + $message = "{$filePath} can not be deleted!"; + } + return array("success" => $success, "message" => $message); + } + + /** + * Get a list of portals found on internal and sd storage. + */ + private function getListOfPortals() + { + + // an array of all of the portals found + $portals = array(); + $availableMediums = array("internal"); + + // if the sd card is available add it to the availableMediums + if ($this->isSDAvailable()) { + array_push($availableMediums, "sd"); + } + + foreach($availableMediums as $medium) { + $storageLocation = $this->STORAGE_LOCATIONS[$medium]; + foreach (preg_grep('/^([^.])/', scandir($storageLocation)) as $object) { + if (!is_dir($storageLocation . $object)) // skip the object if it is not a directory. + continue; + + $portal = array( + "title" => $object, + "portalType" => $this->getValueFromJSONFile(array("type"), "{$storageLocation}{$object}/{$object}.ep")["type"], + "location" => "{$storageLocation}{$object}", + "storage" => $medium, + "active" => (file_exists("/www/{$object}.ep")) + ); + // push the portal object to the array of portals found + array_push($portals, $portal); } } - $this->response = array("portalFiles" => $allFiles); + + return array("success" => true, "portals" => $portals); } - public function deletePortalFile() + /** + * Create a new Portal with a given name of a given type on a given storage medium. + * @param $name : The name of the new portal + * @param $type : The type of portal to create (targeted or basic) + * @param $storage : The storage medium to save the portal to (sd or internal) + * @return array + */ + private function createNewPortal($name, $type, $storage) { - $portalName = $this->request->name; - $storage = $this->request->stroage; - $fileName = $this->request->portalFile; + // force the name of the portal to be lower cased and replace spaces with underscores + $name = strtolower(str_replace(' ', '_', $name)); - $dir = ($storage == "sd" ? "/sd/portals/" : "/root/portals/"); - $message = "Unable to delete file."; - if (file_exists($dir . $portalName . "/" . $fileName)) { - unlink($dir . $portalName . "/" . $fileName); - $message = "Successfully deleted " . $dir . $portalName . "/" . $fileName; + // $storage should be equal to "sd" or "internal". If its anything else just make it "internal" + $storage = ($storage == "sd" or $storage == "internal") ? $storage : "internal"; + + // the path to store the portal + $portalPath = $this->STORAGE_LOCATIONS[$storage]; + + // verify that no portal with the same name already exists + if (file_exists("{$this->STORAGE_LOCATIONS["internal"]}{$name}") or + (file_exists("{$this->STORAGE_LOCATIONS["sd"]}{$name}") and $this->isSDAvailable())) { + return array("success" => false, "message" => "A portal named {$name} already exists!"); } - $this->response = array("deleteMessage" => $message); + // if the portal is supposed to be stored on the SD card, make sure that it is indeed available first. + if ($storage == "sd" and !$this->isSDAvailable()) { + return array("success" => false, "message" => "There is no SD card available!"); + } + + // create the directory for the portal + mkdir($portalPath . $name); + + // handle the portal types. If anything other than "targeted" is specified then it will create a basic portal + switch ($type) { + case 'targeted': + exec("cp /pineapple/modules/EvilPortal/includes/targeted_skeleton/* {$portalPath}{$name}/"); + exec("cp /pineapple/modules/EvilPortal/includes/targeted_skeleton/.* {$portalPath}{$name}/"); + exec("mv {$portalPath}{$name}/portalinfo.json {$portalPath}{$name}/{$name}.ep"); + $this->updateJSONFile(array("name" => $name, "type" => "targeted"), "{$portalPath}{$name}/{$name}.ep"); + exec("sed -i 's/\"portal_name_here\"/\"{$name}\"/g' {$portalPath}{$name}/index.php"); + break; + + default: + exec("cp /pineapple/modules/EvilPortal/includes/skeleton/* {$portalPath}{$name}/"); + exec("cp /pineapple/modules/EvilPortal/includes/skeleton/.* {$portalPath}{$name}/"); + exec("mv {$portalPath}{$name}/portalinfo.json {$portalPath}{$name}/{$name}.ep"); + $this->updateJSONFile(array("name" => $name, "type" => "basic"), "{$portalPath}{$name}/{$name}.ep"); + break; + } + + // make these scripts executable + exec("chmod +x {$portalPath}{$name}/.enable"); + exec("chmod +x {$portalPath}{$name}/.disable"); + + return array("success" => true, "message" => "Created {$type} portal {$name}!"); + } + + /** + * Move a portal between one storage medium to another. + * + * If the current medium is "internal" then the portal will be moved to "sd" and visa-versa + * + * @param $name : The name of the portal to move + * @param $storage : The current storage medium + * @return array + */ + private function movePortal($name, $storage) + { + $storage = ($storage == "internal" || $storage == "sd") ? $storage : "internal"; + $newMedium = ($storage == "internal") ? "sd" : "internal"; + $newStorage = $this->STORAGE_LOCATIONS[$newMedium]; + + // active portals should not be moved so check if the portal is currently active + if (file_exists("/www/{$name}.ep")) { + return array("success" => false, "message" => "You can not move an active portal!"); + } else + + // make sure that an SD card is inserted if it is going to be needed + if (($storage == "sd" || $newMedium == "sd") && !$this->isSDAvailable()) { + return array("success" => false, "message" => "Please insert a SD card to preform this action."); + } + + // if the portal doesn't exist then return an error + if (!file_exists($this->STORAGE_LOCATIONS[$storage] . $name)) { + return array("success" => false, "message" => "Could not find portal named {$name} on {$storage} storage"); + } + + // verify that a portal with the same name doesn't already exist in the new location. + if (file_exists($newStorage . $name)) { + return array("success" => false, "message" => "A portal named {$name} already exists on {$newMedium} storage"); + } + + // all of the above conditions should have passed so lets move the damn portal. + exec(escapeshellcmd("mv {$this->STORAGE_LOCATIONS[$storage]}{$name} {$newStorage}{$name}")); + + // verify that the directory was moved + if (file_exists($newStorage . $name)) { + return array("success" => true, "message" => "{$name} was moved to {$newMedium} storage!"); + } else { + return array("success" => false, "message" => "An error occurred moving {$name} to {$newMedium} storage"); + } } - public function activatePortal() + /** + * Set a given portal to "active". + * This means to move the portals contents to /www so it can be access via HTTP port 80. + * + * If any file with the same name as one of the files being copied to /www already exists in /www + * then that file will be renamed to {file_name}.ep_backup and restored when the portal is deactivated. + * + * If any portals are currently active when this method is called they will be deactivated. + * + * @param $name: The name of the portal to activate + * @param $storage: The storage medium the portal is on + * @return array + */ + private function activatePortal($name, $storage) { - $portalName = $this->request->name; - $storage = $this->request->storage; + $dir = $this->STORAGE_LOCATIONS[$storage]; - $dir = ($storage == "sd" ? "/sd/portals/" : "/root/portals/"); + // check if there is a currently activate portal and deactivate it. + foreach(scandir("/www") as $file) { + if (substr($file, strlen($file) - strlen(".ep")) === ".ep") { // deactivate a portal if needed + $portalName = rtrim($file, ".ep"); + $realPath = realpath("/www/{$file}"); + $storage = ($realPath == "{$this->STORAGE_LOCATIONS['internal']}{$portalName}/{$portalName}.ep") ? "internal": "sd"; + $this->deactivatePortal($portalName, $storage); + break; + } + } - $message = ""; - $portalPath = escapeshellarg($dir . $portalName); - if (file_exists($dir . $portalName)) { + $success = false; + $portalPath = escapeshellarg($dir . $name); + if (file_exists($dir . $name)) { exec("ln -s /pineapple/modules/EvilPortal/includes/api /www/captiveportal"); - $portal_files = scandir($dir . $portalName); + $portal_files = scandir($dir . $name); foreach ($portal_files as $file) { if (file_exists("/www/{$file}")) { rename("/www/{$file}", "/www/{$file}.ep_backup"); } exec("ln -s {$portalPath}/{$file} /www/{$file}"); + $success = true; } - $message = $portalName . " is now active."; + // holding off on toggle commands until a future release. + // exec("echo {$portalPath}/.enable | at now"); + $message = "{$name} is now active."; } else { - $message = "Couldn't find " . $portalPath . "."; + $message = "Couldn't find {$portalPath}."; } - $this->response = array("message" => $message); - + return array("message" => $message, "success" => $success); } - public function deactivatePortal() + /** + * Deactivate a given portal. + * + * To do this we remove all files associated with the given portal from /www + * This method also renames any files with the extension ".ep_backup" to their original name + * + * @param $name: The name of the portal to deactivate + * @param $storage: The storage medium the portal is on + * @return array + */ + private function deactivatePortal($name, $storage) { - $portalName = $this->request->name; - $storage = $this->request->storage; + $storage = ($storage == "internal" || $storage == "sd") ? $storage : "internal"; + $dir = $this->STORAGE_LOCATIONS[$storage]; - $dir = ($storage == "sd" ? "/sd/portals/" : "/root/portals/"); - - $message = "Couldn't find " . $portalName; - $deactivateSuccess = false; - if (file_exists($dir . $portalName)) { - $portal_files = scandir($dir . $portalName); - foreach ($portal_files as $file) { - unlink("/www/{$file}"); - } - $www_files = scandir("/www/"); - foreach ($www_files as $file) { - if ($this->endsWith($file, ".ep_backup")) { - rename("/www/{$file}", "/www/" . str_replace(".ep_backup", "", $file)); - } - } - $message = "Deactivated {$portalName}."; - $deactivateSuccess = true; + // if the portal is not active then return an error + if (!(file_exists("/www/{$name}.ep"))) { + return array("success" => false, "message" => "{$name} is not currently active."); } - $this->response = array("message" => $message, "deactivateSuccess" => $deactivateSuccess); - - } - - /* Credits to SteveRusin at http://php.net/manual/en/ref.strings.php */ - private function endsWith($str, $sub) - { - return (substr($str, strlen($str) - strlen($sub)) === $sub); - } - - public function handleDeletePortal() - { - $portalName = $this->request->name; - $storage = $this->request->storage; - - $dir = ($storage == "sd" ? "/sd/portals/" : "/root/portals/"); - - exec("rm -rf " . escapeshellarg($dir . $portalName)); - - $message = ""; - - if (!file_exists($dir . $portalName)) { - $message = "Deleted " . $portalName; - } else { - $message = "Error deleting " . $portalName; + // if the portal does not exist then return an error + if (!file_exists($dir . $name)) { + return array("success" => false, "message" => "Unable to find the portal {$name}."); } - $this->response = array("message" => $message); - - } - - public function submitPortalCode() - { - $code = $this->request->portalCode; - $storage = $this->request->storage; - $portalName = $this->request->name; - $fileName = $this->request->fileName; - - $dir = ($storage == "sd" ? "/sd/portals/" : "/root/portals/"); - - file_put_contents($dir . $portalName . "/" . $fileName, $code); - $message = (!file_exists($dir . $portalName . "/" . $fileName)) ? "Created " . $portalName : "Updated " . $portalName; - - $this->response = array( - "message" => $message - ); - - } - - public function handleGetPortalList() - { - if (!file_exists("/root/portals")) { - mkdir("/root/portals"); + // remove portal files from /www + foreach(scandir($dir. $name) as $file) { + unlink("/www/{$file}"); } - $all_portals = array(); - $root_portals = preg_grep('/^([^.])/', scandir("/root/portals")); - - foreach ($root_portals as $portal) { - if (!is_file($portal)) { - $active = (file_exists("/www/{$portal}.ep")); - $obj = array("title" => $portal, "location" => "internal", "active" => $active); - array_push($all_portals, $obj); + // rename any files that may have been renamed back to their original name + foreach(scandir("/www/") as $file) { + if (substr($file, strlen($file) - strlen(".ep_backup")) === ".ep_backup") { + $oldName = str_replace(".ep_backup", "", $file); + rename("/www/{$file}", "www/{$oldName}"); } } - //$active = array("title" => "splash.html", "location" => "active"); - //$active = array(); - //array_push($all_portals, $active); + // holding off on toggle commands until a future release. + // exec("echo {$dir}{$name}/.disable | at now"); - $this->response = $all_portals; + return array("success" => true, "message" => "Deactivated {$name}."); } - public function handleCreateNewPortal() + /** + * Attempt to get rules for a targeted portal + * @param $name: The name of the portal + * @param $storage: The storage medium of the portal + * @return array + */ + private function getPortalRules($name, $storage) { - $portalName = str_replace(' ', '_', $this->request->portalName); - $portalPath = "/root/portals/"; - if (!file_exists($portalPath)) { - mkdir($portalPath); - } + $storage = ($storage == "internal" || $storage == "sd") ? $storage : "internal"; + $path = $this->STORAGE_LOCATIONS[$storage]; - if (file_exists($portalPath . $portalName)) { - $this->response = array("create_success" => false, "create_message" => "A portal named {$portalName} already exists."); - return; - } - mkdir($portalPath . $portalName); - exec("cp /pineapple/modules/EvilPortal/includes/skeleton/* {$portalPath}{$portalName}/"); - file_put_contents($portalPath . $portalName . "/" . $portalName . ".ep", "DO NOT DELETE THIS"); - - $this->response = array("create_success" => true, "create_message" => "Created {$portalName}"); - - } - - public function handleEnable() - { - $response_array = array(); - if (!$this->checkAutoStart()) { - //exec("/etc/init.d/firewall disable"); - //exec("/etc/init.d/nodogsplash enable"); - copy("/pineapple/modules/EvilPortal/includes/evilportal.sh", "/etc/init.d/evilportal"); - chmod("/etc/init.d/evilportal", 0755); - exec("/etc/init.d/evilportal enable"); - $enabled = $this->checkAutoStart(); - $message = "EvilPortal is now enabled on startup."; - if (!$enabled) { - $message = "Error enabling EvilPortal on startup."; - } - - $response_array = array( - "control_success" => $enabled, - "control_message" => $message + if (is_file("{$path}{$name}/{$name}.ep")) { + $rules = $this->getValueFromJSONFile(array("targeted_rules"), "{$path}{$name}/{$name}.ep")["targeted_rules"]; + return array( + "message" => "Found portal rules", + "data" => $rules, + "success" => true ); - } else { - exec("/etc/init.d/evilportal disable"); - //exec("/etc/init.d/firewall enable"); - $enabled = !$this->checkAutoStart(); - $message = "EvilPortal now disabled on startup."; - if (!$enabled) { - $message = "Error disabling EvilPortal on startup."; - } - - $response_array = array( - "control_success" => $enabled, - "control_message" => $message - ); + return array("message" => "Unable to find portal.", "success" => false); } - $this->response = $response_array; + } - public function checkCaptivePortalRunning() + /** + * Save rules to a targeted portal + * @param $name : The name of the portal + * @param $storage : The storage medium of the portal + * @param $rules : The rules to save + * @return array + */ + private function savePortalRules($name, $storage, $rules) { - return exec("iptables -t nat -L PREROUTING | grep 172.16.42.1") == '' ? false : true; + $storage = ($storage == "internal" || $storage == "sd") ? $storage : "internal"; + $path = $this->STORAGE_LOCATIONS[$storage]; + + if (is_file("{$path}{$name}/{$name}.ep")) { + $this->updateJSONFile(array("targeted_rules" => json_decode($rules)), "{$path}{$name}/{$name}.ep")["targeted_rules"]; + return array( + "message" => "Saved portal rules", + "success" => true + ); + } else { + return array("message" => "Unable to find portal {$name}.", "success" => false); + } + } - public function startCaptivePortal() + /** + * Check if Evil Portal is currently running or not be checking iptables. + * @return bool + */ + private function checkEvilPortalRunning() { + return exec("iptables -t nat -L PREROUTING | grep 172.16.42.1") != ''; + } + /** + * Check if EvilPortal is running when the Pineapple starts or not + * @return bool + */ + public function checkAutoStart() + { + return !(exec("ls /etc/rc.d/ | grep evilportal") == ''); + } + + /** + * Grant a client access to the internet and stop blocking them with the captive portal + * @param $client: The IP address of the client to authorize + */ + private function authorizeClient($client) + { + exec("iptables -t nat -I PREROUTING -s {$client} -j ACCEPT"); +// exec("{$this->BASE_EP_COMMAND} add {$client}"); + $this->writeFileContents($this->CLIENTS_FILE, "{$client}", true); + } + + /** + * Revoke a clients access to the internet and start blocking them with the captive portal + * @param $client: The IP address of the client to revoke + */ + private function revokeClient($client) + { +// exec("{$this->BASE_EP_COMMAND} remove {$client}"); + exec("iptables -t nat -D PREROUTING -s {$client}"); + exec("iptables -t nat -D PREROUTING -s {$client} -j ACCEPT"); + } + + /** + * Start the captive portal portion of Evil Portal + * + * All clients in the White List should be automatically authorized when Evil Portal starts. + * + * @return array + */ + private function startEvilPortal() + { // Delete client tracking file if it exists if (file_exists($this->CLIENTS_FILE)) { unlink($this->CLIENTS_FILE); @@ -338,199 +547,176 @@ class EvilPortal extends Module $allowedClients = file_get_contents($this->ALLOWED_FILE); file_put_contents($this->CLIENTS_FILE, $allowedClients); - // Configure other rules - exec("iptables -t nat -A PREROUTING -s 172.16.42.0/24 -p tcp --dport 80 -j DNAT --to-destination 172.16.42.1:80"); - exec("iptables -A INPUT -p tcp --dport 53 -j ACCEPT"); +// // Configure other rules + exec("iptables -A INPUT -s 172.16.42.0/24 -j DROP"); + exec("iptables -A OUTPUT -s 172.16.42.0/24 -j DROP"); + exec("iptables -A INPUT -s 172.16.42.0/24 -p udp --dport 53 -j ACCEPT"); + + // Allow the pineapple + exec("iptables -A INPUT -s 172.16.42.1 -j ACCEPT"); + exec("iptables -A OUTPUT -s 172.16.42.1 -j ACCEPT"); + + //exec("iptables -A INPUT -i br-lan -p tcp --dport 443 -j DROP"); + //exec("iptables -t nat -A PREROUTING -i br-lan -j DROP"); + + exec("iptables -t nat -A PREROUTING -i br-lan -p tcp --dport 80 -j DNAT --to-destination 172.16.42.1:80"); + exec("iptables -t nat -A POSTROUTING -j MASQUERADE"); + +// exec("{$this->BASE_EP_COMMAND} init"); // Add rule for each allowed client $lines = file($this->CLIENTS_FILE); foreach ($lines as $client) { $this->authorizeClient($client); - //exec("iptables -t nat -I PREROUTING -s {$client} -j ACCEPT"); } - // Drop everything else - exec("iptables -I INPUT -p tcp --dport 443 -j DROP"); - - return $this->checkCaptivePortalRunning(); + $success = $this->checkEvilPortalRunning(); + $message = ($success) ? "EvilPortal is now up and running!" : "EvilPortal failed to start."; + return array("success" => $success, "message" => $message); } - private function authorizeClient($client) - { - exec("iptables -t nat -I PREROUTING -s {$client} -j ACCEPT"); - } - - private function revokeClient($client) - { - exec("iptables -t nat -D PREROUTING -s {$client}"); - exec("iptables -t nat -D PREROUTING -s {$client} -j ACCEPT"); - } - - public function stopCaptivePortal() + /** + * Stop the captive portal portion of Evil Portal from running + * @return mixed + */ + private function stopEvilPortal() { if (file_exists($this->CLIENTS_FILE)) { $lines = file($this->CLIENTS_FILE); foreach ($lines as $client) { $this->revokeClient($client); - //exec("iptables -t nat -D PREROUTING -s {$client} -j ACCEPT"); } unlink($this->CLIENTS_FILE); } - exec("iptables -t nat -D PREROUTING -s 172.16.42.0/24 -p tcp --dport 80 -j DNAT --to-destination 172.16.42.1:80"); + exec("iptables -t nat -D PREROUTING -i br-lan -p tcp --dport 80 -j DNAT --to-destination 172.16.42.1:80"); exec("iptables -D INPUT -p tcp --dport 53 -j ACCEPT"); exec("iptables -D INPUT -j DROP"); - return $this->checkCaptivePortalRunning(); +// exec("{$this->BASE_EP_COMMAND} purge"); + $success = !$this->checkEvilPortalRunning(); + $message = ($success) ? "EvilPortal has stopped running" : "There was an issue stopping EvilPortal"; + + return array("success" => $success, "messsage" => $message); } - public function handleRunning() + /** + * If Evil Portal is running then stop it, otherwise start it. + */ + private function toggleCaptivePortal() { + // Make the file executable. In the future the `module` command should do this for us. + chmod("/pineapple/modules/EvilPortal/executable/executable", 0755); + + return $this->checkEvilPortalRunning() ? $this->stopEvilPortal() : $this->startEvilPortal(); + } + + /** + * Enable or Disable Evil Portal to run when the pineapple boots. + * If Evil Portal is supposed to run when this method is called then it will be disabled + * If Evil Portal is not supposed to run when this method is called then it will be enabled on boot. + * + * This method does not start nor stop current running instances of Evil Portal. + * + * @return array + */ + private function autoStartEvilPortal() { - $response_array = array(); - if (!$this->checkCaptivePortalRunning()) { - //exec("/etc/init.d/nodogsplash start"); - //$running = $this->checkRunning("nodogsplash"); - $running = $this->startCaptivePortal(); - $message = "Started EvilPortal."; - if (!$running) { - $message = "Error starting EvilPortal."; - } + // if EvilPortal is not set to start on startup then set that shit + if (!$this->checkAutoStart()) { + copy("/pineapple/modules/EvilPortal/includes/evilportal.sh", "/etc/init.d/evilportal"); + chmod("/etc/init.d/evilportal", 0755); + exec("/etc/init.d/evilportal enable"); + $enabled = $this->checkAutoStart(); + $message = ($enabled) ? "EvilPortal is now enabled on start up" : "Error enabling EvilPotal on startup."; - $response_array = array( - "control_success" => $running, - "control_message" => $message + return array( + "success" => $enabled, + "message" => $message ); - } else { - //exec("/etc/init.d/nodogsplash stop"); - //sleep(1); - //$running = !$this->checkRunning("nodogsplash"); - $running = !$this->stopCaptivePortal(); - $message = "Stopped EvilPortal."; - if (!$running) { - $message = "Error stopping EvilPortal."; - } + } else { // if evil portal is set to run on startup then disable that shit. + exec("/etc/init.d/evilportal disable"); + $enabled = !$this->checkAutoStart(); + $message = ($enabled) ? "EvilPortal is now disabled on startup." : "Error disabling EvilPortal on startup."; - $response_array = array( - "control_success" => $running, - "control_message" => $message + return array( + "success" => $enabled, + "message" => $message ); } - - $this->response = $response_array; } - public function getList() + /** + * Removes a client from either the whiteList or authorizedList + * @param $clientIP: The IP address of the client to be removed + * @param $listName: The name of the list to remove the client from + * @return array + */ + private function removeFromList($clientIP, $listName) { - $response_array = array(); - $contents = null; - $message = "Successful"; - switch ($this->request->listName) { + $valid = preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/', $clientIP); + + // if the IP address is invalid then return an error message + if (!$valid) { + return array("success" => false, "message" => "Invalid IP Address."); + } + + $success = true; + switch ($listName) { case "whiteList": - if (!file_exists($this->ALLOWED_FILE)) { - $message = "White List file doesn't exist."; - } else { - $contents = file_get_contents($this->ALLOWED_FILE); - $contents = ($contents == null) ? "No White Listed Clients" : $contents; - } + $data = file_get_contents($this->ALLOWED_FILE); + $data = str_replace("{$clientIP}\n", '', $data); + file_put_contents("/root/removeFromList", $data); + file_put_contents($this->ALLOWED_FILE, $data); break; case "accessList": - if (!file_exists($this->CLIENTS_FILE)) { - $contents = "No Authorized Clients."; - } else { - $contents = file_get_contents($this->CLIENTS_FILE); - $contents = ($contents == null) ? "No Authorized Clients." : $contents; - } + $data = file_get_contents($this->CLIENTS_FILE); + $data = str_replace("{$clientIP}\n", '', $data); + file_put_contents($this->CLIENTS_FILE, $data); + $this->revokeClient($clientIP); break; - } - if ($contents != null) { - $response_array = array( - "list_success" => true, - "list_contents" => $contents, - "list_message" => $message - ); - } else { - $response_array = array("list_success" => false, "list_contents" => "", "list_message" => $message); - } + default: + $success = false; + break; - $this->response = $response_array; + } + $message = ($success) ? "Successfully removed {$clientIP} from {$listName}" : "Error removing {$clientIP} from {$listName}"; + return array("success" => $success, "message" => $message); } - public function addToList() - { - $valid = preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/', $this->request->clientIP); - if ($valid) { - switch ($this->request->listName) { - case "whiteList": - file_put_contents($this->ALLOWED_FILE, $this->request->clientIP . "\n", FILE_APPEND); - $this->response = array("add_success" => true, "add_message" => "Successful"); - break; - - case "accessList": - file_put_contents($this->CLIENTS_FILE, $this->request->clientIP . "\n", FILE_APPEND); - $this->authorizeClient($this->request->clientIP); - $this->response = array("add_success" => true, "add_message" => "Successful"); - break; - - default: - $this->response = array("add_success" => false, "add_message" => "Unkown list."); - break; + /** + * Add a value to a json file + * @param $keyValueArray: The data to add to the file + * @param $file: The file to write the content to. + */ + private function updateJSONFile($keyValueArray, $file) { + $data = json_decode(file_get_contents($file), true); + foreach ($data as $key => $value) { + if (isset($keyValueArray[$key])) { + $data[$key] = $keyValueArray[$key]; } - } else { - $this->response = array("add_success" => false, "add_message" => "Invalid IP Address."); } - + file_put_contents($file, json_encode($data)); } - public function removeFromList() - { - $valid = preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/', $this->request->clientIP); - if ($valid) { - switch ($this->request->listName) { - case "whiteList": - $data = file_get_contents($this->ALLOWED_FILE); - $data = str_replace($this->request->clientIP . "\n", '', $data); - file_put_contents($this->ALLOWED_FILE, $data); - $this->response = array("remove_success" => true, "remove_message" => "Successful"); - break; - - case "accessList": - $data = file_get_contents($this->CLIENTS_FILE); - $data = str_replace($this->request->clientIP . "\n", '', $data); - file_put_contents($this->CLIENTS_FILE, $data); - $this->revokeClient($this->request->clientIP); - $this->response = array("remove_success" => true, "remove_message" => "Successful"); - break; - - default: - $this->response = array("remove_success" => false, "remove_message" => "Unkown list."); - break; - + /** + * Get values from a JSON file + * @param $keys: The key or keys you wish to get the value from + * @param $file: The file to that contains the JSON data. + * @return array + */ + private function getValueFromJSONFile($keys, $file) { + $data = json_decode(file_get_contents($file), true); + $values = array(); + foreach ($data as $key => $value) { + if (in_array($key, $keys)) { + $values[$key] = $value; } - } else { - $this->response = array("remove_success" => false, "remove_message" => "Invalid IP Address."); - } - } - - public function getControlValues() - { - $this->response = array( - //"dependencies" => true, - "running" => $this->checkCaptivePortalRunning(), - "autostart" => $this->checkAutoStart() - ); - } - - public function checkAutoStart() - { - if (exec("ls /etc/rc.d/ | grep evilportal") == '') { - return false; - } else { - return true; } + return $values; } } diff --git a/EvilPortal/executable/executable b/EvilPortal/executable/executable old mode 100644 new mode 100755 index 0f186ae..079cab0 --- a/EvilPortal/executable/executable +++ b/EvilPortal/executable/executable @@ -1,133 +1,99 @@ -#!/usr/bin/python +#!/bin/bash -# -# Evil Portal -# Newbi3 -# This is the python control script to handle all iptables things for creating and managing a Captive Portal -# +#Modified by oXis for the Wifi Pineapple (OpenWRT) -import sys -import os +# Written by Sitwon and The Doctor. +# Copyright (C) 2013 Project Byzantium +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. +arp () { cat /proc/net/arp; } # arp function -CLIENTS_FILE = "/tmp/EVILPORTAL_CLIENTS.txt" -WHITE_LIST = ["172.16.42.42"] +IPTABLES=/usr/sbin/iptables +ARP=arp +IP=172.16.42.1 +case "$1" in + 'init') -def revoke_client(ip_address=None): - global CLIENTS_FILE + # Convert the IP address of the client interface into a netblock. + CLIENTNET=`echo $IP | sed 's/1$/0\/24/'` - if not ip_address: - print "An ipaddress is expected." - return + # Exempt traffic which does not originate from the client network. + $IPTABLES -t mangle -I PREROUTING -p all ! -s $CLIENTNET -j RETURN - f = open(CLIENTS_FILE, 'r') - lines = f.readlines() - f.close() + # Traffic not coming from an accepted user gets marked 99. + $IPTABLES -t mangle -A fwmark -j MARK --set-mark 99 - f = open(CLIENTS_FILE, 'w') - for line in lines: - if line == ip_address + "\n": - os.system("iptables -t nat -D PREROUTING -s " + line.rstrip('\n') + " -j ACCEPT") - else: - f.write(line) - f.close() + # Traffic which has been marked 99 and is headed for 80/TCP or 443/TCP + # should be redirected to the captive portal web server. + $IPTABLES -t nat -A prerouting_rule -m mark --mark 99 -p tcp --dport 80 -j DNAT --to-destination $IP:80 + # Need to activate HTTPS on the nginx server of the PineAP, so for now HTTPS traffic is dropped. + #$IPTABLES -t nat -A prerouting_rule -m mark --mark 99 -p tcp --dport 443 -j DNAT --to-destination $IP:443 + # for use with dns spoff + $IPTABLES -t filter -A forwarding_rule -p udp --dport 53 -j ACCEPT + $IPTABLES -t nat -A prerouting_rule -m mark --mark 99 -p udp --dport 53 -j DNAT --to-destination $IP:53 -def authorize_client(ip_address=None): - global CLIENTS_FILE + $IPTABLES -t filter -A input_rule -p tcp --dport 80 -j ACCEPT #Webserver + #$IPTABLES -t filter -A input_rule -p tcp --dport 443 -j ACCEPT #Webserver + $IPTABLES -t filter -A input_rule -p tcp --dport 1471 -j ACCEPT #PineAP admin page + $IPTABLES -t filter -A input_rule -p tcp --dport 22 -j ACCEPT #SSH - if not ip_address: - print "An ipaddress is expected." - return + # All other traffic which is marked 99 is just dropped + $IPTABLES -t filter -A forwarding_rule -m mark --mark 99 -j DROP + # Even on INPUT rule + $IPTABLES -t filter -A input_rule -m mark --mark 99 -j DROP - f = open(CLIENTS_FILE, 'a+') - lines = f.readlines() - if ip_address + "\n" not in lines: - os.system("iptables -t nat -I PREROUTING -s " + ip_address + " -j ACCEPT") - f.write(ip_address + "\n") - f.close() + exit 0 + ;; + 'add') + # $2: IP address of client. + CLIENT=$2 + # Isolate the MAC address of the client in question. + CLIENTMAC=`$ARP -n | grep ':' | grep $CLIENT | awk '{print $4}'` -def stop_evilportal(): - """ - stop_evilportal - Stop EvilPortals - """ - global CLIENTS_FILE, WHITE_LIST + # Add the MAC address of the client to the whitelist, so it'll be able + # to access the mesh even if its IP address changes. + $IPTABLES -t mangle -I fwmark -m mac --mac-source $CLIENTMAC -j RETURN + $IPTABLES -A INPUT -m mac --mac-source 74:da:38:5a:03:66 -p udp --dport 53 -j ACCEPT - if os.path.isfile(CLIENTS_FILE): - # Remove rule for each accepted client - [os.system("iptables -t nat -D PREROUTING -s " + line.rstrip('\n') + " -j ACCEPT") for line in open(CLIENTS_FILE, 'r')] + exit 0 + ;; + 'remove') + # $2: IP address of client. + CLIENT=$2 - # Delete the clients file - os.remove(CLIENTS_FILE) + # Isolate the MAC address of the client in question. + CLIENTMAC=`$ARP -n | grep ':' | grep $CLIENT | awk '{print $4}'` - # Stop HTTP Redirection - os.system("iptables -t nat -D PREROUTING -s 172.16.42.0/24 -p tcp --dport 80 -j DNAT --to-destination 172.16.42.1:80") + # Delete the MAC address of the client from the whitelist. + $IPTABLES -t mangle -D fwmark -m mac --mac-source $CLIENTMAC -j RETURN - # Remove DNS Policy - os.system("iptables -D INPUT -p tcp --dport 53 -j ACCEPT") + exit 0 + ;; + 'purge') + CLIENTNET=`echo $IP | sed 's/1$/0\/24/'` + # Purge the user defined chains + $IPTABLES -t mangle -F fwmark + $IPTABLES -t nat -F prerouting_rule + $IPTABLES -t filter -F input_rule + $IPTABLES -t filter -F forwarding_rule + $IPTABLES -t mangle -D PREROUTING -p all ! -s $CLIENTNET -j RETURN + $IPTABLES -t nat -D prerouting_rule -m mark --mark 99 -p udp --dport 53 -j DNAT --to-destination $IP:53 -def start_evilportal(): - """ - start_evilportal - Start EvilPortals IP table based captive portal - """ - global CLIENTS_FILE, WHITE_LIST + exit 0 + ;; + 'list') + # Display the currently running IP tables ruleset. + $IPTABLES --list -t nat -n + $IPTABLES --list -t mangle -n + $IPTABLES --list -t filter -n - if os.path.isfile(CLIENTS_FILE): - os.remove(CLIENTS_FILE) - - # Make sure forwarding is enabled which it should be but just to be sure do it here - os.system("echo 1 > /proc/sys/net/ipv4/ip_forward") - - # Setup the iptables - # Set white listed clients - f = open(CLIENTS_FILE, "w") - for client in WHITE_LIST: - os.system("iptables -A INPUT -s " + client + " -j ACCEPT") - f.write(client + "\n") - f.close() - - # Redirect all web traffic to port 80 on the pineapple - os.system("iptables -t nat -A PREROUTING -s 172.16.42.0/24 -p tcp --dport 80 -j DNAT --to-destination 172.16.42.1:80") - - # Accept dns - os.system("iptables -A INPUT -p tcp --dport 53 -j ACCEPT") - - -def handler(args=None): - cmd = None - param = None - if args is not None: - try: - cmd = args[1].lower() - except IndexError as e: - cmd = "help" - try: - param = args[2].lower() - except IndexError as e: - pass - else: - cmd = "start" - - commands = {"start": "Start EvilPortal", "stop": "Stop EvilPortal", "help": "What you are reading"} - - if cmd == "start": - start_evilportal() - elif cmd == "stop": - stop_evilportal() - elif cmd == "authorize": - authorize_client(param) - elif cmd == "help": - print "-"*20 - print "Evil Portal" - print "-"*20 - for command, description in commands.iteritems(): - print command + ":\t" + description - - -if __name__ == '__main__': - handler(sys.argv) + exit 0 + ;; + *) + echo "USAGE: $0 {initialize|add |remove |purge|list}" + exit 0 + esac \ No newline at end of file diff --git a/EvilPortal/includes/api/Portal.php b/EvilPortal/includes/api/Portal.php index a5862b6..cd46422 100644 --- a/EvilPortal/includes/api/Portal.php +++ b/EvilPortal/includes/api/Portal.php @@ -7,6 +7,7 @@ abstract class Portal protected $error; protected $AUTHORIZED_CLIENTS_FILE = "/tmp/EVILPORTAL_CLIENTS.txt"; + private $BASE_EP_COMMAND = 'module EvilPortal'; public function __construct($request) { @@ -24,44 +25,80 @@ abstract class Portal } } + /** + * Run a command in the background and don't wait for it to finish. + * @param $command: The command to run + */ + protected function execBackground($command) + { + exec("echo \"{$command}\" | at now"); + } + + /** + * Creates an iptables rule allowing the client to access the internet and writes them to the authorized clients. + * Override this method to add other authorization steps validation. + * @param $clientIP: The IP address of the client to authorize + * @return bool: True if the client was successfully authorized otherwise false. + */ protected function authorizeClient($clientIP) { if (!$this->isClientAuthorized($clientIP)) { exec("iptables -t nat -I PREROUTING -s {$clientIP} -j ACCEPT"); +// exec("{$this->BASE_EP_COMMAND} add {$clientIP}"); file_put_contents($this->AUTHORIZED_CLIENTS_FILE, "{$clientIP}\n", FILE_APPEND); - $this->redirect(); - } else { - return false; } + return true; } + /** + * Handle client authorization here. + * By default it just checks that the redirection target is in the request. + * Override this to perform your own validation. + */ protected function handleAuthorization() { if (isset($this->request->target)) { $this->authorizeClient($_SERVER['REMOTE_ADDR']); - $this->showSuccess(); + $this->onSuccess(); + $this->redirect(); } elseif ($this->isClientAuthorized($_SERVER['REMOTE_ADDR'])) { - $this->showSuccess(); + $this->redirect(); } else { $this->showError(); } } + /** + * Where to redirect to on successful authorization. + */ protected function redirect() { header("Location: {$this->request->target}", true, 302); } - protected function showSuccess() + /** + * Override this to do something when the client is successfully authorized. + * By default it just notifies the Web UI. + */ + protected function onSuccess() { - echo "You have been authorized successfully."; + $this->execBackground("notify New client authorized through EvilPortal!"); } + /** + * If an error occurs then do something here. + * Override to provide your own functionality. + */ protected function showError() { echo "You have not been authorized."; } + /** + * Checks if the client has been authorized. + * @param $clientIP: The IP of the client to check. + * @return bool|int: True if the client is authorized else false. + */ protected function isClientAuthorized($clientIP) { $authorizeClients = file_get_contents($this->AUTHORIZED_CLIENTS_FILE); diff --git a/EvilPortal/includes/skeleton/.disable b/EvilPortal/includes/skeleton/.disable new file mode 100644 index 0000000..93d79c3 --- /dev/null +++ b/EvilPortal/includes/skeleton/.disable @@ -0,0 +1,4 @@ +#!/bin/bash + +# Commands in this file are ran when a portal is de-activated. +# You can use any interpreter you want to, the default is bash. diff --git a/EvilPortal/includes/skeleton/.enable b/EvilPortal/includes/skeleton/.enable new file mode 100644 index 0000000..b9c7ddb --- /dev/null +++ b/EvilPortal/includes/skeleton/.enable @@ -0,0 +1,4 @@ +#!/bin/bash + +# Commands in this file are ran when a portal is activated and when Evil Portal startsup on boot. +# You can use any interpreter you want to, the default is bash. diff --git a/EvilPortal/includes/skeleton/MyPortal.php b/EvilPortal/includes/skeleton/MyPortal.php index 16e4ece..8c610a8 100644 --- a/EvilPortal/includes/skeleton/MyPortal.php +++ b/EvilPortal/includes/skeleton/MyPortal.php @@ -5,18 +5,27 @@ class MyPortal extends Portal public function handleAuthorization() { + // handle form input or other extra things there + // Call parent to handle basic authorization first parent::handleAuthorization(); - // Check for other form data here } - public function showSuccess() + /** + * Override this to do something when the client is successfully authorized. + * By default it just notifies the Web UI. + */ + public function onSuccess() { // Calls default success message - parent::showSuccess(); + parent::onSuccess(); } + /** + * If an error occurs then do something here. + * Override to provide your own functionality. + */ public function showError() { // Calls default error message diff --git a/EvilPortal/includes/skeleton/helper.php b/EvilPortal/includes/skeleton/helper.php new file mode 100644 index 0000000..046684c --- /dev/null +++ b/EvilPortal/includes/skeleton/helper.php @@ -0,0 +1,45 @@ + Evil Portal - + + + + -
+

Evil Portal

-

This is the default Evil Portal page

+

This is the default Evil Portal page.

+

The SSID you are connected to is

+

Your host name is

+

Your MAC Address is

+

Your internal IP address is

-
+
-
+
- \ No newline at end of file + diff --git a/EvilPortal/includes/skeleton/portalinfo.json b/EvilPortal/includes/skeleton/portalinfo.json new file mode 100644 index 0000000..9ca5352 --- /dev/null +++ b/EvilPortal/includes/skeleton/portalinfo.json @@ -0,0 +1,4 @@ +{ + "name": null, + "type": "basic" +} \ No newline at end of file diff --git a/EvilPortal/includes/targeted_skeleton/.disable b/EvilPortal/includes/targeted_skeleton/.disable new file mode 100644 index 0000000..93d79c3 --- /dev/null +++ b/EvilPortal/includes/targeted_skeleton/.disable @@ -0,0 +1,4 @@ +#!/bin/bash + +# Commands in this file are ran when a portal is de-activated. +# You can use any interpreter you want to, the default is bash. diff --git a/EvilPortal/includes/targeted_skeleton/.enable b/EvilPortal/includes/targeted_skeleton/.enable new file mode 100644 index 0000000..b9c7ddb --- /dev/null +++ b/EvilPortal/includes/targeted_skeleton/.enable @@ -0,0 +1,4 @@ +#!/bin/bash + +# Commands in this file are ran when a portal is activated and when Evil Portal startsup on boot. +# You can use any interpreter you want to, the default is bash. diff --git a/EvilPortal/includes/targeted_skeleton/MyPortal.php b/EvilPortal/includes/targeted_skeleton/MyPortal.php new file mode 100644 index 0000000..329daa1 --- /dev/null +++ b/EvilPortal/includes/targeted_skeleton/MyPortal.php @@ -0,0 +1,33 @@ + + + + + Evil Portal + + + + + + + +
+

Evil Portal

+

This is the default Evil Portal page.

+

The SSID you are connected to is

+

Your host name is

+

Your MAC Address is

+

Your internal IP address is

+ +
+ + +
+ +
+ + + + \ No newline at end of file diff --git a/EvilPortal/includes/targeted_skeleton/helper.php b/EvilPortal/includes/targeted_skeleton/helper.php new file mode 100644 index 0000000..046684c --- /dev/null +++ b/EvilPortal/includes/targeted_skeleton/helper.php @@ -0,0 +1,45 @@ + getClientMac($_SERVER['REMOTE_ADDR']), + "ssid" => getClientSSID($_SERVER['REMOTE_ADDR']), + "hostname" => getClientHostName($_SERVER['REMOTE_ADDR']), + "useragent" => $_SERVER['HTTP_USER_AGENT'] +]; + +// Read the json +$jsonData = json_decode(file_get_contents("{$PORTAL_NAME}.ep"), true); +$routeData = $jsonData['targeted_rules']; + +// This variable represents the page to include +$includePage = null; + +// Check rules to find the page +foreach ($routeData['rule_order'] as $key) { + $includePage = handle_rule($routeData['rules'][$key], $MAPPED_RULES[$key]); + if ($includePage != null) { + include $includePage; + break; + } +} + +// We have to display something. +// If the includePage variable is still null after checking the rules +// then include the default page. +if ($includePage == null) { + include $routeData['default']; +} + +/** + * Checks if a given rule matches a given value + * @param $rules: The rules to check the client data against + * @param $client_data: The data to check if the rules match + * @return string: If a rule matches it returns the page to include, null otherwise + */ +function handle_rule($rules, $client_data) { + $return_value = null; + foreach ($rules as $key => $val) { + switch($key) { + case "exact": // exact matches + if (isset($val[$client_data])) { + $return_value = $val[$client_data]; + break 2; // break out of the loop + } + break 1; + + case "regex": // regex matches + foreach($val as $expression => $destination) { + if (preg_match($expression, $client_data)) { + $return_value = $destination; + break 1; // match was found. Exit this loop + } + + if ($return_value != null) + break 2; // break out of the main loop + } + break 1; + } + } + return $return_value; +} diff --git a/EvilPortal/includes/targeted_skeleton/jquery-2.2.1.min.js b/EvilPortal/includes/targeted_skeleton/jquery-2.2.1.min.js new file mode 100644 index 0000000..349030d --- /dev/null +++ b/EvilPortal/includes/targeted_skeleton/jquery-2.2.1.min.js @@ -0,0 +1,4 @@ +/*! jQuery v2.2.1 | (c) jQuery Foundation | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=a.document,e=c.slice,f=c.concat,g=c.push,h=c.indexOf,i={},j=i.toString,k=i.hasOwnProperty,l={},m="2.2.1",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return e.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:e.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a){return n.each(this,a)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(e.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:g,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){var b=a&&a.toString();return!n.isArray(a)&&b-parseFloat(b)+1>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!k.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?i[j.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=d.createElement("script"),b.text=a,d.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(s(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):g.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:h.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,g=0,h=[];if(s(a))for(d=a.length;d>g;g++)e=b(a[g],g,c),null!=e&&h.push(e);else for(g in a)e=b(a[g],g,c),null!=e&&h.push(e);return f.apply([],h)},guid:1,proxy:function(a,b){var c,d,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(d=e.call(arguments,2),f=function(){return a.apply(b||this,d.concat(e.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:l}),"function"==typeof Symbol&&(n.fn[Symbol.iterator]=c[Symbol.iterator]),n.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){i["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=!!a&&"length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ga(),z=ga(),A=ga(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+M+"))|)"+L+"*\\]",O=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+N+")*)|.*)\\)|)",P=new RegExp(L+"+","g"),Q=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),R=new RegExp("^"+L+"*,"+L+"*"),S=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),T=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),U=new RegExp(O),V=new RegExp("^"+M+"$"),W={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M+"|[*])"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},X=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Z=/^[^{]+\{\s*\[native \w/,$=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,_=/[+~]/,aa=/'|\\/g,ba=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),ca=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},da=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(ea){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fa(a,b,d,e){var f,h,j,k,l,o,r,s,w=b&&b.ownerDocument,x=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==x&&9!==x&&11!==x)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==x&&(o=$.exec(a)))if(f=o[1]){if(9===x){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(w&&(j=w.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(o[2])return H.apply(d,b.getElementsByTagName(a)),d;if((f=o[3])&&c.getElementsByClassName&&b.getElementsByClassName)return H.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==x)w=b,s=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(aa,"\\$&"):b.setAttribute("id",k=u),r=g(a),h=r.length,l=V.test(k)?"#"+k:"[id='"+k+"']";while(h--)r[h]=l+" "+qa(r[h]);s=r.join(","),w=_.test(a)&&oa(b.parentNode)||b}if(s)try{return H.apply(d,w.querySelectorAll(s)),d}catch(y){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(Q,"$1"),b,d,e)}function ga(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ha(a){return a[u]=!0,a}function ia(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ja(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function ka(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function la(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function na(a){return ha(function(b){return b=+b,ha(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function oa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=fa.support={},f=fa.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fa.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ia(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ia(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Z.test(n.getElementsByClassName),c.getById=ia(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return"undefined"!=typeof b.getElementsByClassName&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=Z.test(n.querySelectorAll))&&(ia(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ia(function(a){var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Z.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ia(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",O)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Z.test(o.compareDocumentPosition),t=b||Z.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return ka(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?ka(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},fa.matches=function(a,b){return fa(a,null,null,b)},fa.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(T,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fa(b,n,null,[a]).length>0},fa.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fa.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fa.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fa.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fa.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fa.selectors={cacheLength:50,createPseudo:ha,match:W,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ba,ca),a[3]=(a[3]||a[4]||a[5]||"").replace(ba,ca),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fa.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fa.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return W.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&U.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ba,ca).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fa.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(P," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fa.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ha(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ha(function(a){var b=[],c=[],d=h(a.replace(Q,"$1"));return d[u]?ha(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ha(function(a){return function(b){return fa(a,b).length>0}}),contains:ha(function(a){return a=a.replace(ba,ca),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ha(function(a){return V.test(a||"")||fa.error("unsupported lang: "+a),a=a.replace(ba,ca).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Y.test(a.nodeName)},input:function(a){return X.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:na(function(){return[0]}),last:na(function(a,b){return[b-1]}),eq:na(function(a,b,c){return[0>c?c+b:c]}),even:na(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:na(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:na(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:na(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function ra(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j,k=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(j=b[u]||(b[u]={}),i=j[b.uniqueID]||(j[b.uniqueID]={}),(h=i[d])&&h[0]===w&&h[1]===f)return k[2]=h[2];if(i[d]=k,k[2]=a(b,c,g))return!0}}}function sa(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ta(a,b,c){for(var d=0,e=b.length;e>d;d++)fa(a,b[d],c);return c}function ua(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function va(a,b,c,d,e,f){return d&&!d[u]&&(d=va(d)),e&&!e[u]&&(e=va(e,f)),ha(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ta(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ua(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ua(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ua(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function wa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ra(function(a){return a===b},h,!0),l=ra(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[ra(sa(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return va(i>1&&sa(m),i>1&&qa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(Q,"$1"),c,e>i&&wa(a.slice(i,e)),f>e&&wa(a=a.slice(e)),f>e&&qa(a))}m.push(c)}return sa(m)}function xa(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=F.call(i));u=ua(u)}H.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&fa.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ha(f):f}return h=fa.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xa(e,d)),f.selector=a}return f},i=fa.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ba,ca),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=W.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ba,ca),_.test(j[0].type)&&oa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qa(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||_.test(a)&&oa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ia(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ia(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ja("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ia(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ja("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ia(function(a){return null==a.getAttribute("disabled")})||ja(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fa}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.uniqueSort=n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},v=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},w=n.expr.match.needsContext,x=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,y=/^.[^:#\[\.,]*$/;function z(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(y.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return h.call(b,a)>-1!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(z(this,a||[],!1))},not:function(a){return this.pushStack(z(this,a||[],!0))},is:function(a){return!!z(this,"string"==typeof a&&w.test(a)?n(a):a||[],!1).length}});var A,B=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=n.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||A,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:B.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),x.test(e[1])&&n.isPlainObject(b))for(e in b)n.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&f.parentNode&&(this.length=1,this[0]=f),this.context=d,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?void 0!==c.ready?c.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};C.prototype=n.fn,A=n(d);var D=/^(?:parents|prev(?:Until|All))/,E={children:!0,contents:!0,next:!0,prev:!0};n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=w.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?h.call(n(a),this[0]):h.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.uniqueSort(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function F(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return u(a,"parentNode")},parentsUntil:function(a,b,c){return u(a,"parentNode",c)},next:function(a){return F(a,"nextSibling")},prev:function(a){return F(a,"previousSibling")},nextAll:function(a){return u(a,"nextSibling")},prevAll:function(a){return u(a,"previousSibling")},nextUntil:function(a,b,c){return u(a,"nextSibling",c)},prevUntil:function(a,b,c){return u(a,"previousSibling",c)},siblings:function(a){return v((a.parentNode||{}).firstChild,a)},children:function(a){return v(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(E[a]||n.uniqueSort(e),D.test(a)&&e.reverse()),this.pushStack(e)}});var G=/\S+/g;function H(a){var b={};return n.each(a.match(G)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?H(a):n.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),h>=c&&h--}),this},has:function(a){return a?n.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().progress(c.notify).done(c.resolve).fail(c.reject):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=e.call(arguments),d=c.length,f=1!==d||a&&n.isFunction(a.promise)?d:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?e.call(arguments):d,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(d>1)for(i=new Array(d),j=new Array(d),k=new Array(d);d>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().progress(h(b,j,i)).done(h(b,k,c)).fail(g.reject):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(d,[n]),n.fn.triggerHandler&&(n(d).triggerHandler("ready"),n(d).off("ready"))))}});function J(){d.removeEventListener("DOMContentLoaded",J),a.removeEventListener("load",J),n.ready()}n.ready.promise=function(b){return I||(I=n.Deferred(),"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(n.ready):(d.addEventListener("DOMContentLoaded",J),a.addEventListener("load",J))),I.promise(b)},n.ready.promise();var K=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)K(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},L=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function M(){this.expando=n.expando+M.uid++}M.uid=1,M.prototype={register:function(a,b){var c=b||{};return a.nodeType?a[this.expando]=c:Object.defineProperty(a,this.expando,{value:c,writable:!0,configurable:!0}),a[this.expando]},cache:function(a){if(!L(a))return{};var b=a[this.expando];return b||(b={},L(a)&&(a.nodeType?a[this.expando]=b:Object.defineProperty(a,this.expando,{value:b,configurable:!0}))),b},set:function(a,b,c){var d,e=this.cache(a);if("string"==typeof b)e[b]=c;else for(d in b)e[d]=b[d];return e},get:function(a,b){return void 0===b?this.cache(a):a[this.expando]&&a[this.expando][b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=a[this.expando];if(void 0!==f){if(void 0===b)this.register(a);else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in f?d=[b,e]:(d=e,d=d in f?[d]:d.match(G)||[])),c=d.length;while(c--)delete f[d[c]]}(void 0===b||n.isEmptyObject(f))&&(a.nodeType?a[this.expando]=void 0:delete a[this.expando])}},hasData:function(a){var b=a[this.expando];return void 0!==b&&!n.isEmptyObject(b)}};var N=new M,O=new M,P=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Q=/[A-Z]/g;function R(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Q,"-$&").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:P.test(c)?n.parseJSON(c):c}catch(e){}O.set(a,b,c); +}else c=void 0;return c}n.extend({hasData:function(a){return O.hasData(a)||N.hasData(a)},data:function(a,b,c){return O.access(a,b,c)},removeData:function(a,b){O.remove(a,b)},_data:function(a,b,c){return N.access(a,b,c)},_removeData:function(a,b){N.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=O.get(f),1===f.nodeType&&!N.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),R(f,d,e[d])));N.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){O.set(this,a)}):K(this,function(b){var c,d;if(f&&void 0===b){if(c=O.get(f,a)||O.get(f,a.replace(Q,"-$&").toLowerCase()),void 0!==c)return c;if(d=n.camelCase(a),c=O.get(f,d),void 0!==c)return c;if(c=R(f,d,void 0),void 0!==c)return c}else d=n.camelCase(a),this.each(function(){var c=O.get(this,d);O.set(this,d,b),a.indexOf("-")>-1&&void 0!==c&&O.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){O.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=N.get(a,b),c&&(!d||n.isArray(c)?d=N.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return N.get(a,c)||N.access(a,c,{empty:n.Callbacks("once memory").add(function(){N.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length",""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};$.optgroup=$.option,$.tbody=$.tfoot=$.colgroup=$.caption=$.thead,$.th=$.td;function _(a,b){var c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function aa(a,b){for(var c=0,d=a.length;d>c;c++)N.set(a[c],"globalEval",!b||N.get(b[c],"globalEval"))}var ba=/<|&#?\w+;/;function ca(a,b,c,d,e){for(var f,g,h,i,j,k,l=b.createDocumentFragment(),m=[],o=0,p=a.length;p>o;o++)if(f=a[o],f||0===f)if("object"===n.type(f))n.merge(m,f.nodeType?[f]:f);else if(ba.test(f)){g=g||l.appendChild(b.createElement("div")),h=(Y.exec(f)||["",""])[1].toLowerCase(),i=$[h]||$._default,g.innerHTML=i[1]+n.htmlPrefilter(f)+i[2],k=i[0];while(k--)g=g.lastChild;n.merge(m,g.childNodes),g=l.firstChild,g.textContent=""}else m.push(b.createTextNode(f));l.textContent="",o=0;while(f=m[o++])if(d&&n.inArray(f,d)>-1)e&&e.push(f);else if(j=n.contains(f.ownerDocument,f),g=_(l.appendChild(f),"script"),j&&aa(g),c){k=0;while(f=g[k++])Z.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),l.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",l.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var da=/^key/,ea=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,fa=/^([^.]*)(?:\.(.+)|)/;function ga(){return!0}function ha(){return!1}function ia(){try{return d.activeElement}catch(a){}}function ja(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ja(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=ha;else if(!e)return a;return 1===f&&(g=e,e=function(a){return n().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=n.guid++)),a.each(function(){n.event.add(this,b,e,d,c)})}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=N.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return"undefined"!=typeof n&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(G)||[""],j=b.length;while(j--)h=fa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=N.hasData(a)&&N.get(a);if(r&&(i=r.events)){b=(b||"").match(G)||[""],j=b.length;while(j--)if(h=fa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&N.remove(a,"handle events")}},dispatch:function(a){a=n.event.fix(a);var b,c,d,f,g,h=[],i=e.call(arguments),j=(N.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.rnamespace||a.rnamespace.test(g.namespace))&&(a.handleObj=g,a.data=g.data,d=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==d&&(a.result=d)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&("click"!==a.type||isNaN(a.button)||a.button<1))for(;i!==this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>-1:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,la=/\s*$/g;function pa(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function qa(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function ra(a){var b=na.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function sa(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(N.hasData(a)&&(f=N.access(a),g=N.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}O.hasData(a)&&(h=O.access(a),i=n.extend({},h),O.set(b,i))}}function ta(a,b){var c=b.nodeName.toLowerCase();"input"===c&&X.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}function ua(a,b,c,d){b=f.apply([],b);var e,g,h,i,j,k,m=0,o=a.length,p=o-1,q=b[0],r=n.isFunction(q);if(r||o>1&&"string"==typeof q&&!l.checkClone&&ma.test(q))return a.each(function(e){var f=a.eq(e);r&&(b[0]=q.call(this,e,f.html())),ua(f,b,c,d)});if(o&&(e=ca(b,a[0].ownerDocument,!1,a,d),g=e.firstChild,1===e.childNodes.length&&(e=g),g||d)){for(h=n.map(_(e,"script"),qa),i=h.length;o>m;m++)j=e,m!==p&&(j=n.clone(j,!0,!0),i&&n.merge(h,_(j,"script"))),c.call(a[m],j,m);if(i)for(k=h[h.length-1].ownerDocument,n.map(h,ra),m=0;i>m;m++)j=h[m],Z.test(j.type||"")&&!N.access(j,"globalEval")&&n.contains(k,j)&&(j.src?n._evalUrl&&n._evalUrl(j.src):n.globalEval(j.textContent.replace(oa,"")))}return a}function va(a,b,c){for(var d,e=b?n.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||n.cleanData(_(d)),d.parentNode&&(c&&n.contains(d.ownerDocument,d)&&aa(_(d,"script")),d.parentNode.removeChild(d));return a}n.extend({htmlPrefilter:function(a){return a.replace(ka,"<$1>")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(l.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=_(h),f=_(a),d=0,e=f.length;e>d;d++)ta(f[d],g[d]);if(b)if(c)for(f=f||_(a),g=g||_(h),d=0,e=f.length;e>d;d++)sa(f[d],g[d]);else sa(a,h);return g=_(h,"script"),g.length>0&&aa(g,!i&&_(a,"script")),h},cleanData:function(a){for(var b,c,d,e=n.event.special,f=0;void 0!==(c=a[f]);f++)if(L(c)){if(b=c[N.expando]){if(b.events)for(d in b.events)e[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);c[N.expando]=void 0}c[O.expando]&&(c[O.expando]=void 0)}}}),n.fn.extend({domManip:ua,detach:function(a){return va(this,a,!0)},remove:function(a){return va(this,a)},text:function(a){return K(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return ua(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=pa(this,a);b.appendChild(a)}})},prepend:function(){return ua(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=pa(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return ua(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return ua(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(_(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return K(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!la.test(a)&&!$[(Y.exec(a)||["",""])[1].toLowerCase()]){a=n.htmlPrefilter(a);try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(_(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return ua(this,arguments,function(b){var c=this.parentNode;n.inArray(this,a)<0&&(n.cleanData(_(this)),c&&c.replaceChild(b,this))},a)}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),f=e.length-1,h=0;f>=h;h++)c=h===f?this:this.clone(!0),n(e[h])[b](c),g.apply(d,c.get());return this.pushStack(d)}});var wa,xa={HTML:"block",BODY:"block"};function ya(a,b){var c=n(b.createElement(a)).appendTo(b.body),d=n.css(c[0],"display");return c.detach(),d}function za(a){var b=d,c=xa[a];return c||(c=ya(a,b),"none"!==c&&c||(wa=(wa||n(" - +
-
+

Evil Portal must be started first.

@@ -218,35 +253,72 @@
Evil Portal Change - Log
+ data-target="#collapseChangelog" class="text-muted">Evil Portal Info
-
    -
  • 2.1
  • +
    + Disclaimer +

    Evil Portal and all of its components are intended for professional use only. No one associated with this project is an anyway liable for your actions.

    +
    +
    + Help +
    Summary
    +

    Evil Portal is a captive portal program that enables you to easily create a captive portal for whatever your needs are. There are two kinds of portals Basic and Targeted. + Basic portals are just a simple page that gets served to all clients. Targeted portals allow you to serve a unique page to different clients based on a given rule.

    +
    Basic Portals
    +

    A Basic Portal is a one-size serves all kind of portal. These portals are designed to be a single page that all clients will land on. This is the traditional way + that captive portals work and the way Evil Portal as been doing things from the beginning.

    +
    Targeted Portals
    +

    A Targeted Portal allows you to serve a different page on a per-client basis based on a condition. This can be something like their mac address, user-agent, etc... + Targeted Portals give you a whole new dynamic to what Evil Portal can do, if you want clients who are connected to the SSID "Coffee Shop" to see a "Coffee Shop" branded portal + while at the same time clients with Android phones seeing an Android branded portal then this is what you want.

    +
    Useful Links
    + Hak5 Forum Thread +
    + GitHub Project +
    +
    + Change Log
      -
    • Removed un-needed verbosity
    • -
    • Made tab key indent in the editor instead of change elements
    • -
    • Added confirmation dialogue box when deleting a portal
    • -
    • Created auto-start feature
    • -
    • Various other quality of life updates
    • +
    • 3.0
    • +
        +
      • Added ability to route clients to different portals based upon some identifier [ssid, mac vendor, ip, etc...]
      • +
      • Updated the work bench so users can choose between targeted and non-targeted portals
      • +
      • Created easy-to-use interface for creating targeting rules
      • +
      • Created some consistency throughout the UI
      • +
      • Added ability to create portals on an SD card and move between SD and Internal storage easily
      • +
      • Made white listed and authorized clients IP addresses clickable like SSIDs in PineAP
      • +
      • Created helper functions getClientMac, getClientSSID, getClientHostName
      • +
      • Sending notification when a client goes through a portal.
      • +
      • Various quality of life improvements
      • +
    -
-
    -
  • 2.0
    • -
    • Captive Portal is now purely iptables (because F*** - NoDogSplash) -
    • +
    • 2.1
    • +
        +
      • Removed un-needed verbosity
      • +
      • Made tab key indent in the editor instead of change elements
      • +
      • Added confirmation dialogue box when deleting a portal
      • +
      • Created auto-start feature
      • +
      • Various other quality of life updates
      • +
    -
-
    -
  • 1.0
    • -
    • Initial Pineapple Nano Release
    • +
    • 2.0
    • +
        +
      • Captive Portal is now purely iptables (because F*** + NoDogSplash) +
      • +
    -
+
    +
  • 1.0
  • +
      +
    • Initial Pineapple Nano Release
    • +
    +
+
@@ -254,6 +326,7 @@ +