Merge branch 'master' into master

This commit is contained in:
Nick 2018-07-15 23:58:38 -04:00 committed by GitHub
commit 4d96f79d78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
91 changed files with 12328 additions and 1013 deletions

11
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,11 @@
Contributing
============
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:
`apt install php-codesniffer`
`phpcs --standard=PSR2 your_file.php`

View File

@ -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()
commander.run()

View File

@ -6,5 +6,5 @@
"tetra"
],
"title": "Commander",
"version": "2.0"
}
"version": "2.1"
}

View File

@ -6,5 +6,5 @@
"tetra"
],
"title": "Deauth",
"version": "1.4"
}
"version": "1.5"
}

View File

@ -21,7 +21,7 @@ TMPBLACKLIST=${MYPATH}lists/blacklist.tmp
if [ "$1" = "start" ]; then
killall -9 mkd3
killall -9 mdk3
rm ${TMPBLACKLIST}
rm ${TMPWHITELIST}
rm ${LOG}

File diff suppressed because it is too large Load Diff

188
EvilPortal/executable/executable Normal file → Executable file
View File

@ -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 <IP>|remove <IP>|purge|list}"
exit 0
esac

View File

@ -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);

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -0,0 +1,45 @@
<?php
/**
* getClientMac
* Gets the mac address of a client by the IP address
* Returns the mac address as a string
* @param $clientIP : The clients IP address
* @return string
*/
function getClientMac($clientIP)
{
return trim(exec("grep " . escapeshellarg($clientIP) . " /tmp/dhcp.leases | awk '{print $2}'"));
}
/**
* getClientSSID
* Gets the SSID a client is associated by the IP address
* Returns the SSID as a string
* @param $clientIP : The clients IP address
* @return string
*/
function getClientSSID($clientIP)
{
// Get the clients mac address. We need this to get the SSID
$mac = getClientMac($clientIP);
// get the path to the log file
$pineAPLogPath = trim(file_get_contents('/etc/pineapple/pineap_log_location'));
// get the ssid
return trim(exec("grep " . $mac . " " . $pineAPLogPath . "pineap.log | grep 'Association' | awk -F ',' '{print $4}'"));
}
/**
* getClientHostName
* Gets the host name of the connected client by the IP address
* Returns the host name as a string
* @param $clientIP : The clients IP address
* @return string
*/
function getClientHostName($clientIP)
{
return trim(exec("grep " . escapeshellarg($clientIP) . " /tmp/dhcp.leases | awk '{print $4}'"));
}

View File

@ -1,27 +1,33 @@
<?php
$destination = "http://". $_SERVER['HTTP_HOST'] . $_SERVER['HTTP_URI'] . "";
$destination = "http://" . $_SERVER['HTTP_HOST'] . $_SERVER['HTTP_URI'] . "";
require_once('helper.php');
?>
<HTML>
<HEAD>
<title>Evil Portal</title>
<script type="text/javascript">
function redirect() { setTimeout(function(){window.location = "/captiveportal/index.php";},100);}
</script>
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<meta name="viewport" content="width=device-width, initial-scale=1">
</HEAD>
<BODY>
<center>
<div style="text-align: center;">
<h1>Evil Portal</h1>
<p>This is the default Evil Portal page</p>
<p>This is the default Evil Portal page.</p>
<p>The SSID you are connected to is <?=getClientSSID($_SERVER['REMOTE_ADDR']);?></p>
<p>Your host name is <?=getClientHostName($_SERVER['REMOTE_ADDR']);?></p>
<p>Your MAC Address is <?=getClientMac($_SERVER['REMOTE_ADDR']);?></p>
<p>Your internal IP address is <?=$_SERVER['REMOTE_ADDR'];?></p>
<form method="POST" action="/captiveportal/index.php" onsubmit="redirect()">
<form method="POST" action="/captiveportal/index.php">
<input type="hidden" name="target" value="<?=$destination?>">
<button type="submit">Authorize</button>
</form>
</center>
</div>
</BODY>
</HTML>
</HTML>

View File

@ -0,0 +1,4 @@
{
"name": null,
"type": "basic"
}

View File

@ -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.

View File

@ -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.

View File

@ -0,0 +1,33 @@
<?php namespace evilportal;
class MyPortal extends Portal
{
public function handleAuthorization()
{
// handle form input or other extra things there
// Call parent to handle basic authorization first
parent::handleAuthorization();
}
/**
* 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::onSuccess();
}
/**
* If an error occurs then do something here.
* Override to provide your own functionality.
*/
public function showError()
{
// Calls default error message
parent::showError();
}
}

View File

@ -0,0 +1,35 @@
<?php
$destination = "http://" . $_SERVER['HTTP_HOST'] . $_SERVER['HTTP_URI'] . "";
require_once('helper.php');
?>
<HTML>
<HEAD>
<title>Evil Portal</title>
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<script type="text/javascript">
function redirect() { setTimeout(function(){window.location = "/captiveportal/index.php";},100);}
</script>
</HEAD>
<BODY>
<div style="text-align: center;">
<h1>Evil Portal</h1>
<p>This is the default Evil Portal page.</p>
<p>The SSID you are connected to is <?=getClientSSID($_SERVER['REMOTE_ADDR']);?></p>
<p>Your host name is <?=getClientHostName($_SERVER['REMOTE_ADDR']);?></p>
<p>Your MAC Address is <?=getClientMac($_SERVER['REMOTE_ADDR']);?></p>
<p>Your internal IP address is <?=$_SERVER['REMOTE_ADDR'];?></p>
<form method="POST" action="/captiveportal/index.php" onsubmit="redirect()">
<input type="hidden" name="target" value="<?=$destination?>">
<button type="submit">Authorize</button>
</form>
</div>
</BODY>
</HTML>

View File

@ -0,0 +1,45 @@
<?php
/**
* getClientMac
* Gets the mac address of a client by the IP address
* Returns the mac address as a string
* @param $clientIP : The clients IP address
* @return string
*/
function getClientMac($clientIP)
{
return trim(exec("grep " . escapeshellarg($clientIP) . " /tmp/dhcp.leases | awk '{print $2}'"));
}
/**
* getClientSSID
* Gets the SSID a client is associated by the IP address
* Returns the SSID as a string
* @param $clientIP : The clients IP address
* @return string
*/
function getClientSSID($clientIP)
{
// Get the clients mac address. We need this to get the SSID
$mac = getClientMac($clientIP);
// get the path to the log file
$pineAPLogPath = trim(file_get_contents('/etc/pineapple/pineap_log_location'));
// get the ssid
return trim(exec("grep " . $mac . " " . $pineAPLogPath . "pineap.log | grep 'Association' | awk -F ',' '{print $4}'"));
}
/**
* getClientHostName
* Gets the host name of the connected client by the IP address
* Returns the host name as a string
* @param $clientIP : The clients IP address
* @return string
*/
function getClientHostName($clientIP)
{
return trim(exec("grep " . escapeshellarg($clientIP) . " /tmp/dhcp.leases | awk '{print $4}'"));
}

View File

@ -0,0 +1,81 @@
<?php
require_once('helper.php');
/**
*
* DO NOT MODIFY THIS FILE
* I highly recommend against modifying this file unless you know what you are doing!
*
* This file handles determining the destination file a client should see based on the conditions set in the json.
*
*/
// The value for this variable needs to be set when an new instance of a portal is created
// EvilPortal does this automatically when a targeted portal is created by running:
// sed -i 's/"portal_name_here"/"{portalName}"/g' index.php
$PORTAL_NAME = "portal_name_here";
// Get the information about the client and map
// it to the rule key specified in the {$PORTAL_NAME}.ep file
$MAPPED_RULES = [
"mac" => 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;
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,34 @@
{
"name": null,
"type": "targeted",
"targeted_rules": {
"default": "default.php",
"rule_order": ["mac", "ssid", "hostname", "useragent"],
"rules": {
"mac": {
"exact": {
},
"regex": {
}
},
"ssid": {
"exact": {
},
"regex": {
}
},
"hostname": {
"exact": {
},
"regex": {
}
},
"useragent": {
"exact": {
},
"regex": {
}
}
}
}
}

View File

@ -1,322 +1,671 @@
registerController("EvilPortalController", ['$api', '$scope', function ($api, $scope) {
getControls();
getPortals();
$scope.portals = [];
$scope.portalToDelete = null;
$scope.portalDeleteValidation = '';
$scope.messages = [];
$scope.newPortalName = '';
$scope.throbber = true;
$scope.running = false;
$scope.library = true;
$scope.whiteList = '';
$scope.whiteListInput = '';
$scope.accessList = '';
$scope.accessListInput = '';
$scope.workshopPortal = {name: "", files: [], storage: "internal"};
$scope.editPortalFile = {portalName: "", storage: "", file: "", code: ""};
$scope.handleControl = function (control) {
control.throbber = true;
switch (control.title) {
case "CaptivePortal":
$api.request({
module: "EvilPortal",
action: "startStop"
}, function (response) {
getControls();
control.throbber = false;
if (!response.control_success) {
$scope.sendMessage(control.title, response.control_message);
}
$scope.refreshLivePreview()
});
break;
case "Auto Start":
$api.request({
module: "EvilPortal",
action: "enableDisable"
}, function (response) {
getControls();
control.throbber = false;
if (!response.control_success) {
$scope.sendMessage(control.title, response.control_message);
}
});
break;
}
// status information about the module
$scope.evilPortal = {
"throbber": false,
"sdAvailable": false,
"running": false,
"startOnBoot": false,
"library": true
};
// controls that belong in the Controls pane
$scope.controls = [
{ "title": "Captive Portal", "visible": true, "throbber": false, "status": "Start"},
{"title": "Start On Boot", "visible": true, "throbber": false, "status": "Enable"}
];
// messages to be displayed in the Messages pane
$scope.messages = [];
$scope.whiteList = {"clients": "", "toManipulate": null};
$scope.accessList = {"clients": "", "toManipulate": null};
// all of the portals that could be found
$scope.portals = [];
// a model of a new portal to create
$scope.newPortal = {"type": "basic", "name": ""};
// deleting portal stuff
$scope.portalToDelete = null;
$scope.portalDeleteValidation = null;
// the portal workshop
$scope.workshop = {"portal": {}, "dirContents": null, "inRoot": true, "rootDirectory": null, "editFile": {"path": null, "isNewFile": true},
"onEnable": null, "onDisable": null, "concreteTargetedRules": null, "workingTargetedRules": null, "deleteFile": null
};
/**
* Reset the workshop object to a blank slate with initial values.
*/
$scope.resetWorkshop = function () {
$scope.workshop = {"portal": {}, "dirContents": null, "inRoot": true, "rootDirectory": null, "editFile": {"path": null, "isNewFile": true},
"onEnable": null, "onDisable": null, "concreteTargetedRules": null, "workingTargetedRules": null, "deleteFile": null
};
};
/**
* Push a message to the Evil Portal Messages Pane
* @param t: The Title of the message
* @param m: The message body
*/
$scope.sendMessage = function (t, m) {
// Add a new message to the top of the list
$scope.messages.unshift({title: t, msg: m});
// if there are 4 items in the list remove the 4th item
if ($scope.messages.length == 4) {
if ($scope.messages.length === 4) {
$scope.dismissMessage(3);
}
};
/**
* Remove a message from the Evil Portal Messages pane
* @param $index: The index of the message in the list to remove
*/
$scope.dismissMessage = function ($index) {
//var index = $scope.messages.indexOf(message);
$scope.messages.splice($index, 1);
};
function getControls() {
$scope.throbber = true;
$api.request({
module: "EvilPortal",
action: "getControlValues"
}, function (response) {
updateControls(response);
});
}
/**
* Preform an action for a given control
* This can be starting the captive portal or toggle on boot.
* @param control: The control to handle
*/
$scope.handleControl = function(control) {
control.throbber = true;
var actionToPreform = null;
switch(control.title) {
case "Captive Portal":
actionToPreform = "toggleCaptivePortal";
break;
function updateControls(response) {
var running;
var autostart;
if (response.running == false) {
running = "Start";
$scope.running = false;
} else {
running = "Stop";
$scope.running = true;
case "Start On Boot":
actionToPreform = "toggleOnBoot";
break;
}
if (response.autostart == false) {
autostart = "Enable";
} else {
autostart = "Disable";
}
$scope.controls = [
{
title: "CaptivePortal",
status: running,
visible: true,
throbber: false
},
{
title: "Auto Start",
status: autostart,
visible: true,
throbber: false
}];
$scope.throbber = false;
}
$scope.createNewPortal = function () {
if (actionToPreform !== null) {
$api.request({
module: "EvilPortal",
action: actionToPreform
}, function(response) {
if (!response.success) {
$scope.sendMessage(control.title, response.message);
}
getStatus();
});
}
};
/**
* Validates the information in the newPortal model and then makes an API request to create a new portal.
* @param storage: The storage medium to create the portal on (internal or sd)
*/
$scope.createNewPortal = function(storage) {
$api.request({
module: "EvilPortal",
action: "createNewPortal",
portalName: $scope.newPortalName
}, function (response) {
if (response.create_success) {
getPortals();
$scope.newPortalName = '';
} else {
$scope.sendMessage("Error Creating Portal", response.create_message);
name: $scope.newPortal.name,
type: $scope.newPortal.type,
storage: storage
}, function(response) {
if (!response.success) {
$scope.sendMessage('Error Creating Portal', response.message);
return;
}
});
};
$scope.deletePortalRequest = function(portal) {
$scope.portalToDelete = portal;
console.log(portal);
};
$scope.deletePortal = function (portal) {
console.log(portal.storage);
console.log(portal.title);
$scope.portalToDelete = null;
$scope.portalDeleteValidation = null
$api.request({
module: "EvilPortal",
action: "deletePortal",
storage: portal.storage,
name: portal.title
}, function (response) {
$scope.sendMessage("Delete Portal", response.message);
$scope.newPortal = {"type": "basic", "name": ""};
getPortals();
});
};
$scope.activatePortal = function (portal) {
/**
* Move a given portal between storage mediums if an SD card is present.
* @param portal: The portal to move
*/
$scope.movePortal = function(portal) {
if (!$scope.evilPortal.sdAvailable) {
$scope.sendMessage("No SD Card.", "An SD card must be present to preform this action.");
return;
}
$api.request({
module: "EvilPortal",
action: "movePortal",
name: portal.title,
storage: portal.storage
}, function(response) {
if (response.success) {
getPortals();
$scope.sendMessage("Moved Portal", response.message);
} else {
$scope.sendMessage("Error Moving " + portal.title, response.message);
}
});
};
/**
* Delete a portal from the wifi pineapple
* @param verified: Has the delete request been verified? If so then make the API request otherwise setup
* @param portal: The portal to delete
*/
$scope.deletePortal = function(verified, portal) {
if (!verified) { // if the request has not been verified then setup the shits
$scope.portalToDelete = portal;
return;
}
if ($scope.portalToDelete === null || $scope.portalToDelete.fullPath === null) {
$scope.sendMessage("Unable To Delete Portal", "No portal was set for deletion.");
return;
}
deleteFileOrDirectory($scope.portalToDelete.fullPath, function (response) {
if (!response.success) {
$scope.sendMessage("Error Deleting Portal", response.message); // push an error if deletion failed
} else {
$scope.sendMessage("Deleted Portal", "Successfully deleted " + $scope.portalToDelete.title + ".");
$scope.portalToDelete = null;
$scope.portalDeleteValidation = null;
getPortals(); // refresh the library
}
});
};
/**
* Activate a portal
* @param portal: The portal to activate
*/
$scope.activatePortal = function(portal) {
$api.request({
module: "EvilPortal",
action: "activatePortal",
storage: portal.storage,
name: portal.title
}, function (response) {
//$scope.sendMessage("Activate Portal", response.message);
getPortals();
name: portal.title,
storage: portal.storage
}, function(response) {
console.log(response);
if (response.success) {
getPortals();
$scope.sendMessage("Activated Portal", portal.title + " has been activated successfully.");
} else {
$scope.sendMessage("Error Activating " + portal.title, response.message);
}
});
};
$scope.deactivatePortal = function (portal) {
/**
* Deactivate a given portal if its active
* @param portal: The portal to deactivate
*/
$scope.deactivatePortal = function(portal) {
$api.request({
module: "EvilPortal",
action: "deactivatePortal",
storage: portal.storage,
name: portal.title
}, function (response) {
//$scope.sendMessage("Deactivate Portal", response.message);
getPortals();
});
};
$scope.editPortal = function (portal, file) {
$api.request({
module: "EvilPortal",
action: "getPortalCode",
storage: portal.storage,
name: portal.name,
portalFile: file
}, function (response) {
//$scope.sendMessage("Edit Portal", response.message);
$scope.editPortalFile.code = response.code;
$scope.editPortalFile.file = file;
$scope.editPortalFile.portalName = portal.name;
$scope.editPortalFile.storage = portal.storage;
});
};
$scope.savePortalCode = function (editFile) {
$api.request({
module: "EvilPortal",
action: "submitPortalCode",
storage: editFile.storage,
portalCode: editFile.code,
name: editFile.portalName,
fileName: editFile.file
}, function (response) {
$scope.sendMessage("Edit File", response.message);
});
};
$scope.getPortalFiles = function (portal) {
$api.request({
module: "EvilPortal",
action: "portalFiles",
storage: portal.storage,
name: portal.title
}, function (response) {
$scope.workshopPortal.name = portal.title;
$scope.workshopPortal.storage = portal.storage;
$scope.workshopPortal.files = response.portalFiles;
$scope.library = false;
});
};
function getPortals() {
$api.request({
module: "EvilPortal",
action: "portalList"
}, function (response) {
$scope.portals = [];
for (var i = 0; i < response.length; i++) {
$scope.portals.unshift({
title: response[i].title,
storage: response[i].location,
active: response[i].active
});
//console.log({title: response[i].title, storage: response[i].location, active: response[i].active});
name: portal.title,
storage: portal.storage
}, function(response) {
console.log(response);
if (response.success) {
getPortals();
$scope.sendMessage("Deactivated Portal", portal.title + " has been deactivated successfully.");
} else {
$scope.sendMessage("Error Deactivating " + portal.title, response.message);
}
});
}
$scope.refreshLivePreview = function () {
window.frames['livePreviewIframe'].src = "http://172.16.42.1";
};
$scope.getList = function (listToGet) {
/**
* Load portal contents and open it up in the work bench
* @param portal: The portal to get the contents of
*/
$scope.loadPortal = function (portal) {
getFileOrDirectoryContent(portal.fullPath, function(response) {
if (!response.success) {
$scope.sendMessage("Error Getting Contents", response.message);
return;
}
$scope.workshop.inRoot = true;
$scope.workshop.portal = portal;
$scope.workshop.dirContents = response.content;
$scope.workshop.rootDirectory = portal.fullPath;
$scope.evilPortal.library = false;
console.log(response.content);
});
};
/**
* Load toggle commands for the current portal in the work bench.
* These are the commands that are executed when a portal is enabled/disabled.
*/
$scope.loadToggleCommands = function() {
[".enable", ".disable"].forEach(getScript);
function getScript(scriptName) {
getFileOrDirectoryContent($scope.workshop.rootDirectory + "/" + scriptName, function (response) {
if (!response.success) {
$scope.sendMessage("Error Getting Contents", response.message);
return;
}
if (scriptName === ".enable")
$scope.workshop.onEnable = response.content.fileContent;
else
$scope.workshop.onDisable = response.content.fileContent;
});
}
};
/**
* Save toggle commands.
* @param cmdFile: The commands to save (enable, disable)
*/
$scope.saveToggleCommands = function(cmdFile) {
function sendData(f, content) {
writeToFile($scope.workshop.rootDirectory + "/" + f, content, false, function (response) {
if (!response.success)
$scope.sendMessage("Error write to file " + f, response.message);
});
}
switch(cmdFile) {
case "disable":
sendData(".disable", $scope.workshop.onDisable);
break;
case "enable":
sendData(".enable", $scope.workshop.onEnable);
break;
default:
sendData(".enable", $scope.workshop.onEnable);
sendData(".disable", $scope.workshop.onDisable);
break;
}
};
/**
* Load the rules for targeted portals
*/
$scope.loadTargetedRules = function() {
$api.request({
module: "EvilPortal",
action: "getList",
listName: listToGet
}, function (response) {
if (response.list_success) {
if (listToGet == "whiteList") {
$scope.whiteList = response.list_contents;
} else if (listToGet == "accessList") {
$scope.accessList = response.list_contents;
action: "getRules",
name: $scope.workshop.portal.title,
storage: $scope.workshop.portal.storage
}, function(response) {
if (response.success) {
$scope.workshop.concreteTargetedRules = response.data;
$scope.workshop.workingTargetedRules = {"rules": {}};
// welcome to the realm of loops. I will be your guide
// We have to turn each rule into a keyed set of rules with a rule index represented by var index
// this is because we need a constant key for each rule when editing on the web interface
// the index must be removed later before saving the results to the routes.json file
// if you have a better way to do this you are my hero. Email me n3rdcav3@gmail.com or fork the repo :)
// This first loop loops over each rule categories such as "mac", "ssid" and so on
for (var key in response.data['rules']) {
// we then create the a object with that key name in our workingData object
$scope.workshop.workingTargetedRules['rules'][key] = {};
// Now its time to loop over each category specifier such as "exact" and "regex"
for (var specifier in response.data['rules'][key]) {
var index = 0;
// We then create that specifier in our workingData
$scope.workshop.workingTargetedRules['rules'][key][specifier] = {};
// finally we loop over the specific rules defined in the specifier
for (var r in response.data['rules'][key][specifier]) {
var obj = {};
obj['key'] = r;
obj['destination'] = response.data['rules'][key][specifier][r];
$scope.workshop.workingTargetedRules['rules'][key][specifier][index] = obj;
}
// increment index
index++;
}
}
} else {
$scope.sendMessage("List Data Error", response.list_message);
console.log(response);
$scope.sendMessage("Error", "There was an issue getting the portal rules.");
}
});
};
$scope.addWhiteListClient = function () {
/**
* Remove a targeted rule
* @param rule
* @param specifier
* @param index
*/
$scope.removeTargetedRule = function(rule, specifier, index) {
delete $scope.workshop.workingTargetedRules['rules'][rule][specifier][index];
};
/**
* Create a new targeted rule
* @param rule
* @param specifier
*/
$scope.newTargetedRule = function(rule, specifier) {
// make sure the specifier is set
if ($scope.workshop.workingTargetedRules['rules'][rule][specifier] == undefined) {
$scope.workshop.workingTargetedRules['rules'][rule][specifier] = {};
}
var highest = 0;
// get the highest index
for (var i in $scope.workshop.workingTargetedRules['rules'][rule][specifier]) {
if (parseInt(i) >= highest) {
highest = i + 1;
}
}
$scope.workshop.workingTargetedRules['rules'][rule][specifier][highest] = {"": ""};
};
/**
* Build the targeted rules and sned them to the API for saving
*/
$scope.saveTargetedRules = function() {
// build the rules
for (var key in $scope.workshop.concreteTargetedRules.rules) {
for (var specifier in $scope.workshop.concreteTargetedRules.rules[key]) {
var obj = {};
for (var i in $scope.workshop.workingTargetedRules['rules'][key][specifier]) {
obj[$scope.workshop.workingTargetedRules['rules'][key][specifier][i]['key']] = $scope.workshop.workingTargetedRules['rules'][key][specifier][i]['destination'];
}
$scope.workshop.concreteTargetedRules['rules'][key][specifier] = obj;
}
}
console.log(JSON.stringify($scope.workshop.concreteTargetedRules));
$api.request({
module: "EvilPortal",
action: "addToList",
listName: "whiteList",
clientIP: $scope.whiteListInput
}, function (response) {
if (response.add_success) {
$scope.whiteListInput = '';
$scope.getList("whiteList");
} else {
$scope.sendMessage("White List", response.add_message);
console.log(response);
action: "saveRules",
name: $scope.workshop.portal.title,
storage: $scope.workshop.portal.storage,
rules: JSON.stringify($scope.workshop.concreteTargetedRules)
}, function(response) {
if (!response.success) {
$scope.sendMessage("Error", response.message);
}
});
};
$scope.removeWhiteListClient = function () {
/**
* check if a given object is empty.
* @param obj: The object to check
* @returns {boolean}: true if empty false if not empty
*/
$scope.isObjectEmpty = function(obj) {
return (Object.keys(obj).length === 0);
};
/**
* Load the contents of a given file.
* @param filePath: The path to the file to load
*/
$scope.loadFileContent = function(filePath) {
getFileOrDirectoryContent(filePath, function(response) {
if (!response.success) {
$scope.sendMessage("Error Getting Contents", response.message);
return;
}
$scope.workshop.editFile = {
"name": response.content.name,
"path": response.content.path,
"size": response.content.size,
"content": response.content.fileContent
};
});
};
/**
* Setup the workshop to create a new empty file.
*/
$scope.setupNewFile = function() {
var basePath = ($scope.workshop.portal.storage === "sd") ? "/sd/portals/" : "/root/portals/";
$scope.workshop.editFile.path = basePath + $scope.workshop.portal.title + "/";
$scope.workshop.editFile.isNewFile = true;
};
/**
* Write file content to the file system.
* @param editFile: A portal.editFile object
*/
$scope.saveFileContent = function(editFile) {
// new files wont have the filename in the path so make sure to set it here if needed.
if (!editFile.path.includes(editFile.name))
editFile.path = editFile.path + editFile.name;
console.log(editFile.path);
writeToFile(editFile.path, editFile.content, false, function(response) {
if (!response.success)
$scope.sendMessage("Error write to file " + editFile.name, response.message);
$scope.loadPortal($scope.workshop.portal); // refresh the portal
});
};
/**
* Delete a requested file.
*/
$scope.deleteFile = function() {
deleteFileOrDirectory($scope.workshop.deleteFile.path, function(response){
if (!response.success)
$scope.sendMessage("Error deleting file " + $scope.workshop.deleteFile.name, response.message);
$scope.loadPortal($scope.workshop.portal); // refresh the portal
});
};
/**
* Load either the white list or the authorized clients (access) list
* @param listName: The name of the list: whiteList or accessList (authorized clients)
*/
$scope.getList = function (listName) {
var whiteList = '/pineapple/modules/EvilPortal/data/allowed.txt';
var authorized = '/tmp/EVILPORTAL_CLIENTS.txt';
getFileOrDirectoryContent((listName === "whiteList") ? whiteList : authorized, function (response) {
switch (listName) {
case 'whiteList':
$scope.whiteList.clients = response.content.fileContent;
break;
case 'accessList':
$scope.accessList.clients = response.content.fileContent;
break;
}
})
};
/**
* Remove a client from either the white list (whiteList) the authorized clients list (accessList)
* @param listName: whiteList or accessList
*/
$scope.removeClientFromList = function(listName) {
var clientToRemove = (listName === 'whiteList') ? $scope.whiteList.toManipulate : $scope.accessList.toManipulate;
console.log(clientToRemove);
$api.request({
module: "EvilPortal",
action: "removeFromList",
listName: "whiteList",
clientIP: $scope.whiteListInput
}, function (response) {
if (response.remove_success) {
$scope.whiteListInput = '';
$scope.getList("whiteList");
} else {
$scope.sendMessage("White List", response.remove_message);
console.log(response);
action: "removeClientFromList",
clientIP: clientToRemove,
listName: listName
}, function(response) {
if (!response.success) {
$scope.sendMessage("Error", response.message);
return;
}
$scope.getList(listName);
switch (listName) {
case 'whiteList':
$scope.whiteList.toManipulate = null;
break;
case 'accessList':
$scope.accessList.toManipulate = null;
break;
}
});
};
/**
* Add a new client to the white list
*/
$scope.addWhiteListClient = function() {
writeToFile('/pineapple/modules/EvilPortal/data/allowed.txt', $scope.whiteList.toManipulate + "\n", true, function(response) {
$scope.getList('whiteList');
});
$scope.whiteList.toManipulate = null;
};
/**
* Authorize a new client
*/
$scope.authorizeClient = function () {
$api.request({
module: "EvilPortal",
action: "addToList",
listName: "accessList",
clientIP: $scope.accessListInput
action: "authorizeClient",
clientIP: $scope.accessList.toManipulate
}, function (response) {
if (response.add_success) {
$scope.accessListInput = '';
$scope.getList("accessList");
} else {
$scope.sendMessage("Access List", response.add_message);
console.log(response);
}
$scope.getList('accessList');
$scope.accessList.toManipulate = null;
});
};
$scope.revokeClient = function () {
/**
* Get a line clicked in a text area and set that line as the text for a text input
* @param textareaId: The id of the text area to grab from
* @param inputname: The name of the input field to write to
*/
$scope.getClickedClient = function(textareaId, inputname) {
var textarea = $('#' + textareaId);
var lineNumber = textarea.val().substr(0, textarea[0].selectionStart).split('\n').length;
var ssid = textarea.val().split('\n')[lineNumber-1].trim();
$("input[name='" + inputname + "']").val(ssid).trigger('input');
};
/**
* Write given content to a given file on the file system.
* @param filePath: The path to the file to write content to
* @param fileContent: The content to write to the file
* @param appendFile: Should the content be append to the file (true) or overwrite the file (false)
* @param callback: A callback function to handle the API response
*/
function writeToFile(filePath, fileContent, appendFile, callback) {
$api.request({
module: "EvilPortal",
action: "removeFromList",
listName: "accessList",
clientIP: $scope.accessListInput
}, function (response) {
if (response.remove_success) {
$scope.accessListInput = '';
$scope.getList("accessList");
} else {
$scope.sendMessage("Access List", response.remove_message);
console.log(response);
}
action: "writeFileContent",
filePath: filePath,
content: fileContent,
append: appendFile
}, function(response) {
callback(response);
});
}
/**
* Get the contents of a directory
* @param pathToObject: The full path to the file or directory to get the contents of
* @param callback: A function that handles the response from the API.
*/
function getFileOrDirectoryContent(pathToObject, callback) {
$api.request({
module: "EvilPortal",
action: "getFileContent",
filePath: pathToObject
}, function(response) {
callback(response);
});
}
/**
* Delete a file or directory from the pineapples filesystem.
* This is intended to be used for only deleting portals and portal related files but anything can be delete.
* @param fileOrDirectory: The path to the file to delete
* @param callback: The callback function to handle the API response
*/
function deleteFileOrDirectory(fileOrDirectory, callback) {
$api.request({
module: "EvilPortal",
action: "deleteFile",
filePath: fileOrDirectory
}, function(response) {
callback(response);
});
}
/**
* Update the control models so they reflect the proper information
*/
function updateControls() {
$scope.controls = [
{
"title": "Captive Portal",
"status": ($scope.evilPortal.running) ? "Stop" : "Start",
"visible": true,
"throbber": false
},
{
"title": "Start On Boot",
"status": ($scope.evilPortal.startOnBoot) ? "Disable": "Enable",
"visible": true,
"throbber": false
}
];
}
/**
* Get the status's for the controls in the Controls pane and other various information
*/
function getStatus() {
$scope.evilPortal.throbber = true;
$api.request({
module: "EvilPortal",
action: "status"
}, function (response) {
for (var key in response) {
if (response.hasOwnProperty(key) && $scope.evilPortal.hasOwnProperty(key)) {
$scope.evilPortal[key] = response[key];
}
}
$scope.evilPortal.throbber = false;
updateControls();
});
}
/**
* Get all of the portals on the Pineapple
*/
function getPortals() {
$scope.evilPortal.throbber = true;
$api.request({
module: "EvilPortal",
action: "listAvailablePortals"
}, function(response) {
if (!response.success) {
$scope.sendMessage("Error Listing Portals", "An error occurred while trying to get list of portals.");
return;
}
$scope.portals = [];
response.portals.forEach(function(item, index) {
$scope.portals.unshift({
title: item.title,
storage: item.storage,
active: item.active,
type: item.portalType,
fullPath: item.location
});
});
});
}
// The status for the Evil Portal module as well as current portals should be retrieved when the controller loads.
getStatus();
getPortals();
}]);

View File

@ -1,6 +1,7 @@
<div class="row" ng-controller="EvilPortalController">
<div class="col-md-3">
<!-- Controls Panel -->
<div class="panel panel-default">
<div class="panel-heading">
<a href="javascript:;" data-toggle="collapse" data-target="#collapseControls" class="text-muted"><h4
@ -24,6 +25,8 @@
</div>
</div>
</div>
<!-- Messages Panel -->
<div class="panel panel-default">
<div class="panel-heading">
<a href="javascript:;" data-toggle="collapse" data-target="#collapseMessages" class="text-muted"><h4
@ -37,10 +40,11 @@
<table style="width:100%" ng-hide="(messages.length == 0)">
<tr ng-repeat="message in messages">
<td>
<hr/>
<h5><b>{{ message.title }}</b> <a ng-click="dismissMessage($index)" href="javascript:;"
class="pull-right">Dismiss</a></h5>
<fieldset>
<legend><h5><b>{{ message.title }}</b> <a ng-click="dismissMessage($index)" href="javascript:;"
class="pull-right">Dismiss</a></h5></legend>
<p class="text-muted"><i>{{ message.msg }}</i></p>
</fieldset>
</td>
</tr>
</table>
@ -49,49 +53,65 @@
</div>
</div>
<!--<div ng-show="dependencies">-->
<div class="panel-group" id="accordion">
<div class="col-md-9">
<!-- Library panel -->
<div class="panel panel-default">
<div class="panel-heading">
<h5 class="panel-title"><a href="javascript:;" data-toggle="collapse" data-parent="#accordion"
data-target="#collapseLibrary" class="text-muted">Work Bench</a></h5>
data-target="#collapseLibrary" class="text-muted">Work Bench <b style="text-transform: capitalize"><i>{{ workshop.portal.title }}</i></b></a></h5>
</div>
<div id="collapseLibrary" class="panel-collapse collapse in">
<div class="panel-body">
<div class="input-group" ng-show="library">
<input type="text" class="form-control" placeholder="PortalName" name="portalName"
ng-model="newPortalName">
<span class="input-group-btn">
<button ng-disabled="newPortalName == ''" class="btn btn-default" type="button" ng-click="createNewPortal()">Create New Portal</button>
</span>
<div class="input-group" ng-show="evilPortal.library">
<span class="input-group-btn">
<select class="form-control ng-pristine ng-valid ng-touched" style="width:80px" ng-model="newPortal.type">
<option value="basic">Basic</option>
<option value="targeted">Targeted</option>
</select>
</span>
<input type="text" class="form-control" placeholder="Portal Name" name="portalName" ng-model="newPortal.name">
<span class="input-group-btn">
<button ng-disabled="newPortal.name == ''" class="btn btn-default" type="button" ng-click="createNewPortal('internal')">Create New Portal</button>
<button ng-disabled="newPortal.name == ''" class="btn btn-default" type="button" ng-click="createNewPortal('sd')" ng-show="evilPortal.sdAvailable">Create On SD Card</button>
</span>
</div>
<hr ng-show="library"/>
<div ng-show="portals.length > 0 && library == true">
<hr ng-show="evilPortal.library"/>
<div ng-show="portals.length > 0 && evilPortal.library">
<div class="table-responsive">
<table class="table table-striped" align="center">
<thead>
<th>Portal Name</th>
<th>Portal Type</th>
<th>Location</th>
<th ng-show="evilPortal.sdAvailable">Move</th>
<th>Activate</th>
<th>Delete</th>
</thead>
<tbody>
<tr ng-repeat="portal in portals">
<td><a href="javascript:;" ng-click="getPortalFiles(portal)"><b>{{ portal.title
}}</b></a></td>
<td class="text-muted"><i>{{ portal.storage }}</i></td>
<td ng-hide="portal.active"><a href="javascript:;"
ng-click="activatePortal(portal)">Activate</a>
<!-- Open portal work shop -->
<td><a href="javascript:;" ng-click="loadPortal(portal)" style="text-transform: capitalize"><b>{{ portal.title }}</b></a></td>
<!-- Portal type -->
<td class="text-muted" style="text-transform: capitalize"><i>{{ portal.type }}</i></td>
<!-- What storage device the portal is on -->
<td class="text-muted" style="text-transform: capitalize"><i>{{ portal.storage }}</i></td>
<!-- change storage -->
<td ng-show="evilPortal.sdAvailable">
<a ng-show="(portal.storage == 'internal') && !portal.active" href="javascript:;" ng-click="movePortal(portal, 'sd');">Move to SD</a>
<a ng-show="(portal.storage == 'sd') && !portal.active" href="javascript:;" ng-click="movePortal(portal, 'internal');">Move to Internal</a>
</td>
<td ng-show="portal.active"><a href="javascript:;"
ng-click="deactivatePortal(portal)">Deactivate</a>
<!-- activate/deactivate -->
<td>
<a ng-hide="portal.active" href="javascript:;" ng-click="activatePortal(portal)">Activate</a>
<a ng-show="portal.active" href="javascript:;" ng-click="deactivatePortal(portal)">Deactivate</a>
</td>
<td ng-hide="portal.active"><a href="javascript:;" data-toggle="modal"
data-target="#deleteModal"
ng-click="deletePortalRequest(portal)"
ng-hide="portal.storage == 'active'">Delete</a>
<!-- delete portal button -->
<td>
<a ng-hide="portal.active" data-toggle="modal" data-target="#deleteModal" ng-click="deletePortal(false, portal)">Delete</a>
</td>
</tr>
</tbody>
@ -99,28 +119,43 @@
</div>
</div>
<div ng-hide="portals.length > 0 || library == false">
<div ng-hide="portals.length > 0 || !evilPortal.library">
<p class="text-muted text-center"><i>No Portals in Library to Display.</i></p>
</div>
<div ng-show="library == false">
<button type="submit" class="btn btn-default btn-sm" ng-click="library = true">Back To
Library
</button>
<hr />
<h3>Contents of {{ workshopPortal.name }}</h3>
<!-- Portal Editor -->
<div ng-hide="evilPortal.library">
<!-- Editor Buttons -->
<div class="row-fluid">
<span class="pull-left">
<button type="submit" class="btn btn-default btn-sm" ng-click="evilPortal.library = true; resetWorkshop()">Back To Library</button>
</span>
<span class="pull-right">
<button type="submit" class="btn btn-default btn-sm" data-toggle="modal" data-target="#fileModal" ng-click="setupNewFile()">New File</button>
<!-- Holding off on toggle commands until a future release. Still need to think through how they should work. -->
<!--<button type="submit" class="btn btn-default btn-sm" data-toggle="modal" data-target="#commandEditorModal" ng-click="loadToggleCommands()">Toggle Commands</button>-->
<button type="submit" class="btn btn-default btn-sm" data-toggle="modal" data-target="#ruleEditorModal" ng-show="workshop.portal.type == 'targeted'" ng-click="loadTargetedRules();">Target Rule Editor</button>
<button type="submit" class="btn btn-default btn-sm" ng-click="loadPortal(workshop.portal)">Refresh</button>
</span>
</div>
<br/>
<hr />
<!-- Portal Files -->
<div class="table-responsive">
<table class="table table-striped" align="center">
<thead>
<th>File Name</th>
<th>Edit</th>
<th>Delete</th>
</thead>
<tbody>
<tr ng-repeat="file in workshopPortal.files">
<td>{{ file }}</td>
<td><a href="javascript:;" ng-click="editPortal(workshopPortal, file)"
data-toggle="modal" data-target="#fileModal">Edit</a></td>
<tr ng-repeat="file in workshop.dirContents">
<td>{{ file.name }}</td>
<td><a href="javascript:;" ng-click="loadFileContent(file.path)" data-toggle="modal" data-target="#fileModal">Edit</a></td>
<td><a href="javascript:;" ng-click="workshop.deleteFile = file" data-toggle="modal" data-target="#deleteFileModal" ng-show="file.deletable">Delete</a></td>
</tr>
</tbody>
</table>
@ -144,18 +179,18 @@
<i>This is a list of clients who are allowed to connect to the internet without ever viewing the captive portal.</i>
</p>
<p>
<textarea class="form-control" rows="15" ng-model="whiteList" readonly></textarea>
<textarea id="whiteListPool" class="form-control" rows="15" ng-mouseup="getClickedClient('whiteListPool', 'whiteListInput');" ng-model="whiteList.clients" readonly></textarea>
</p>
<div class="input-group">
<input type="text" class="form-control" placeholder="IP Address" name="ipaddress"
ng-model="whiteListInput">
<input type="text" class="form-control" placeholder="IP Address" name="whiteListInput"
ng-model="whiteList.toManipulate">
<span class="input-group-btn">
<button class="btn btn-default" type="button"
ng-click="addWhiteListClient()">Add</button>
<button class="btn btn-default" type="button"
ng-click="removeWhiteListClient()">Remove</button>
<button class="btn btn-default" type="button" ng-click="addWhiteListClient()">Add</button>
<button class="btn btn-default" type="button" ng-click="removeClientFromList('whiteList')">Remove</button>
</span>
</div>
<br/>
<button class="btn btn-default btn-sm" type="button" ng-click="getList('whiteList')">Refresh</button>
</div>
</div>
</div>
@ -169,22 +204,24 @@
</div>
<div id="collapseAuthorizedClients" class="panel-collapse collapse">
<div class="panel-body">
<p class="text-muted" ng-show="running">
<i>This is a list of clients who have been authorized through the captive portal.</i>
</p>
<p ng-show="running">
<textarea class="form-control" rows="15" ng-model="accessList" readonly></textarea>
</p>
<div class="input-group" ng-show="running">
<input type="text" class="form-control" placeholder="IP Address" name="ipaddress"
ng-model="accessListInput">
<span class="input-group-btn">
<button class="btn btn-default" type="button"
ng-click="authorizeClient()">Authorize</button>
<button class="btn btn-default" type="button" ng-click="revokeClient()">Revoke</button>
</span>
<div ng-show="evilPortal.running">
<p class="text-muted">
<i>This is a list of clients who have been authorized through the captive portal.</i>
</p>
<p>
<textarea id='authorizedPool' class="form-control" rows="15" ng-mouseup="getClickedClient('authorizedPool', 'accessListInput');" ng-model="accessList.clients" readonly></textarea>
</p>
<div class="input-group">
<input type="text" class="form-control" placeholder="IP Address" name="accessListInput" ng-model="accessList.toManipulate">
<span class="input-group-btn">
<button class="btn btn-default" type="button" ng-click="authorizeClient()">Authorize</button>
<button class="btn btn-default" type="button" ng-click="removeClientFromList('accessList')">Revoke</button>
</span>
</div>
<br />
<button class="btn btn-default btn-sm" type="button" ng-click="getList('accessList')">Refresh</button>
</div>
<div ng-hide="running">
<div ng-hide="evilPortal.running">
<p class="text-muted text-center"><i>Evil Portal must be started first.</i></p>
</div>
</div>
@ -200,14 +237,12 @@
</div>
<div id="collapsePreview" class="panel-collapse collapse">
<div class="panel-body">
<div ng-show="running">
<div ng-show="evilPortal.running">
<iframe src="http://172.16.42.1" style="width:100%;height:300px;border:none;"
id="livePreviewIframe"></iframe>
<button type="submit" ng-click="refreshLivePreview()" class="btn btn-default btn-sm">
Refresh
</button>
<button type="submit" ng-click="refreshLivePreview()" class="btn btn-default btn-sm">Refresh</button>
</div>
<div ng-hide="running">
<div ng-hide="evilPortal.running">
<p class="text-muted text-center"><i>Evil Portal must be started first.</i></p>
</div>
</div>
@ -218,35 +253,72 @@
<div class="panel panel-default">
<div class="panel-heading">
<h5 class="panel-title"><a href="javascript:;" data-toggle="collapse" data-parent="#accordion"
data-target="#collapseChangelog" class="text-muted">Evil Portal Change
Log</a></h5>
data-target="#collapseChangelog" class="text-muted">Evil Portal Info</a></h5>
</div>
<div id="collapseChangelog" class="panel-collapse collapse">
<div class="panel-body">
<ul>
<li><b>2.1</b></li>
<fieldset>
<legend>Disclaimer</legend>
<p><b>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.</b></p>
</fieldset>
<fieldset>
<legend>Help</legend>
<h5>Summary</h5>
<p>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.</p>
<h5>Basic Portals</h5>
<p>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.</p>
<h5>Targeted Portals</h5>
<p>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.</p>
<h5>Useful Links</h5>
<a href="https://forums.hak5.org/index.php?/topic/37874-official-evilportal/">Hak5 Forum Thread</a>
<br />
<a href="https://github.com/frozenjava/EvilPortalNano">GitHub Project</a>
</fieldset>
<fieldset>
<legend>Change Log</legend>
<ul>
<li class="text-muted">Removed un-needed verbosity</li>
<li class="text-muted">Made tab key indent in the editor instead of change elements</li>
<li class="text-muted">Added confirmation dialogue box when deleting a portal</li>
<li class="text-muted">Created auto-start feature</li>
<li class="text-muted">Various other quality of life updates</li>
<li><b>3.0</b></li>
<ul>
<li class="text-muted">Added ability to route clients to different portals based upon some identifier [ssid, mac vendor, ip, etc...]</li>
<li class="text-muted">Updated the work bench so users can choose between targeted and non-targeted portals</li>
<li class="text-muted">Created easy-to-use interface for creating targeting rules</li>
<li class="text-muted">Created some consistency throughout the UI</li>
<li class="text-muted">Added ability to create portals on an SD card and move between SD and Internal storage easily</li>
<li class="text-muted">Made white listed and authorized clients IP addresses clickable like SSIDs in PineAP</li>
<li class="text-muted">Created helper functions getClientMac, getClientSSID, getClientHostName</li>
<li class="text-muted">Sending notification when a client goes through a portal.</li>
<li class="text-muted">Various quality of life improvements</li>
</ul>
</ul>
</ul>
<ul>
<li><b>2.0</b></li>
<ul>
<li class="text-muted">Captive Portal is now purely iptables (because F***
NoDogSplash)
</li>
<li><b>2.1</b></li>
<ul>
<li class="text-muted">Removed un-needed verbosity</li>
<li class="text-muted">Made tab key indent in the editor instead of change elements</li>
<li class="text-muted">Added confirmation dialogue box when deleting a portal</li>
<li class="text-muted">Created auto-start feature</li>
<li class="text-muted">Various other quality of life updates</li>
</ul>
</ul>
</ul>
<ul>
<li><b>1.0</b></li>
<ul>
<li class="text-muted">Initial Pineapple Nano Release</li>
<li><b>2.0</b></li>
<ul>
<li class="text-muted">Captive Portal is now purely iptables (because F***
NoDogSplash)
</li>
</ul>
</ul>
</ul>
<ul>
<li><b>1.0</b></li>
<ul>
<li class="text-muted">Initial Pineapple Nano Release</li>
</ul>
</ul>
</fieldset>
</div>
</div>
</div>
@ -254,6 +326,7 @@
</div>
<!--</div>-->
<!-- Edit file modal -->
<div id="fileModal" class="modal fade" role="dialog">
<script type="text/javascript">
@ -263,7 +336,7 @@
if (keyCode == 9) {
e.preventDefault();
var start = $(this).get(0).selectionStart;
var start = $(this).get(0).selection;
var end = $(this).get(0).selectionEnd;
// set textarea value to: text before caret + tab + text after caret
@ -284,30 +357,27 @@
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
ng-click="editPortalFile = {}">&times;</button>
<h4 class="modal-title">File Editor {{ editPortalFile.file }}</h4>
ng-click="workshop.editFile = {}">&times;</button>
<h4 class="modal-title" ng-show="!workshop.editFile.isNewFile">Editing File {{ workshop.editFile.name }}</h4>
<h4 class="modal-title" ng-show="workshop.editFile.isNewFile">Creating New File</h4>
</div>
<div class="modal-body">
<form class="form-horizontal">
<div class="form-group">
<label class="control-label">File Name</label>
<input type="text" class="form-control" ng-model="editPortalFile.file"
placeholder="Notes.txt" disabled>
<input type="text" class="form-control" ng-model="workshop.editFile.name" placeholder="Notes.txt" ng-disabled="!workshop.editFile.isNewFile">
</div>
<div class="form-group">
<label class="control-label">File Contents</label> <a href="javascript:;"
ng-click="editPortalFile.code = ''">Clear</a>
<textarea class="form-control" rows="10" ng-model="editPortalFile.code"
<label class="control-label">File Contents</label> <a href="javascript:;" ng-click="workshop.editFile.content = null">Clear</a>
<textarea class="form-control" rows="10" ng-model="workshop.editFile.content"
placeholder="Write some text here" id="evilportalEditor"></textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" ng-click="savePortalCode(editPortalFile)" class="btn btn-success pull-left"
data-dismiss="modal">Save
</button>
<button type="button" class="btn btn-default pull-right" data-dismiss="modal"
ng-click="editPortalFile = {}">Cancel
<button type="button" ng-click="saveFileContent(workshop.editFile);" class="btn btn-success pull-right" data-dismiss="modal" ng-disabled="workshop.editFile.content == null">Save</button>
<button type="button" class="btn btn-default pull-left" data-dismiss="modal"
ng-click="workshop.editFile = {}">Cancel
</button>
</div>
</div>
@ -315,7 +385,7 @@
</div>
<!-- Delete file Modal -->
<!-- Delete Portal Modal -->
<div id="deleteModal" class="modal fade" role="dialog">
<div class="modal-dialog">
@ -323,7 +393,7 @@
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" ng-click="portalToDelete = null; portalDeleteValidation = null">&times;</button>
<h4 class="modal-title">Delete Portal {{ portalToDelete.title }}</h4>
<h4 class="modal-title">Delete Portal <i>{{ portalToDelete.title }}</i>?</h4>
</div>
<div class="modal-body">
<p>You are about to delete {{ portalToDelete.title }} on {{ portalToDelete.storage }} storage. Once you do this it can not be undone.</p>
@ -331,8 +401,121 @@
<input type="text" class="form-control" placeholder="PortalName" name="portalName" ng-model="portalDeleteValidation">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary pull-left" data-dismiss="modal" ng-click="portalToDelete = null; portalDeleteValidation = null">Cancel</button>
<button type="button" class="btn btn-danger pull-right" data-dismiss="modal" ng-click="deletePortal(portalToDelete)" ng-disabled="portalDeleteValidation != portalToDelete.title">Delete</button>
<button type="button" class="btn btn-default pull-left" data-dismiss="modal" ng-click="portalToDelete = null; portalDeleteValidation = null">Cancel</button>
<button type="button" class="btn btn-danger pull-right" data-dismiss="modal" ng-click="deletePortal(true, portalToDelete)" ng-disabled="portalDeleteValidation.toLowerCase() != portalToDelete.title.toLowerCase()">Delete</button>
</div>
</div>
</div>
</div>
<!-- Delete file Modal -->
<div id="deleteFileModal" class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" ng-click="workshop.deleteFile = {}">&times;</button>
<h4 class="modal-title">Delete File <i><b>{{ workshop.deleteFile.name }}</b></i>?</h4>
</div>
<div class="modal-body">
<p>You are about to delete <b>{{ workshop.deleteFile.path }}</b>. Once you do this it can not be undone.</p>
<p><b>Are you absolutely sure you want to delete <i>{{ workshop.deleteFile.name }}</i>?</b></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default pull-left" data-dismiss="modal" ng-click="workshop.deleteFile = {}">Cancel</button>
<button type="button" class="btn btn-danger pull-right" data-dismiss="modal" ng-click="deleteFile()">Delete {{ workshop.deleteFile.name }}</button>
</div>
</div>
</div>
</div>
<!-- Rule Editor Modal -->
<div id="ruleEditorModal" class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" ng-click="">&times;</button>
<h4 class="modal-title">Editing Rules for {{ workshop.portal.title }}</h4>
</div>
<div class="modal-body">
<div ng-repeat="(key, data) in workshop.concreteTargetedRules.rules">
<fieldset>
<legend style="text-transform: uppercase">{{ key }}</legend>
<div ng-repeat="(specifier, values) in data">
<h5 style="text-transform: capitalize">{{ specifier }} <a href="javascript:;" data-target="#" ng-click="newTargetedRule(key, specifier);">Add Rule</a></h5>
<div class="table-responsive" ng-hide="isObjectEmpty(workshop.workingTargetedRules.rules[key][specifier])">
<table class="table table-striped" align="center">
<thead>
<th>Rule Key</th>
<th>Destination</th>
<!--<th>Commit</th>-->
<th>Remove</th>
</thead>
<tbody>
<tr ng-repeat="(i, rules) in workshop.workingTargetedRules.rules[key][specifier]">
<td><input type="text" placeholder="Key Value" ng-model="rules.key"></td>
<td><input type="text" placeholder="Destination.php" ng-model="rules.destination"></td>
<!--<td><a href="javascript:;" data-target="#" ng-click="commitPortalRule(key, specifier, i, rules.key, rules.destination)">Commit</a></td>-->
<td><button ng-click="removeTargetedRule(key, specifier, i)" class="btn btn-sm btn-danger">Remove</button></td>
</tr>
</tbody>
</table>
</div>
</div>
</fieldset>
<br />
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default pull-left" data-dismiss="modal" ng-click="">Cancel</button>
<button type="button" class="btn btn-success pull-right" data-dismiss="modal" ng-click="saveTargetedRules()" >Save</button>
</div>
</div>
</div>
</div>
<!-- Toggled command editor -->
<div id="commandEditorModal" class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">Toggle Commands for {{ workshop.portal.title }}</h4>
</div>
<div class="modal-body">
<fieldset>
<legend>On Activation - <a ng-click="saveToggleCommands('enable')">Save</a></legend>
<div class="center-block">
<!-- For sake of getting EP 3.0 release, I'm not going to implement this just yet.
<span class="center-block">
<button type="submit" class="btn btn-link btn-sm">Enable PineAP</button>
<button type="submit" class="btn btn-link btn-sm">Enable Karma Associations</button>
<button type="submit" class="btn btn-link btn-sm">Enable Beacon Response</button>
</span>-->
</div>
<textarea style="width:100%;" rows="10" ng-model="workshop.onEnable"></textarea>
</fieldset>
<fieldset>
<legend>On Deactivation - <a ng-click="saveToggleCommands('disable')">Save</a></legend>
<div class="row-fluid">
<!-- For sake of getting EP 3.0 release, I'm not going to implement this just yet.
<span class="center-block">
<button type="submit" class="btn btn-link btn-sm">Disable PineAP</button>
<button type="submit" class="btn btn-link btn-sm">Disable Karma Associations</button>
<button type="submit" class="btn btn-link btn-sm">Disable Beacon Response</button>
</span>-->
</div>
<textarea style="width:100%;" rows="10" ng-model="workshop.onDisable"></textarea>
</fieldset>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default pull-left" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-success pull-right" data-dismiss="modal" ng-click="saveToggleCommands('all')">Save All</button>
</div>
</div>
</div>

View File

@ -6,5 +6,5 @@
"tetra"
],
"title": "Evil Portal",
"version": "2.1"
}
"version": "3.0"
}

View File

@ -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);
}

View File

@ -6,7 +6,7 @@
<div id="LEDController" class="panel-collapse collapse">
<div class="panel-body">
<h3 style="text-align: center;">LED Controller</h3>
<h6 style="text-align: center;">Version 1.1</h6>
<h6 style="text-align: center;">Version 1.3</h6>
<p style="text-align: center;">Written by Foxtrot</p>
<p style="text-align: center;">This module allows you to control the LEDs behaviour on both the NANO and the TETRA.</p>

View File

@ -6,5 +6,5 @@
"tetra"
],
"title": "LED Controller",
"version": "1.1"
}
"version": "1.3"
}

View File

@ -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);
}
}
}

View File

@ -6,5 +6,5 @@
"tetra"
],
"title": "MAC Info",
"version": "1.2"
"version": "1.3"
}

View File

@ -6,5 +6,5 @@
"tetra"
],
"title": "Online Hash Crack",
"version": "1.2"
"version": "1.3"
}

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,214 @@
<?php
// Define the pineapple namespace
namespace pineapple;
// Upload Directory where we store vpn_configs
define('__UPLOAD__', "/root/vpn_config/");
/* Main module class for OpenVPNConnect */
class OpenVPNConnect extends Module{
// Set up our routes for our angular functions to call
public function route(){
switch ($this->request->action) {
case 'startVPN':
$this->startVPN();
break;
case 'stopVPN':
$this->stopVPN();
break;
case 'initializeModule':
$this->initializeModule();
break;
case 'handleDependencies':
$this->handleDependencies();
break;
case 'checkDependencies':
$this->checkDependencies();
break;
case 'uploadFile':
$this->uploadFile();
}
}
// Checks the dependencies using the pineapple API functions
private function checkDependencies(){
if($this->checkDependency('openvpn')){
$installLabel = 'success';
$installLabelText = 'Installed';
}else{
$installLabel = 'danger';
$installLabelText = 'Not Installed';
}
$this->response = array("success" => true,
"label" => $installLabel,
"text" => $installLabelText);
}
// Initializes the module by checking for/creating the required vpn_config directory in /root
private function initializeModule(){
$result = exec('cd /root && ls | grep vpn_config');
if($result == 'vpn_config'){
$result = "VPN Connect is ready!";
}else{
$this->execBackground('cd /root && mkdir vpn_config');
$result = exec('cd /root && ls | grep vpn_config');
if($result == 'vpn_config'){
$result = "VPN Connect is ready!";
}else{
$result = "VPN Connect setup failed :(";
}
}
//Get Available Certs
$certs = preg_grep('/^([^.])/', scandir("/root/vpn_config/"));
$cert_arr = array();
foreach ($certs as $cert){
array_push($cert_arr, (object)array('name' => $cert));
}
$this->response = array("success" => true,
"content" => $result,
"certs" => $cert_arr);
}
// Handles dependency installation and removal
private function handleDependencies(){
if($this->checkDependency('openvpn')){
exec('opkg remove openvpn-openssl');
$messsage = "Dependencies should now be removed! Note: the vpn_config directory is NOT removed in this process. Please wait for the page to refresh...";
}else{
$this->installDependency('openvpn-openssl');
$messsage = "Depedencies should now be installed! Please wait for the page to refresh...";
}
$this->response = array("success" => true,
"content" => $messsage);
}
// Builds the openvpn command string and calls it to star the VPN
private function startVPN(){
$inputData = $this->request->data;
$open_vpn_cmd = "openvpn --config ";
if($inputData[0] != ''){
$config_name = $inputData[0];
$open_vpn_cmd .= "/root/vpn_config/" . $config_name . " ";
}else{
$this->response = array("success" => false,
"content" => "Please specify a VPN config name..");
return;
}
if($inputData[1] != ''){
//Create password file for openvpn command to read in
$config_pass = $inputData[1];
$pass_file = fopen("/tmp/vpn_pass.txt", "w");
fwrite($pass_file, $config_pass);
fclose($pass_file);
$open_vpn_cmd .= "--auth-nocache --askpass /tmp/vpn_pass.txt ";
}
if($inputData[2] != ''){
$openvpn_flags = $inputData[2];
$open_vpn_cmd .= $openvpn_flags;
}
if($inputData[3] == true){
//Share VPN With Clients Connecting
$this->execBackground("iptables -t nat -A POSTROUTING -s 172.16.42.0/24 -o tun0 -j MASQUERADE");
$this->execBackground("iptables -A FORWARD -s 172.16.42.0/24 -o tun0 -j ACCEPT");
$this->execBackground("iptables -A FORWARD -d 172.16.42.0/24 -m state --state ESTABLISHED,RELATED -i tun0 -j ACCEPT");
}
$result = $this->execBackground($open_vpn_cmd);
$this->response = array("success" => true,
"content" => "VPN Running... ");
}
// Calls pkill to kill the OpenVPN process and stop the VPN
private function stopVPN(){
//Remove password file that could have been created, don't want any creds lying around ;)
unlink("/tmp/vpn_pass.txt");
exec("pkill openvpn");
$this->response = array("success" => true,
"content" => "VPN Stopped...");
}
// Uploads the .ovnp recieved from the service
private function uploadFile(){
$inputData = $this->request->file;
$fileName = $inputData[0];
$file = base64_decode($inputData[1]);
$response = [];
$name = $fileName;
$type = pathinfo($fileName, PATHINFO_EXTENSION);
// Do not accept any file other than .ovpn
if ($type != "ovpn") {
$this->response = array("success" => false);
return;
}
// Ensure the upload directory exists
if (!file_exists(__UPLOAD__)) {
if (!mkdir(__UPLOAD__, 0755, true)) {
$response[$name] = "Failed. Unable to upload because vpn_certs directory does not exist/could not be created!";
$this->response = array("success" => false);
return;
}
}
$uploadPath = __UPLOAD__ . $name;
$res = file_put_contents( $uploadPath, $file );
if ($res) {
$response[$name] = true;
} else {
$response[$name] = false;
}
$this->response = array("success" => $response[$name]);
}
}

222
OpenVPNConnect/js/module.js Normal file
View File

@ -0,0 +1,222 @@
/* Main AngularJS openVPNConnectController */
registerController('openVPNConnectController', ['$api', '$scope', '$timeout', '$window', '$http', function($api, $scope, $timeout, $window, $http) {
// Workspace Variables. Each value is populated by the form or displays to the form.
$scope.workspace = {config: "",
pass: "",
flags: "",
sharedconnection: false,
setupcontent: "",
outputcontent: "",
availablecerts: [],
uploadstatusLabel: "",
uploadstatus: ""};
/* Other variables used to display content or assist the dependency installation and file upload
functions
*/
$scope.content = "";
$scope.installLabel = "default";
$scope.installLabelText = "Checking..."
$scope.selectedFiles = [];
$scope.uploading = false;
// Call a function to install/uninstall dependencies for the module
$scope.handleDependencies = function(){
$scope.workspace.setupcontent = "Handling dependencies please wait...";
$api.request({
module: 'OpenVPNConnect',
action: 'handleDependencies',
}, function(response) {
if (response.success === true) {
$scope.workspace.setupcontent = response.content;
checkDependencies();
$timeout(function() {$window.location.reload();}, 5000);
}
//console.log(response) //Log the response to the console, this is useful for debugging.
});
}
/* Checks the current status of the dependencies for the module and displays
it via the dependency install/uninstall button to the user.
This is checked each time the app is loaded or when the user
installs/uninstalls dependencies manually
*/
var checkDependencies = function(){
$api.request({
module: 'OpenVPNConnect',
action: 'checkDependencies',
}, function(response) {
if (response.success === true) {
$scope.installLabel = response.label;
$scope.installLabelText = response.text;
}
//console.log(response) //Log the response to the console, this is useful for debugging.
});
}
// Call the checkDependencies function on page load
checkDependencies();
/* Initializes module by creating necessary folder structures and scanning for uploaded certs
this function is called each time the app is loaded to make sure the module is set up correctly
*/
var initializeModule = function(){
$api.request({
module: 'OpenVPNConnect',
action: 'initializeModule',
}, function(response) {
if (response.success === true) {
$scope.workspace.setupcontent = response.content;
for(var i = 0; i <= response.certs.length - 1; i++){
$scope.workspace.availablecerts.push(response.certs[i].name);
}
}
//console.log(response) //Log the response to the console, this is useful for debugging.
});
}
// Call the initializeModule function on page load
initializeModule();
/* Just calls the initializeModule function to refresh the cert list when the
user clicks the drop down menu button
*/
$scope.refreshCertList = function(){
$scope.workspace.availablecerts = [];
initializeModule();
}
// Sets the current config to use for the VPN connection
$scope.setConfig = function(cert){
$scope.workspace.config = cert;
}
/* Calls the startVPN function, passes all the form params to the API to run the OpenVPN command
Users can pass a config, an option password, and optional command line flags to run with the
openvpn command line utility. Also the shared connection open lets the user share the
VPN connection with its clients
*/
$scope.startVPN = function() {
$api.request({
module: 'OpenVPNConnect',
action: 'startVPN',
data: [$scope.workspace.config,
$scope.workspace.pass,
$scope.workspace.flags,
$scope.workspace.sharedconnection]
}, function(response) {
if (response.success === true) {
$scope.workspace.outputcontent = response.content;
}
//console.log(response) //Log the response to the console, this is useful for debugging.
});
}
// This function calls the API to kill the openvpn process and stop the VPN
$scope.stopVPN = function() {
$api.request({
module: 'OpenVPNConnect',
action: 'stopVPN'
}, function(response) {
if (response.success === true) {
$scope.workspace.outputcontent = response.content;
}
//console.log(response) //Log the response to the console, this is useful for debugging.
});
}
//File Upload Code, the first two functions prep the files for upload in the modal
$scope.setSelectedFiles = function(){
files = document.getElementById("selectedFiles").files;
for (var x = 0; x < files.length; x++) {
$scope.selectedFiles.push(files[x]);
}
};
$scope.removeSelectedFile = function(file){
var x = $scope.selectedFiles.length;
while (x--) {
if ($scope.selectedFiles[x] === file) {
$scope.selectedFiles.splice(x,1);
}
}
};
/* File upload function. Instaitates a FileReader object the makes a promise call to
doUpload once the async reader call is complete on each iteration
*/
$scope.uploadFile = function(){
$scope.uploading = true;
for (x = 0; x < $scope.selectedFiles.length; x++) {
var fileReader = new FileReader();
var fileName = $scope.selectedFiles[x].name;
var filesToUpload = $scope.selectedFiles.length;
readFile($scope.selectedFiles[x], fileName, filesToUpload);
}
};
// Read file function to handle a Promise for multiple file uploads using FilerReader
function readFile(file, file_name, files_to_upload){
return new Promise((resolve, reject) => {
var fr = new FileReader();
fr.onload = () => {
final_file = fr.result.split(',')[1]
resolve(doUpload(file_name, final_file, files_to_upload - 1));
};
fr.readAsDataURL(file);
});
}
/* Actually performs the upload request to the API. Passes the file name and a base64 encoded
file to be uploaded by the service
*/
var doUpload = function(file_name, file, files_to_upload){
$api.request({
module: 'OpenVPNConnect',
action: 'uploadFile',
file: [file_name,
file
]
}, function(response) {
if(response.success){
$scope.workspace.uploadstatusLabel = "Upload Success!";
$scope.workspace.uploadstatus = "success";
}else{
$scope.workspace.uploadstatusLabel = "One or more files failed to upload!";
$scope.workspace.uploadstatus = "danger";
}
if(files_to_upload === 0){
$scope.selectedFiles = [];
$scope.uploading = false;
}
//console.log(response) //Log the response to the console, this is useful for debugging.
});
};
}]);

151
OpenVPNConnect/module.html Normal file
View File

@ -0,0 +1,151 @@
<div class="col-md-12" ng-controller="openVPNConnectController">
<div class="panel-group">
<div class="col-md-12">
<!-- OpenVPN Connect Title Panel -->
<div class="panel panel-default">
<div class="panel-header text-muted text-center">
<h3>OpenVPN Connect
</h5>
</div>
<div id="main-panel" class="panel panel-default">
<div class="panel-body">
<!-- Dependency Installer UI -->
<div class="panel-body">
<div class="form-group">
<label class="control-label text-center">Dependencies:</label>
<button type="button" style="width: 90px;" class="btn btn-{{installLabel}} btn-xs" ng-click="handleDependencies()">{{installLabelText}}</button>
</div>
<div class="form-group">
<label class="control-label text-center">Setup Status</label>
<p>
<input class="form-control" ng-model="workspace.setupcontent" placeholder="" readonly></input>
</p>
</div>
</div>
<!-- OpenVPN Cert Selector -->
<div class="panel-body">
<form class="form-horizontal">
<div class="form-group">
<p>
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#ovpn_upload_view">Upload Cert</button>
</p>
<p>
<div>
<label class="control-label">
<a href="javascript:;" data-toggle="collapse" data-parent="#accordion" data-target="#collapseCertList">
<button type="button" class="btn btn-default" ng-click="refreshCertList()">
Select Cert +
</button>
</a>
</label>
<div id="collapseCertList" class="panel-collapse collapse">
<div class="panel panel-default">
<div>
<ul class="list-group text-center" ng-repeat="cert in workspace.availablecerts">
<li class="list-group-item">
<button type="button" class="btn btn-default" ng-click="setConfig(cert)">{{cert}}</button>
</ul>
</ul>
</div>
</div>
</div>
</div>
</p>
<!-- OpenVPN Connect Option Form -->
<label class="control-label text-center">VPN Config Name:</label>
<a href="javascript:;" ng-click="workspace.config = ''">Clear</a>
<p>
<input class="form-control" ng-model="workspace.config" placeholder="Enter input"></input>
</p>
<label class="control-label text-center">VPN Config Password (Optional):</label>
<a href="javascript:;" ng-click="workspace.pass = ''">Clear</a>
<p>
<input class="form-control" type="password" ng-model="workspace.pass" placeholder="Enter input"></input>
</p>
<label class="control-label text-center">OpenVPN Flags (Optional):</label>
<a href="javascript:;" ng-click="workspace.flags = ''">Clear</a>
<p>
<input class="form-control" ng-model="workspace.flags" placeholder="Enter input"></input>
</p>
<label class="control-label text-center">Share Connection With Clients:</label>
<input type="checkbox" ng-model="workspace.sharedconnection">
<div class="input-group">
<span class="input-group-btn text-center">
<button class="btn btn-default" type="button" ng-click="startVPN()">Start VPN</button>
<button class="btn btn-default" type="button" ng-click="stopVPN()">Stop VPN</button>
</span>
</div>
</div>
</form>
</div>
<!-- OpenVPN Status Section -->
<div class="panel-body">
<div class="form-group">
<label class="control-label text-center">OpenVPN Status</label>
<p>
<textarea class="form-control" rows="1" ng-model="workspace.outputcontent" placeholder="" readonly></textarea>
</p>
</div>
</div>
</div>
</div>
</div>
<!-- OpenVPN Cert Upload Modal -->
<div id="ovpn_upload_view" class="modal fade" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<div class="btn btn-primary">
<label for="selectedFiles" style="cursor: pointer">Add files</label>
</div>
</div>
<div class="modal-body">
<table class="table">
<thead>
<th>File</th>
<th>Actions</th>
</thead>
<tbody>
<tr ng-repeat="file in selectedFiles">
<td>{{ file.name }}</td>
<td>
<button class="btn btn-danger" ng-click="removeSelectedFile(file);">Remove
</td>
</tr>
</tbody>
</table>
<div style="text-align:center">
<input type="file" accept=".ovpn" id="selectedFiles" onchange="angular.element(this).scope().setSelectedFiles()" style="visibility: hidden;" multiple>
<img ng-show="uploading" ng-hide="!uploading">
<button class="btn" ng-show="!uploading" ng-hide="uploading" ng-disabled="selectedFiles.length == 0" ng-click="uploadFile();">Upload</button>
<div class="alert alert-{{workspace.uploadstatus}}">{{workspace.uploadstatusLabel}}</div>
</div>
</div>
</div>
</div>
</div>
<!-- Change Log Pannel -->
<div class="panel panel-default">
<div class="panel-header text-muted text-center">
<h5>
<a href="javascript:;" data-toggle="collapse" data-parent="#accordion" data-target="#collapseChangelog">Change Log</a>
</h5>
</div>
<div id="collapseChangelog" class="panel-collapse collapse">
<div class="panel-body">
<ul>
<li>
<b>1.0</b>
</li>
<ul>
<li class="text-muted">Initial Pineapple Nano Release</li>
</ul>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,10 @@
{
"author": "3ndG4me",
"description": "OpenVPN Connection Utility",
"devices": [
"nano",
"tetra"
],
"title": "OpenVPNConnect",
"version": "1.0"
}

View File

@ -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.

View File

@ -6,5 +6,5 @@
"tetra"
],
"title": "SSLsplit",
"version": "1.0"
}
"version": "1.2"
}

View File

@ -9,6 +9,7 @@ MYTIME=`date +%s`
killall sslsplit
echo '1' > /proc/sys/net/ipv4/ip_forward
iptables-save > /pineapple/modules/SSLsplit/rules/saved
iptables -X
iptables -F
iptables -t nat -F
@ -20,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

View File

@ -8,9 +8,14 @@ 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
iptables-save > /pineapple/modules/SSLsplit/rules/saved
iptables -X
iptables -F
iptables -t nat -F
@ -37,5 +42,6 @@ elif [ "$1" = "stop" ]; then
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
iptables-restore < /pineapple/modules/SSLsplit/rules/saved
fi

View File

@ -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);
}

View File

@ -6,5 +6,5 @@
"tetra"
],
"title": "Site Survey",
"version": "1.2"
}
"version": "1.4"
}

View File

@ -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}

View File

@ -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

View File

@ -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);
}

View File

@ -6,5 +6,5 @@
"tetra"
],
"title": "Status",
"version": "1.1"
}
"version": "1.2"
}

565
Themes/api/module.php Normal file
View File

@ -0,0 +1,565 @@
<?php namespace pineapple;
class Themes extends Module
{
// CONSTANTS
private $MODULE_DIR = '/pineapple/modules/Themes/';
private $CSS_DIR = '/pineapple/modules/Themes/css/';
private $SKELETON_CSS = '/pineapple/modules/Themes/css/main.css';
private $CURRENT_CSS = '/pineapple/modules/Themes/css/CURRENT_CSS';
private $CURRENT_LOGO = '/pineapple/modules/Themes/img/CURRENT_LOGO';
private $CURRENT_THROBBER = '/pineapple/modules/Themes/img/CURRENT_THROBBER';
private $CURRENT_FAVICON = '/pineapple/modules/Themes/img/CURRENT_FAVICON';
private $CURRENT_DASHBOARD = '/pineapple/modules/Themes/img/CURRENT_DASHBOARD';
private $BACKUP_MAIN_CSS = '/pineapple/css/.backup.main.css';
private $BACKUP_FAVICON = '/pineapple/img/.backup.favicon.ico';
private $BACKUP_THROBBER = '/pineapple/img/.backup.throbber.gif';
private $BACKUP_LOGO = '/pineapple/img/.backup.logo.png';
private $BACKUP_DASHBOARD = '/pineapple/modules/Dashboard/.backup.module_icon.svg';
private $current_theme = "";
/*
* Static list of all module names
* from here we can grab the CURRENT file, and the module directory
*/
private $ALL_MODULES = array(
"Dashboard",
"Recon",
"Profiling",
"Clients",
"ModuleManager",
"Filters",
"PineAP",
"Tracking",
"Logging",
"Reporting",
"Networking",
"Configuration",
"Advanced",
"Help",
);
/*
* Faster to map Hex values then to calculate on the fly
*/
private $WHITE_HEX = 'ffffff';
private $RESTORE_HEX = '808080';
private $NORMAL_COLOR_HEX_MAP = array(
"red" => "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_<MODULE_ICON> 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;
}
}

499
Themes/css/1980.css Normal file
View File

@ -0,0 +1,499 @@
.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: middle;
border-top: 1px solid lime;
}
.switch {
position: relative;
display: inline-block;
width: 46px;
height: 20px;
}
.switch input {display:none;}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: darkgray;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 12px;
width: 12px;
left: 4px;
bottom: 4px;
background-color: black;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: lime;
}
input:focus + .slider {
box-shadow: 0 0 1px lime;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}

250
Themes/css/default.css Normal file
View File

@ -0,0 +1,250 @@
.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;
}
.switch {
position: relative;
display: inline-block;
width: 46px;
height: 20px;
}
.switch input {display:none;}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 12px;
width: 12px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #2196F3;
}
input:focus + .slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}

526
Themes/css/evilrose.css Normal file
View File

@ -0,0 +1,526 @@
.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);
}
.switch {
position: relative;
display: inline-block;
width: 46px;
height: 20px;
}
.switch input {display:none;}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 12px;
width: 12px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #ff0066;
}
input:focus + .slider {
box-shadow: 0 0 1px #ff0066;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}

522
Themes/css/hackerblues.css Normal file
View File

@ -0,0 +1,522 @@
.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: middle;
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: middle;
border-top: 1px solid #0066ff;
}
.modal-content {
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;
}
.switch {
position: relative;
display: inline-block;
width: 46px;
height: 20px;
}
.switch input {display:none;}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 12px;
width: 12px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #0066ff;
}
input:focus + .slider {
box-shadow: 0 0 1px #0066ff;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}

499
Themes/css/hak5.css Normal file
View File

@ -0,0 +1,499 @@
.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;
}
.switch {
position: relative;
display: inline-block;
width: 46px;
height: 20px;
}
.switch input {display:none;}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 12px;
width: 12px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: red;
}
input:focus + .slider {
box-shadow: 0 0 1px red;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}

View File

@ -0,0 +1,525 @@
.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);
}
.switch {
position: relative;
display: inline-block;
width: 46px;
height: 20px;
}
.switch input {display:none;}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 12px;
width: 12px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #991f00;
}
input:focus + .slider {
box-shadow: 0 0 1px #991f00;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}

525
Themes/css/halflife.css Normal file
View File

@ -0,0 +1,525 @@
.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);
}
.switch {
position: relative;
display: inline-block;
width: 46px;
height: 20px;
}
.switch input {display:none;}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 12px;
width: 12px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: black;
}
input:focus + .slider {
box-shadow: 0 0 1px black;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}

576
Themes/css/kbeflo-dark.css Normal file
View File

@ -0,0 +1,576 @@
.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;
}
.switch {
position: relative;
display: inline-block;
width: 46px;
height: 20px;
}
.switch input {display:none;}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: gray;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 12px;
width: 12px;
left: 4px;
bottom: 4px;
background-color: black;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: whitesmoke;
}
input:focus + .slider {
box-shadow: 0 0 1px whitesmoke;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}

250
Themes/css/main.css Normal file
View File

@ -0,0 +1,250 @@
.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;
}
.switch {
position: relative;
display: inline-block;
width: 46px;
height: 20px;
}
.switch input {display:none;}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 12px;
width: 12px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #2196F3;
}
input:focus + .slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}

525
Themes/css/malware.css Normal file
View File

@ -0,0 +1,525 @@
.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);
}
.switch {
position: relative;
display: inline-block;
width: 46px;
height: 20px;
}
.switch input {display:none;}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: darkgray;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 12px;
width: 12px;
left: 4px;
bottom: 4px;
background-color: black;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: red;
}
input:focus + .slider {
box-shadow: 0 0 1px red;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}

511
Themes/css/morning.css Normal file
View File

@ -0,0 +1,511 @@
.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);
}
.switch {
position: relative;
display: inline-block;
width: 46px;
height: 20px;
}
.switch input {display:none;}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 12px;
width: 12px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: rosybrown;
}
input:focus + .slider {
box-shadow: 0 0 1px rosybrown;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}

504
Themes/css/neon.css Normal file
View File

@ -0,0 +1,504 @@
.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);
}
.switch {
position: relative;
display: inline-block;
width: 46px;
height: 20px;
}
.switch input {display:none;}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 12px;
width: 12px;
left: 4px;
bottom: 4px;
background-color: lime;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: purple;
}
input:focus + .slider {
box-shadow: 0 0 1px purple;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}

526
Themes/css/spring.css Normal file
View File

@ -0,0 +1,526 @@
.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);
}
.switch {
position: relative;
display: inline-block;
width: 46px;
height: 20px;
}
.switch input {display:none;}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: darkslategray;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 12px;
width: 12px;
left: 4px;
bottom: 4px;
background-color: #6666ff;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #00e673;
}
input:focus + .slider {
box-shadow: 0 0 1px #00e673;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}

525
Themes/css/starstuff.css Normal file
View File

@ -0,0 +1,525 @@
.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);
}
.switch {
position: relative;
display: inline-block;
width: 46px;
height: 20px;
}
.switch input {display:none;}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: darkgray;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 12px;
width: 12px;
left: 4px;
bottom: 4px;
background-color: #000038;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: gold;
}
input:focus + .slider {
box-shadow: 0 0 1px gold;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}

BIN
Themes/img/favicon-dark.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 533 B

BIN
Themes/img/logo-dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

604
Themes/js/module.js Normal file
View File

@ -0,0 +1,604 @@
registerController("ThemesController", ['$api', '$scope','$window','$route', '$http', function ($api, $scope, $window, $route, $http) {
getThemes();
getCurrentTheme();
backupFiles();
$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.autoRefresh = true;
// 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';
// Color 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);
if ($scope.autoRefresh) {
$window.location.reload();
}
});
};
$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);
if ($scope.autoRefresh) {
$window.location.reload();
}
});
};
$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);
if ($scope.autoRefresh) {
$window.location.reload();
}
});
};
$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 + "(" + $scope.allcontrastBrightness + ")");
log("changeAllIcons", "Success? " + response.success + " " + response.message);
}
if ($scope.autoRefresh) {
$window.location.reload();
}
});
};
$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);
if ($scope.autoRefresh) {
$window.location.reload();
}
});
};
$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);
if ($scope.autoRefresh) {
$window.location.reload();
}
});
};
$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);
if ($scope.autoRefresh) {
$window.location.reload();
}
});
};
$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);
if ($scope.autoRefresh) {
$window.location.reload();
}
});
};
$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);
if ($scope.autoRefresh) {
$window.location.reload();
}
});
};
$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);
if ($scope.autoRefresh) {
$window.location.reload();
}
});
};
$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);
if ($scope.autoRefresh) {
$window.location.reload();
}
});
};
$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);
if ($scope.autoRefresh) {
$window.location.reload();
}
});
};
$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);
if ($scope.autoRefresh) {
$window.location.reload();
}
});
};
$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);
if ($scope.autoRefresh) {
$window.location.reload();
}
});
};
$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);
if ($scope.autoRefresh) {
$window.location.reload();
}
});
};
$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);
if ($scope.autoRefresh) {
$window.location.reload();
}
});
};
$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);
if ($scope.autoRefresh) {
$window.location.reload();
}
});
};
$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);
if ($scope.autoRefresh) {
$window.location.reload();
}
});
};
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<response.modules.length; i++) {
log('backupFiles', response.modules[i]);
}
});
};
function getCurrentTheme() {
$api.request({
module: "Themes",
action: "getCurrentTheme"
}, function(response) {
$scope.current = response.current;
$scope.logocontrastText = response.logo;
$scope.faviconcontrastText = response.icon;
$scope.throbbercontrastText = response.throbber;
$scope.dashboardcontrastText = response.dashboard;
$scope.dashboardcontrastBrightness = response.dashboardbrightness;
$scope.reconcontrastText = response.recon;
$scope.reconcontrastBrightness = response.reconbrightness;
$scope.profilingcontrastText = response.profiling;
$scope.profilingcontrastBrightness = response.profilingbrightness;
$scope.clientscontrastText = response.clients;
$scope.clientscontrastBrightness = response.clientsbrightness;
$scope.modulescontrastText = response.modulemanager;
$scope.modulescontrastBrightness = response.modulemanagerbrightness;
$scope.filterscontrastText = response.filters;
$scope.filterscontrastBrightness = response.filtersbrightness;
$scope.pineapcontrastText = response.pineap;
$scope.pineapcontrastBrightness = response.pineapbrightness;
$scope.trackingcontrastText = response.tracking;
$scope.trackingcontrastBrightness = response.trackingbrightness;
$scope.loggingcontrastText = response.logging;
$scope.loggingcontrastBrightness = response.loggingbrightness;
$scope.reportingcontrastText = response.reporting;
$scope.reportingcontrastBrightness = response.reportingbrightness;
$scope.networkingcontrastText = response.networking;
$scope.networkingcontrastBrightness = response.networkingbrightness;
$scope.configurationcontrastText = response.configuration;
$scope.configurationcontrastBrightness = response.configurationbrightness;
$scope.advancedcontrastText = response.advanced;
$scope.advancedcontrastBrightness = response.advancedbrightness;
$scope.helpcontrastText = response.help;
$scope.helpcontrastBrightness = response.helpbrightness;
$scope.throbbercontrast = true;
$scope.faviconcontrast = true;
$scope.logocontrast = true;
if (response.throbber === 'dark') {
$scope.throbbercontrast = false;
}
if (response.icon === 'dark') {
$scope.faviconcontrast = false;
}
if (response.logo === 'dark') {
$scope.logocontrast = false;
}
log("getCurrentTheme", "Current theme is " + $scope.current);
log("getCurrentTheme", "Current throbber is " + response.throbber);
log("getCurrentTheme", "Current icon is " + response.icon);
log("getCurrentTheme", "Current logo is " + response.logo);
log("getCurrentTheme", "Current Dashboard is " + response.dashboard);
});
};
$scope.file_changed = function(element) {
$scope.$apply(function(scope) {
var photofile = element.files[0];
var reader = new FileReader();
reader.onload = function(e) {
$scope.$apply(function() {
$scope.prev_img = e.target.result;
});
};
reader.readAsDataURL(photofile);
});
};
$scope.restoreDefault = function() {
$scope.working = "Working..";
console.log("Restore Default Function Called");
$scope.throbber = true;
$api.request({
module: "Themes",
action: "restoreDefault"
}, function (response) {
log("restoreDefault", "default CSS restored");
$scope.sendMessage("Restore", "Default CSS restored");
$scope.throbber = false;
log("restoreDefault", "Successful? "+ response.success + ". " + response.message);
$scope.working = "Done!";
if ($scope.autoRefresh) {
$window.location.reload();
}
});
};
$scope.sendMessage = function (t, m) {
// Add a new message to the top of the list
$scope.messages.unshift({title: t, msg: m});
log("sendMessage", m);
// if there are 4 items in the list remove the 4th item
if ($scope.messages.length == 4) {
$scope.dismissMessage(3);
}
};
$scope.dismissMessage = function ($index) {
//var index = $scope.messages.indexOf(message);
$scope.messages.splice($index, 1);
log("dismissMessage", "message at index " + $index + " dismissed" );
};
$scope.createNewTheme = function () {
$api.request({
module: "Themes",
action: "createNewTheme",
themeName: $scope.newThemeName
}, function (response) {
if (response.create_success) {
log("createNewTheme", "success! new theme '"+ $scope.newThemeName + "' created");
getThemes();
$scope.newThemeName = '';
} else {
$scope.sendMessage("Error Creating Theme", response.create_message);
log("createNewTheme", "error! " + response.create_message);
}
});
};
$scope.deleteTheme = function (theme) {
$api.request({
module: "Themes",
action: "deleteTheme",
name: theme.title
}, function (response) {
$scope.sendMessage("Delete", response.message);
getThemes();
});
log('deleteTheme', 'Deleting Theme: ' + theme.title );
};
$scope.activateTheme = function (theme) {
$api.request({
module: "Themes",
action: "activateTheme",
name: theme.title
}, function (response) {
if (response.return === true) {
$scope.currentTheme = theme.title;
getThemes();
$scope.sendMessage("Activated", response.message);
if ($scope.autoRefresh) {
$window.location.reload();
}
}
});
};
$scope.editTheme = function (theme) {
console.log("action: edit " + theme.name);
$api.request({
module: "Themes",
action: "getThemeCode",
name: theme.name
}, function (response) {
$scope.editThemeFile.code = response.code;
$scope.editThemeFile.file = response.file;
$scope.editThemeFile.themeName = theme.name;
});
};
$scope.saveThemeCode = function (editFile) {
console.log("action: save " + editFile.themeName);
$api.request({
module: "Themes",
action: "submitThemeCode",
themeCode: editFile.code,
name: editFile.themeName,
fileName: editFile.file
}, function (response) {
$scope.sendMessage("Saved", editFile.themeName);
});
if ($scope.current === editFile.themeName)
{
$api.request({
module: "Themes",
action: "activateTheme",
name: editFile.themeName
}, function (response) {
if (response.return === true) {
$scope.currentTheme = editFile.themeName;
$scope.sendMessage("Activated", response.message);
}
});
log('saveThemeCode', "Theme code saved, also current so set to inactive");
}
};
$scope.getThemeFields = function (theme) {
$api.request({
module: "Themes",
action: "themeFields",
name: theme.title
}, function (response) {
$scope.workshopTheme.themeName = theme.title;
$scope.workshopTheme.title = theme.title;
$scope.workshopTheme.code = response.code;
console.log(response);
$scope.library = false;
});
};
function getThemes() {
$api.request({
module: "Themes",
action: "getThemeList"
}, function (response) {
$scope.themes = [];
for (var i = 0; i < response.length; i++) {
$scope.themes.unshift({
title: response[i].title,
storage: response[i].location,
active: response[i].active
});
}
});
}
}]);

786
Themes/module.html Normal file
View File

@ -0,0 +1,786 @@
<div class="row" ng-controller="ThemesController">
<div class="col-md-3">
<div class="panel panel-default">
<div class="panel-heading">
<a href="javascript:;" data-toggle="collapse" data-target="#collapseControls" class="text-muted"><h4
class="panel-title"><b>Theme:</b> {{ current.substring(0, current.length - 4); }}</h4></a>
</div>
<div id="collapseControls" class="panel-collapse collapse in">
<div class="panel-body">
<table style="width:100%">
<tr>
<td>
Auto Refresh
</td>
<td>
<label class="switch" >
<input type="checkbox" ng-model="autoRefresh">
<span class="slider round">
</span>
</label>
</td>
<td style="text-align:right;">
<button class="btn btn-default"
data-toggle="modal"
data-target="#loadingModal"
type="button"
ng-click="restoreDefault()">Restore Default</button>
</td>
</tr>
<tr>
</tr>
</table>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<a href="javascript:;" data-toggle="collapse" data-target="#collapseMessages" class="text-muted"><h4
class="panel-title">Themes Messages</h4></a>
</div>
<div id="collapseMessages" class="panel-collapse collapse in">
<div class="panel-body">
<p ng-show="(messages.length == 0)" class="text-muted"><i>No Messages...</i></p>
<a ng-hide="(messages.length < 2)" ng-click="messages = []" class="pull-right" href="javascript:;">Clear
All</a>
<table style="width:100%" ng-hide="(messages.length == 0)">
<tr ng-repeat="message in messages">
<td>
<hr/>
<h5><b>{{ message.title }}</b> <a ng-click="dismissMessage($index)" href="javascript:;"
class="pull-right">Dismiss</a></h5>
<p class="text-muted"><i>{{ message.msg }}</i></p>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<a href="javascript:;" data-toggle="collapse" data-target="#collapseIconColors" class="text-muted"><h4
class="panel-title">Icon Colors</h4></a>
</div>
<div id="collapseIconColors" class="panel-collapse collapse in">
<div class="panel-body">
<table class="table table-striped" align="left">
<thead>
<th>Icon</th>
<th>Color</th>
<th>Brightness</th>
</thead>
<tbody>
<tr><td>Throbber</td><td> {{ throbbercontrastText }}</td><td> N/A </td> </tr>
<tr><td>Logo</td><td> {{ logocontrastText }}</td><td> N/A </td> </tr>
<tr><td>Favicon</td><td> {{ faviconcontrastText }}</td><td> N/A </td> </tr>
<tr><td>Dashboard</td><td> {{ dashboardcontrastText }}</td><td> {{ dashboardcontrastBrightness }}</td> </tr>
<tr><td>Recon</td><td> {{ reconcontrastText }}</td><td> {{ reconcontrastBrightness }}</td> </tr>
<tr><td>Profiling</td><td> {{ profilingcontrastText }}</td><td> {{ profilingcontrastBrightness }}</td> </tr>
<tr><td>Clients</td><td> {{ clientscontrastText }}</td><td> {{ clientscontrastBrightness }}</td> </tr>
<tr><td>Modules</td><td> {{ modulescontrastText }}</td><td> {{ modulescontrastBrightness }}</td> </tr>
<tr><td>Filters</td><td> {{ filterscontrastText }}</td><td> {{ filterscontrastBrightness }}</td> </tr>
<tr><td>PineAP</td><td> {{ pineapcontrastText }}</td><td> {{ pineapcontrastBrightness }}</td> </tr>
<tr><td>Tracking</td><td> {{ trackingcontrastText }}</td><td> {{ trackingcontrastBrightness }}</td> </tr>
<tr><td>Logging</td><td> {{ loggingcontrastText }}</td><td> {{ loggingcontrastBrightness }}</td> </tr>
<tr><td>Reporting</td><td> {{ reportingcontrastText }}</td><td> {{ reportingcontrastBrightness }}</td> </tr>
<tr><td>Networking</td><td> {{ networkingcontrastText }}</td><td> {{ networkingcontrastBrightness }}</td> </tr>
<tr><td>Configuration</td><td> {{ configurationcontrastText }}</td><td> {{ configurationcontrastBrightness }}</td> </tr>
<tr><td>Advanced</td><td> {{ advancedcontrastText }}</td><td> {{ advancedcontrastBrightness }}</td> </tr>
<tr><td>Help</td><td> {{ helpcontrastText }}</td><td> {{ helpcontrastBrightness }}</td> </tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="panel-group" id="accordion">
<div class="col-md-9">
<!-- Library panel -->
<div class="panel panel-default">
<div class="panel-heading">
<h5 class="panel-title"><a href="javascript:;" data-toggle="collapse" data-parent="#accordion"
data-target="#collapseLibrary" class="text-muted">Themes</a></h5>
</div>
<div id="collapseLibrary" class="panel-collapse collapse in">
<div class="panel-body">
<div class="input-group" ng-show="library">
<input type="text" class="form-control" placeholder="Theme Name" name="themeName"
ng-model="newThemeName">
<span class="input-group-btn">
<button ng-disabled="newThemeName == ''" class="btn btn-default" type="button" ng-click="createNewTheme()">Create New Theme</button>
</span>
</div>
<hr ng-show="library"/>
<div ng-show="themes.length > 0 && library == true">
<div class="table-responsive">
<table class="table table-striped" align="center">
<thead>
<th>Theme Name</th>
<th>Location</th>
<th>Activate</th>
<th>Delete</th>
</thead>
<tbody>
<tr ng-repeat="theme in themes">
<td><a href="javascript:;" ng-click="getThemeFields(theme)">
<b>{{ theme.title.substring(0, theme.title.length - 4); }}</b></a></td>
<td class="text-muted"><i>{{ theme.storage }}</i></td>
<td><a href="javascript:;"
ng-click="activateTheme(theme)">Activate</a>
</td>
<td><a href="javascript:;" data-toggle="modal"
ng-click="deleteTheme(theme)"
> Delete</a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div ng-hide="themes.length > 0 || library == false">
<p class="text-muted text-center">
<i>No Themes in Library to Display.</i>
</p>
</div>
<div ng-show="library == false">
<button type="submit" class="btn btn-default btn-sm" ng-click="library = true">Back To
Library
</button>
<hr />
<h3>Theme Editor | {{ workshopTheme.themeName }}</h3>
<div class="table-responsive">
<table class="table table-striped" align="center">
<thead>
<th>CSS Editor</th>
</thead>
<tbody>
<textarea class="form-control" rows="30" ng-model="workshopTheme.code"> </textarea>
<div class="modal-footer">
<div style="padding-right:5em" >
<button type="button" ng-click="saveThemeCode(workshopTheme)" class="btn btn-success pull-left"
data-dismiss="modal">Save
</button>
</div>
<div style="padding-left:5em" >
<button type="button" ng-click="saveThemeCode(workshopTheme); activateTheme(workshopTheme)" class="btn btn-success pull-left"
data-dismiss="modal" >Save and Activate
</button>
</div>
<button type="button" class="btn btn-default pull-right" data-dismiss="modal"
ng-click="library = true">Cancel
</button>
</div>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h5 class="panel-title"><a href="javascript:;" data-toggle="collapse" data-parent="#accordion"
data-target="#collapseImages" class="text-muted">Icons</a></h5>
</div>
<div id="collapseImages" class="panel-collapse collapse">
<div class="panel-body">
<hr style="height:1px;border:none;color:#333;background-color:#333;">
<h3>Generic</h3>
<hr style="height:1px;border:none;color:#333;background-color:#333;">
<table class="table table-striped" align="center">
<thead>
<th>Type</th>
<th>Image</th>
<th>Light Theme</th>
<th>Dark Theme</th>
</thead>
<tbody>
<tr>
<td>
<b>Throbber</b>
</td>
<td>
<img src="/img/throbber.gif"/>
</td>
<td>
<div class="btn-switch"
ng-hide="throbbercontrast == false"
ng-style="switchOff"
ng-model="throbbercontrast"
ng-click="changeThrobber()">
</div>
</td>
<td>
<div class="btn-switch"
ng-hide="throbbercontrast == true"
ng-style="switchOn"
ng-model="throbbercontrast"
ng-click="changeThrobber()">
</div>
</td>
</tr>
<tr>
<td>
<b>Logo</b>
</td>
<td>
<img src="/img/logo.png" width="30" height="30" />
</td>
<td>
<div class="btn-switch"
ng-hide="logocontrast == false"
ng-style="switchOff"
ng-model="logocontrast"
ng-click="changeLogo()">
</div>
</td>
<td>
<div class="btn-switch"
ng-hide="logocontrast == true"
ng-style="switchOn"
ng-model="logocontrast"
ng-click="changeLogo()">
</div>
</td>
</tr>
<tr>
<td>
<b>Icon</b>
</td>
<td>
<img src="/img/favicon.ico"/>
</td>
<td>
<div class="btn-switch"
ng-hide="faviconcontrast == false"
ng-style="switchOff"
ng-model="faviconcontrast"
ng-click="changeFavicon()">
</div>
</td>
<td>
<div class="btn-switch"
ng-hide="faviconcontrast == true"
ng-style="switchOn"
ng-model="faviconcontrast"
ng-click="changeFavicon()">
</div>
</td>
</tr>
</tbody>
</table>
<hr style="height:1px;border:none;color:#333;background-color:#333;">
<h3>Modules</h3>
<hr style="height:1px;border:none;color:#333;background-color:#333;">
<table class="table table-striped" align="center">
<thead>
<th>Type</th>
<th>Image</th>
<th>Color</th>
<th>Brightness</th>
<th></th>
</thead>
<tbody>
<tr>
<td>
<b>ALL</b>
</td>
<td>
</td>
<td>
<select ng-model="allcontrastText"
ng-style="selectOptions"
ng-options="x for x in colors"
ng-value="{{ allcontrastText }}"
>
</select>
</td>
<td>
<select ng-model="allcontrastBrightness"
ng-style="selectOptions"
ng-options="x for x in brightness"
ng-value="{{ allcontrastBrightness }}"
>
</select>
</td>
<td>
<button class="btn btn-default"
type="button"
ng-click="changeAllIcons()">
Apply
</button>
</td>
</tr>
<tr>
<td>
<b>Dashboard</b>
</td>
<td>
<img src="/modules/Dashboard/module_icon.svg" width="30" height="30" />
</td>
<td>
<select ng-model="dashboardcontrastText"
ng-style="selectOptions"
ng-options="x for x in colors"
ng-value="{{ dashboardcontrastText }}"
>
</select>
</td>
<td>
<select ng-model="dashboardcontrastBrightness"
ng-style="selectOptions"
ng-options="x for x in brightness"
ng-value="{{ dashboardcontrastBrightness }}"
>
</select>
</td>
<td>
<button class="btn btn-default"
type="button"
ng-click="changeDashboard()">
Apply
</button>
</td>
</tr>
<tr>
<td><b>Recon</b></td>
<td><img src="/modules/Recon/module_icon.svg" width="30" height="30" /></td>
<td><select ng-model="reconcontrastText"
ng-style="selectOptions"
ng-options="x for x in colors"
ng-value="{{ reconcontrastText }}"
>
</select>
</td>
<td><select ng-model="reconcontrastBrightness"
ng-style="selectOptions"
ng-options="x for x in brightness"
ng-value="{{ reconcontrastBrightness }}"
>
</select>
</td>
<td><button class="btn btn-default"
type="button"
ng-click="changeRecon()">
Apply
</button>
</td>
</tr>
<td><b>Profiling</b></td>
<td><img src="/modules/Profiling/module_icon.svg" width="30" height="30" /></td>
<td><select ng-model="profilingcontrastText"
ng-style="selectOptions"
ng-options="x for x in colors"
ng-value="{{ profilingcontrastText }}"
>
</select>
</td>
<td><select ng-model="profilingcontrastBrightness"
ng-style="selectOptions"
ng-options="x for x in brightness"
ng-value="{{ profilingcontrastBrightness }}"
>
</select>
</td>
<td><button class="btn btn-default"
type="button"
ng-click="changeProfiling()">
Apply
</button>
</td>
</tr>
<td><b>Clients</b></td>
<td><img src="/modules/Clients/module_icon.svg" width="30" height="30" /></td>
<td><select ng-model="clientscontrastText"
ng-style="selectOptions"
ng-options="x for x in colors"
ng-value="{{ clientscontrastText }}"
>
</select>
</td>
<td><select ng-model="clientscontrastBrightness"
ng-style="selectOptions"
ng-options="x for x in brightness"
ng-value="{{ clientscontrastBrightness }}"
>
</select>
</td>
<td><button class="btn btn-default"
type="button"
ng-click="changeClients()">
Apply
</button>
</td>
</tr>
<tr>
<td><b>Modules</b></td>
<td><img src="/modules/ModuleManager/module_icon.svg" width="30" height="30" /></td>
<td><select ng-model="modulescontrastText"
ng-style="selectOptions"
ng-options="x for x in colors"
ng-value="{{ modulescontrastText }}"
>
</select>
</td>
<td><select ng-model="modulescontrastBrightness"
ng-style="selectOptions"
ng-options="x for x in brightness"
ng-value="{{ modulescontrastBrightness }}"
>
</select>
</td>
<td><button class="btn btn-default"
type="button"
ng-click="changeModules()">
Apply
</button>
</td>
</tr>
<tr>
<td><b>Filters</b></td>
<td><img src="/modules/Filters/module_icon.svg" width="30" height="30" /></td>
<td><select ng-model="filterscontrastText"
ng-style="selectOptions"
ng-options="x for x in colors"
ng-value="{{ filterscontrastText }}">
</select>
</td>
<td><select ng-model="filterscontrastBrightness"
ng-style="selectOptions"
ng-options="x for x in brightness"
ng-value="{{ filterscontrastBrightness }}">
</select>
</td>
<td><button class="btn btn-default"
type="button"
ng-click="changeFilters()">
Apply
</button>
</td>
</tr>
<tr>
<td><b>PineAP</b></td>
<td><img src="/modules/PineAP/module_icon.svg" width="30" height="30" /></td>
<td><select ng-model="pineapcontrastText"
ng-style="selectOptions"
ng-options="x for x in colors"
ng-value="{{ pineapcontrastText }}">
</select>
</td>
<td><select ng-model="pineapcontrastBrightness"
ng-style="selectOptions"
ng-options="x for x in brightness"
ng-value="{{ pineapcontrastBrightness }}">
</select>
</td>
<td><button class="btn btn-default"
type="button"
ng-click="changePineap()">Apply
</button>
</td>
</tr>
<tr>
<td><b>Tracking</b></td>
<td><img src="/modules/Tracking/module_icon.svg" width="30" height="30" /></td>
<td><select ng-model="trackingcontrastText"
ng-style="selectOptions"
ng-options="x for x in colors"
ng-value="{{ trackingcontrastText }}">
</select>
</td>
<td><select ng-model="trackingcontrastBrightness"
ng-style="selectOptions"
ng-options="x for x in brightness"
ng-value="{{ trackingcontrastBrightness }}">
</select>
</td>
<td><button class="btn btn-default"
type="button"
ng-click="changeTracking()">Apply</button>
</td>
</tr>
<tr>
<td><b>Logging</b></td>
<td><img src="/modules/Logging/module_icon.svg" width="30" height="30" /></td>
<td><select ng-model="loggingcontrastText"
ng-style="selectOptions"
ng-options="x for x in colors"
ng-value="{{ loggingcontrastText }}">
</select>
</td>
<td><select ng-model="loggingcontrastBrightness"
ng-style="selectOptions"
ng-options="x for x in brightness"
ng-value="{{ loggingcontrastBrightness }}">
</select>
</td>
<td><button class="btn btn-default"
type="button"
ng-click="changeLogging()">Apply
</button>
</td>
</tr>
<tr>
<td><b>Reporting</b></td>
<td><img src="/modules/Reporting/module_icon.svg" width="30" height="30" /></td>
<td><select ng-model="reportingcontrastText"
ng-style="selectOptions"
ng-options="x for x in colors"
ng-value="{{ reportingcontrastText }}">
</select>
</td>
<td><select ng-model="reportingcontrastBrightness"
ng-style="selectOptions"
ng-options="x for x in brightness"
ng-value="{{ reportingcontrastBrightness }}">
</select>
</td>
<td><button class="btn btn-default"
type="button"
ng-click="changeReporting()">Apply
</button>
</td>
</tr>
<tr>
<td><b>Networking</b></td>
<td><img src="/modules/Networking/module_icon.svg" width="30" height="30" /></td>
<td><select ng-model="networkingcontrastText"
ng-style="selectOptions"
ng-options="x for x in colors"
ng-value="{{ networkingcontrastText }}">
</select>
</td>
<td><select ng-model="networkingcontrastBrightness"
ng-style="selectOptions"
ng-options="x for x in brightness"
ng-value="{{ networkingcontrastBrightness }}">
</select>
</td>
<td><button class="btn btn-default"
type="button"
ng-click="changeNetworking()">Apply</button>
</td>
</tr>
<tr>
<td><b>Configuration</b></td>
<td><img src="/modules/Configuration/module_icon.svg" width="30" height="30" /></td>
<td><select ng-model="configurationcontrastText"
ng-style="selectOptions"
ng-options="x for x in colors"
ng-value="{{ configurationcontrastText }}">
</select>
</td>
<td><select ng-model="configurationcontrastBrightness"
ng-style="selectOptions"
ng-options="x for x in brightness"
ng-value="{{ configurationcontrastBrightness }}">
</select>
</td>
<td><button class="btn btn-default"
type="button"
ng-click="changeConfiguration()">Apply
</button>
</td>
</tr>
<tr>
<td><b>Advanced</b></td>
<td><img src="/modules/Advanced/module_icon.svg" width="30" height="30" /></td>
<td><select ng-model="advancedcontrastText"
ng-style="selectOptions"
ng-options="x for x in colors"
ng-value="{{ advancedcontrastText }}">
</select>
</td>
<td><select ng-model="advancedcontrastBrightness"
ng-style="selectOptions"
ng-options="x for x in brightness"
ng-value="{{ advancedcontrastBrightness }}">
</select>
</td>
<td><button class="btn btn-default"
type="button"
ng-click="changeAdvanced()">Apply
</button>
</td>
</tr>
<tr>
<td><b>Help</b></td>
<td><img src="/modules/Help/module_icon.svg" width="30" height="30" /></td>
<td><select ng-model="helpcontrastText"
ng-style="selectOptions"
ng-options="x for x in colors"
ng-value="{{ helpcontrastText }}">
</select>
</td>
<td><select ng-model="helpcontrastBrightness"
ng-style="selectOptions"
ng-options="x for x in brightness"
ng-value="{{ helpcontrastBrightness }}">
</select>
</td>
<td><button class="btn btn-default"
type="button"
ng-click="changeHelp()">Apply
</button>
</td>
</tr>
</tbody>
</table>
</ul>
</div>
</div>
</div>
<!-- Contribution Pannel -->
<div class="panel panel-default">
<div class="panel-heading">
<h5 class="panel-title"><a href="javascript:;" data-toggle="collapse" data-parent="#accordion"
data-target="#collapseContribution" class="text-muted">How To</a></h5>
</div>
<div id="collapseContribution" class="panel-collapse collapse">
<div class="panel-body">
<ul>
<li><b>Share your new theme</b></li>
<ul>
<li class="text-muted">Navigate to the wifi pineapple modules repository</li>
<li class="text-muted">Add your theme .css file in the css directory of this module</li>
<li class="text-muted">Create a new pull request</li>
<li class="text-muted">Update the version number</li>
<li class="text-muted">Update the module version in module.info</li>
</ul>
</ul>
<ul>
<li><b>Fix Images appearing broken/unable to be loaded</b></li>
<ul>
<li class="text-muted">Refresh the page</li>
<li class="text-muted">If the problem persists, see "Submit a Bug"</li>
</ul>
</ul>
<ul>
<li><b>Fix a messy looking page after a save/refresh</b></li>
<ul>
<li class="text-muted">Reselect 'Activate' beside your theme</li>
<li class="text-muted">Refresh the page</li>
</ul>
</ul>
<ul>
<li><b>Select the proper Image type</b></li>
<ul>
<li class="text-muted">Beside each Image type are two options "Light" and "Dark"</li>
<li class="text-muted">"Dark" images are intended for dark backgrounds </li>
<li class="text-muted">"Light" images are intended for light backgrounds </li>
</ul>
</ul>
<ul>
<li><b>Use a theme once without a module</b></li>
<ul>
<li class="text-muted">Navigate to https://github.com/kbeflo/pineapple-themes</li>
<li class="text-muted">Select a theme and run the install.sh script</li>
</ul>
</ul>
<ul>
<li><b>Submit a Bug</b></li>
<ul>
<li class="text-muted">Navigate to https://github.com/hak5/wifipineapple-modules/issues</li>
<li class="text-muted">Select the "New Issue" button </li>
<li class="text-muted">Tag @trashbo4t in your issue description </li>
</ul>
</ul>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h5 class="panel-title"><a href="javascript:;" data-toggle="collapse" data-parent="#accordion"
data-target="#collapseTips" class="text-muted">Community Tips</a></h5>
</div>
<div id="collapseTips" class="panel-collapse collapse">
<div class="panel-body">
<ul>
<li><b>Creating a New Theme</b></li>
<ul>
<li class="text-muted">
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.
</li>
<li class="text-muted">
A reccommended method to ensure larger coverage is to create a new .css file with the theme creator
then copy another .css files code.
</li>
<li class="text-muted">
Once you copied the code do a search and replace for the colors of your choosing!
</li>
</ul>
</ul>
<ul>
<li><b>Working with .css files in a browser</b></li>
<ul>
<li class="text-muted">Modyfing a .css file with active updating is possible in modern browsers.</li>
<li class="text-muted">Select 'ctrl'+'shift'+'I', then select the sourses tab.</li>
<li class="text-muted">Select the 'css' folder, then the main.css file</li>
<li class="text-muted">Modify the main.css file until you like the appearance of the page.</li>
<li class="text-muted">From there select all of the code ('ctrl'+'a') and paste it into your theme files CSS Editor box</li>
<li class="text-muted">Then select "Save" and "Activate"</li>
</ul>
</ul>
<ul>
<li><b>Dark Theme vs Light Theme Icons</b></li>
<ul>
<li class="text-muted">If your background is dark, its best to choose a "Dark Theme" icon.</li>
<li class="text-muted">Overlooked transparency pixels will be harder to see this way!</li>
</ul>
</ul>
</div>
</div>
</div>
<!-- Change Log Pannel -->
<div class="panel panel-default">
<div class="panel-heading">
<h5 class="panel-title"><a href="javascript:;" data-toggle="collapse" data-parent="#accordion"
data-target="#collapseChangelog" class="text-muted">Themes Change Log</a></h5>
</div>
<div id="collapseChangelog" class="panel-collapse collapse">
<div class="panel-body">
<ul>
<li><b>1.0</b></li>
<ul>
<li class="text-muted">Pending release of 1.0</li>
</ul>
</ul>
</div>
</div>
</div>
</div>
</div>
<div id="loadingModal" class="modal fade" role="dialog" >
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Loading</h4>
</div>
<div class="modal-body">
<div class="panel-title"><img src="/img/throbber.gif" ng-show="throbber"/>
<p><b>{{ working }}</b></p>
</div>
</div>
</div>
</div>
</div>

10
Themes/module.info Normal file
View File

@ -0,0 +1,10 @@
{
"author": "trashbo4t",
"description": "Create, download, and share custom themes.",
"devices": [
"nano",
"tetra"
],
"title": "Themes",
"version": "1.0"
}

52
Themes/module_icon.svg Normal file
View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="370.589px" height="370.589px" viewBox="0 0 370.589 370.589" style="enable-background:new 0 0 370.589 370.589;"
xml:space="preserve" fill="#808080">
<g>
<g>
<path d="M5.806,224.753c-7.741,7.741-7.741,20.291,0.002,28.034l10.114,10.114l24.18-9.46L30.99,277.97l50.811,50.812
l39.014-22.66l-19.858,41.815l16.843,16.844c7.743,7.743,20.293,7.743,28.034,0.002l91.812-91.812L97.618,132.941L5.806,224.753z"
/>
<path d="M358.83,11.841l-0.082-0.083c-9.66-9.66-21.981-13.478-35.631-11.043c-27.59,4.924-56.519,34.861-77.384,80.087
c-5.788,12.546-13.997,19.607-25.101,21.588c-21.311,3.803-48.293-11.741-64.344-27.792c-3.362-3.362-8.814-3.361-12.176,0.001
l-16.685,16.686l24.651,24.651c3.202,3.202,3.202,8.392,0,11.594c-3.202,3.202-8.393,3.202-11.594,0l-24.651-24.651l-6.178,6.178
c-3.364,3.364-3.364,8.815-0.002,12.178l139.703,139.703c3.361,3.361,8.812,3.36,12.177-0.002l34.458-34.458
c3.361-3.362,3.362-8.814,0.001-12.177c-16.051-16.051-31.595-43.034-27.792-64.344c1.98-11.103,9.042-19.312,21.588-25.1
c45.226-20.865,75.163-49.793,80.087-77.383C372.309,33.822,368.49,21.501,358.83,11.841z M343.056,48.746
c-5.857,5.857-15.354,5.857-21.213,0c-5.856-5.858-5.856-15.355,0-21.213c5.858-5.858,15.355-5.858,21.213,0
C348.914,33.391,348.914,42.888,343.056,48.746z"/>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -6,5 +6,5 @@
"tetra"
],
"title": "ettercap",
"version": "1.4"
"version": "1.5"
}

View File

@ -14,13 +14,13 @@ if [ "$1" = "install" ]; then
if [ "$2" = "internal" ]; then
opkg install ettercap
sed -i "/redir_command_on = \"iptables/ s/# *//" /etc/ettercap/etter.conf
sed -i "/redir_command_off = \"iptables/ s/# *//" /etc/ettercap/etter.conf
sed -i "/redir_command_on = \"iptables/ s/# *//" /etc/ettercap/etter.conf
sed -i "/redir_command_off = \"iptables/ s/# *//" /etc/ettercap/etter.conf
sed -i 's/^\(ec_uid = \).*/\10/' /etc/ettercap/etter.conf
sed -i 's/^\(ec_gid = \).*/\10/' /etc/ettercap/etter.conf
sed -i 's/^\(ec_uid = \).*/\10/' /etc/ettercap/etter.conf
sed -i 's/^\(ec_gid = \).*/\10/' /etc/ettercap/etter.conf
echo 1 > /proc/sys/net/ipv4/ip_forward
echo 1 > /proc/sys/net/ipv4/ip_forward
elif [ "$2" = "sd" ]; then
opkg install ettercap --dest sd
@ -34,6 +34,10 @@ if [ "$1" = "install" ]; then
echo 1 > /proc/sys/net/ipv4/ip_forward
fi
if [ ! -f /usr/lib/libpcap.so ] && [ -f /usr/lib/libpcap.so.1.3 ]; then
ln -s /usr/lib/libpcap.so /usr/lib/libpcap.so.1.3
fi
touch /etc/config/ettercap
echo "config ettercap 'module'" > /etc/config/ettercap

View File

@ -6,5 +6,5 @@
"tetra"
],
"title": "ngrep",
"version": "1.4"
"version": "1.5"
}

View File

@ -12,12 +12,16 @@ touch /tmp/ngrep.progress
if [ "$1" = "install" ]; then
if [ "$2" = "internal" ]; then
opkg update
opkg update
opkg install ngrep
elif [ "$2" = "sd" ]; then
opkg update
opkg install ngrep --dest sd
fi
if [ ! -f /usr/lib/libpcap.so ] && [ -f /usr/lib/libpcap.so.1.3 ]; then
ln -s /usr/lib/libpcap.so /usr/lib/libpcap.so.1.3
fi
touch /etc/config/ngrep
echo "config ngrep 'module'" > /etc/config/ngrep

View File

@ -7,10 +7,10 @@ class nmap extends Module
public function route()
{
switch ($this->request->action) {
case 'refreshInfo':
$this->refreshInfo();
break;
case 'refreshOutput':
case 'refreshInfo':
$this->refreshInfo();
break;
case 'refreshOutput':
$this->refreshOutput();
break;
case 'refreshStatus':
@ -37,33 +37,30 @@ class nmap extends Module
case 'deleteHistory':
$this->deleteHistory();
break;
case 'downloadHistory':
$this->downloadHistory();
break;
case 'downloadHistory':
$this->downloadHistory();
break;
}
}
protected function checkDependency($dependencyName)
{
return ((exec("which {$dependencyName}") == '' ? false : true) && ($this->uciGet("nmap.module.installed")));
}
protected function checkDependency($dependencyName)
{
return ((exec("which {$dependencyName}") == '' ? false : true) && ($this->uciGet("nmap.module.installed")));
}
protected function getDevice()
{
return trim(exec("cat /proc/cpuinfo | grep machine | awk -F: '{print $2}'"));
}
protected function getDevice()
{
return trim(exec("cat /proc/cpuinfo | grep machine | awk -F: '{print $2}'"));
}
protected function refreshInfo()
{
$moduleInfo = @json_decode(file_get_contents("/pineapple/modules/nmap/module.info"));
$this->response = array('title' => $moduleInfo->title, 'version' => $moduleInfo->version);
}
protected function refreshInfo()
{
$moduleInfo = @json_decode(file_get_contents("/pineapple/modules/nmap/module.info"));
$this->response = array('title' => $moduleInfo->title, 'version' => $moduleInfo->version);
}
private function handleDependencies()
{
if (!file_exists("/usr/lib/libpcap.so.1.3") && file_exists("/usr/lib/libpcap.so")) {
symlink("/usr/lib/libpcap.so", "/usr/lib/libpcap.so.1.3");
}
if(!$this->checkDependency("nmap"))
{
$this->execBackground("/pineapple/modules/nmap/scripts/dependencies.sh install ".$this->request->destination);
@ -88,17 +85,17 @@ class nmap extends Module
}
}
private function scanStatus()
{
if (!$this->checkRunning("nmap"))
{
$this->response = array('success' => true);
}
else
{
$this->response = array('success' => false);
}
}
private function scanStatus()
{
if (!$this->checkRunning("nmap"))
{
$this->response = array('success' => true);
}
else
{
$this->response = array('success' => false);
}
}
private function togglenmap()
{

View File

@ -6,5 +6,5 @@
"tetra"
],
"title": "nmap",
"version": "1.5"
"version": "1.6"
}

View File

@ -12,12 +12,16 @@ touch /tmp/nmap.progress
if [ "$1" = "install" ]; then
if [ "$2" = "internal" ]; then
opkg update
opkg update
opkg install nmap
elif [ "$2" = "sd" ]; then
opkg update
opkg install nmap --dest sd
fi
if [ ! -f /usr/lib/libpcap.so ] && [ -f /usr/lib/libpcap.so.1.3 ]; then
ln -s /usr/lib/libpcap.so /usr/lib/libpcap.so.1.3
fi
touch /etc/config/nmap
echo "config nmap 'module'" > /etc/config/nmap

View File

@ -6,5 +6,5 @@
"tetra"
],
"title": "p0f",
"version": "1.0"
"version": "1.1"
}

View File

@ -18,6 +18,10 @@ if [ "$1" = "install" ]; then
opkg update
opkg install p0f --dest sd
fi
if [ ! -f /usr/lib/libpcap.so ] && [ -f /usr/lib/libpcap.so.1.3 ]; then
ln -s /usr/lib/libpcap.so /usr/lib/libpcap.so.1.3
fi
touch /etc/config/p0f
echo "config p0f 'module'" > /etc/config/p0f

View File

@ -6,5 +6,5 @@
"tetra"
],
"title": "tcpdump",
"version": "1.4"
"version": "1.5"
}

View File

@ -12,12 +12,16 @@ touch /tmp/tcpdump.progress
if [ "$1" = "install" ]; then
if [ "$2" = "internal" ]; then
opkg update
opkg update
opkg install tcpdump
elif [ "$2" = "sd" ]; then
opkg update
opkg install tcpdump --dest sd
fi
if [ ! -f /usr/lib/libpcap.so ] && [ -f /usr/lib/libpcap.so.1.3 ]; then
ln -s /usr/lib/libpcap.so /usr/lib/libpcap.so.1.3
fi
touch /etc/config/tcpdump
echo "config tcpdump 'module'" > /etc/config/tcpdump

302
tor/api/module.php Executable file
View File

@ -0,0 +1,302 @@
<?php namespace pineapple;
class Tor extends Module
{
private $progressFile = '/tmp/tor.progress';
private $moduleConfigFile = '/etc/config/tor/config';
private $dependenciesScriptFile = '/pineapple/modules/tor/scripts/dependencies.sh';
// Error Constants
const INVALID_NAME = 'Invalid name';
const INVALID_PORT = 'Invalid port';
const INVALID_DESTINATION = 'Invalid destination';
// Display Constants
const DANGER = 'danger';
const WARNING = 'warning';
const SUCCESS = 'success';
public function route()
{
switch ($this->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();
}
}

193
tor/files/torrc Normal file
View File

@ -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 <nobody AT example dot com>
## You might also include your PGP or GPG fingerprint if you have one:
#ContactInfo 0xFFFFFFFF Random Person <nobody AT example dot com>
## 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

156
tor/js/module.js Executable file
View File

@ -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();
}]);

158
tor/module.html Executable file
View File

@ -0,0 +1,158 @@
<div class="row">
<div class="col-md-4">
<div class="panel panel-default" ng-controller="tor_DependenciesController">
<div class="panel-heading">
<h3 class="panel-title">Dependencies</h3>
</div>
<div class="panel-body">
<table style="width:100%">
<tr>
<td style="padding-bottom: .5em;" class="text-muted">Dependencies</td>
<td ng-hide="$root.status.installed" style="text-align:right;padding-bottom: .5em;">
<button type="button" style="width: 90px;" class="btn btn-{{installLabel}} btn-xs" data-toggle="modal" data-target="#dependenciesInstallModal" ng-disabled="processing">{{install}}</button>
</td>
<td ng-show="$root.status.installed" style="text-align:right;padding-bottom: .5em;">
<button type="button" style="width: 90px;" class="btn btn-{{installLabel}} btn-xs" data-toggle="modal" data-target="#dependenciesRemoveModal" ng-disabled="processing">{{install}}</button>
</td>
</tr>
<tr class="form-inline" ng-show="$root.status.installed">
<td style="padding-bottom: .5em;" class="text-muted">tor</td>
<td style="text-align:right;padding-bottom: .5em;">
<button type="button" style="width: 90px;" class="btn btn-{{statusLabel}} btn-xs" ng-disabled="starting" ng-click="toggletor()">{{status}}</button>
</td>
</tr>
</table>
</div>
<div class="modal fade" id="dependenciesInstallModal" tabindex="-1" role="dialog" aria-labelledby="dependenciesModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="dependenciesInstallModalLabel">Install dependencies</h4>
</div>
<div class="modal-body">
All required dependencies have to be installed first. This may take a few minutes.<br /><br />
Please wait, do not leave or refresh this page. Once the install is complete, this page will refresh automatically.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-info" ng-click="handleDependencies('internal')" data-dismiss="modal">Internal</button>
<button type="button" class="btn btn-info" ng-hide="device == 'tetra' || sdAvailable == false" ng-click="handleDependencies('sd')" data-dismiss="modal">SD Card</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="dependenciesRemoveModal" tabindex="-1" role="dialog" aria-labelledby="dependenciesModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="dependenciesRemoveModalLabel">Remove dependencies</h4>
</div>
<div class="modal-body">
All required dependencies will be removed. This may take a few minutes.<br /><br />
Please wait, do not leave or refresh this page. Once the remove is complete, this page will refresh automatically.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-info" ng-click="handleDependencies()" data-dismiss="modal">Confirm</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-8">
<div class="panel panel-default" ng-controller="tor_ConfigurationController" ng-show="$root.status.installed">
<div class="panel-heading">
<h3 class="panel-title">Hidden Services {{ hiddenServicesLoad }}</h3>
{{ config }}
</div>
<div class="panel-body">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addHiddenService" ng-disabled="processing">Add Service</button>
<button type="button" class="btn btn-info" ng-show="hiddenServices.length > 0" data-toggle="modal" data-target="#addServiceForward" ng-disabled="processing">Add Forward</button>
<div ng-repeat="service in hiddenServices">
<h4>
<span>{{ service.name }}</span>
<span ng-show="service.hostname">(<a href="http://{{service.hostname}}">{{service.hostname}}</a>)</span>
<span>
<button type="button" class="btn btn-danger btn-xs pull-right" data-toggle="modal" ng-click="removeHiddenService(service.name)" ng-disabled="processing">Remove Service</button>
</span>
</h4>
<div>
<table style="width: 100%">
<th>Port</th>
<th>Redirect to</th>
<th></th>
<tr ng-repeat="forward in service.forwards">
<td>{{ forward.port }}</td>
<td>{{ forward.redirect_to }}</td>
<td style="text-align:right;padding-bottom: .5em;"><button type="button" class="btn btn-danger btn-xs" data-toggle="modal" data-target="#removeServiceForward" ng-click="removeServiceForward(service.name, forward.port, forward.redirect_to);" ng-disabled="processing" aria-label="Delete Forward">X</button></td>
</tr>
</table>
</div>
</div>
</div>
<div class="modal fade" id="addHiddenService" tabindex="-1" role="dialog" aria-labelledby="addHiddenServiceLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="addHiddenServiceRemoveModalLabel">Add Hidden Service</h4>
</div>
<div class="modal-body">
<table style="width: 100%">
<tr>
<td style="padding-bottom: .5em;" class="text-muted">Directory:</td>
<td><input type="text" class="form-control" placeholder="hidden_service" ng-model="name"></td>
</tr>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-info" ng-click="addHiddenService()" data-dismiss="modal">Add Service</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="addServiceForward" tabindex="-1" role="dialog" aria-labelledby="addServiceForwardLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="addServiceForwardRemoveModalLabel">Add Forward to Service</h4>
</div>
<div class="modal-body">
<table style="width: 100%">
<tr>
<td>Service:</td>
<td style="padding-bottom: .5em">
<select ng-model="name">
<option ng-repeat="service in hiddenServices">{{service.name}}</option>
</select>
</td>
</tr>
<tr>
<td style="padding-bottom: .5em;" class="text-muted">Port:</td>
<td><input type="text" class="form-control" placeholder="80" ng-model="port"></td>
</tr>
<tr>
<td style="padding-bottom: .5em;" class="text-muted">Redirect to:</td>
<td><input type="text" class="form-control" placeholder="127.0.0.1:1471" ng-model="redirect_to"></td>
</tr>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-info" ng-click="addServiceForward()" data-dismiss="modal">Add Forward</button>
</div>
</div>
</div>
</div>
</div>
</div>

7
tor/module.info Executable file
View File

@ -0,0 +1,7 @@
{
"author":"catatonicprime",
"description":"Connect device to tor network, manage hidden services, etc.",
"devices": [ "nano", "tetra" ],
"title":"tor",
"version":"1.0"
}

29
tor/scripts/dependencies.sh Executable file
View File

@ -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

View File

@ -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);
}

View File

@ -485,7 +485,7 @@ registerController('wps_OptionsController', ['$api', '$scope', '$rootScope', fun
angular.forEach($scope.reaverAdvancedOptions, function(value, key) {
if(value.check != false)
return_value += value.check + " " + value.val;
return_value += value.check + " " + value.val + " ";
});
return return_value;
@ -507,7 +507,7 @@ registerController('wps_OptionsController', ['$api', '$scope', '$rootScope', fun
angular.forEach($scope.bullyAdvancedOptions, function(value, key) {
if(value.check != false)
return_value += value.check + " " + value.val;
return_value += value.check + " " + value.val + " ";
});
return return_value;

View File

@ -6,5 +6,5 @@
"tetra"
],
"title": "wps",
"version": "1.2"
}
"version": "1.5"
}

View File

@ -12,7 +12,7 @@ touch /tmp/wps.progress
if [ "$1" = "install" ]; then
if [ "$2" = "internal" ]; then
opkg update
opkg update
opkg install reaver
opkg install bully
@ -24,6 +24,10 @@ if [ "$1" = "install" ]; then
opkg install bully --dest sd
fi
if [ ! -f /usr/lib/libpcap.so ] && [ -f /usr/lib/libpcap.so.1.3 ]; then
ln -s /usr/lib/libpcap.so /usr/lib/libpcap.so.1.3
fi
touch /etc/config/wps
echo "config wps 'module'" > /etc/config/wps
@ -32,6 +36,8 @@ if [ "$1" = "install" ]; then
uci commit wps.module.installed
elif [ "$1" = "remove" ]; then
opkg remove reaver
opkg remove bully
rm -rf /etc/config/wps
fi

View File

@ -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}