diff --git a/opentrafficshaper/plugins/webserver/pages/index.pm b/opentrafficshaper/plugins/webserver/pages/index.pm new file mode 100644 index 0000000000000000000000000000000000000000..3d26e8a01a5d69916e7174e5b7c1e6780aa52629 --- /dev/null +++ b/opentrafficshaper/plugins/webserver/pages/index.pm @@ -0,0 +1,51 @@ +# OpenTrafficShaper webserver module: index page +# Copyright (C) 2007-2013, AllWorldIT +# +# 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 +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +package opentrafficshaper::plugins::webserver::pages::index; + +use strict; +use warnings; + + +# Exporter stuff +require Exporter; +our (@ISA,@EXPORT,@EXPORT_OK); +@ISA = qw(Exporter); +@EXPORT = qw( +); +@EXPORT_OK = qw( +); + + + +sub _catchall +{ + my ($globals,$module,$daction,$request) = @_; + # If we not passed default by the main app, just return + return if ($daction ne "default"); + + + # Build content + my $content = ""; + + $content .= "Hi there: $daction"; + + + return (200,$content); +} + + +1; diff --git a/opentrafficshaper/plugins/webserver/pages/users.pm b/opentrafficshaper/plugins/webserver/pages/users.pm new file mode 100644 index 0000000000000000000000000000000000000000..a0613f0c6a133635b41d3c2d8367066fff045f98 --- /dev/null +++ b/opentrafficshaper/plugins/webserver/pages/users.pm @@ -0,0 +1,96 @@ +# OpenTrafficShaper webserver module: users page +# Copyright (C) 2007-2013, AllWorldIT +# +# 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 +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +package opentrafficshaper::plugins::webserver::pages::users; + +use strict; +use warnings; + + +# Exporter stuff +require Exporter; +our (@ISA,@EXPORT,@EXPORT_OK); +@ISA = qw(Exporter); +@EXPORT = qw( +); +@EXPORT_OK = qw( +); + + + +sub default +{ + my ($globals,$module,$daction,$request) = @_; + + # If we not passed default by the main app, just return + return if ($daction ne "default"); + + + # Build content + my $content = ""; + + # Header + $content .=<<EOF; +<table class="table"> + <caption>User List</caption> + <thead> + <tr> + <th>#</th> + <th>User</th> + <th>IP</th> + <th>Group</th> + <th>Class</th> + <th>Limits</th> + </tr> + </thead> + <tbody> +EOF + # Body + foreach my $userid (keys %{$globals->{'users'}}) { + my $user = $globals->{'users'}->{$userid}; + + $content .=<<EOF; + <tr> + <td>X</td> + <td>$user->{'Username'}</td> + <td>$user->{'IP'}</td> + <td>$user->{'GroupName'}</td> + <td>$user->{'ClassName'}</td> + <td>$user->{'Limits'}</td> + </tr> +EOF + } + # No results + if (keys %{$globals->{'users'}} < 1) { + $content .=<<EOF; + <tr class="info"> + <td colspan="6"><p class="text-center">No Results</p></td> + </tr> +EOF + } + + # Footer + $content .=<<EOF; + </tbody> +</table> +EOF + + + return (200,$content); +} + + +1; diff --git a/opentrafficshaper/plugins/webserver/webserver.pm b/opentrafficshaper/plugins/webserver/webserver.pm index 530d91efac4169e70dc44bb7e896170306e66202..c96c341505b3e3ce834d18bb2ade664c0aefed28 100644 --- a/opentrafficshaper/plugins/webserver/webserver.pm +++ b/opentrafficshaper/plugins/webserver/webserver.pm @@ -21,8 +21,17 @@ package opentrafficshaper::plugins::webserver; use strict; use warnings; +use HTML::Entities; +use HTTP::Response; +use HTTP::Status qw(:constants :is status_message); +use POE qw(Component::Server::TCP Filter::HTTPD); -use POE; +use opentrafficshaper::logger; + +# Pages (this is used a little below) +use opentrafficshaper::plugins::webserver::pages::static; +use opentrafficshaper::plugins::webserver::pages::index; +use opentrafficshaper::plugins::webserver::pages::users; # Exporter stuff @@ -50,6 +59,20 @@ our $pluginInfo = { # Copy of system globals my $globals; +my $logger; +# This is the mapping of our pages +my $pages = { + 'index' => { + '_catchall' => \&opentrafficshaper::plugins::webserver::pages::index::_catchall, + }, + 'static' => { + '_catchall' => \&opentrafficshaper::plugins::webserver::pages::static::_catchall, + }, + 'users' => { + 'default' => \&opentrafficshaper::plugins::webserver::pages::users::default, + }, +}; + # Initialize plugin @@ -58,8 +81,8 @@ sub init $globals = shift; - print STDERR "HI HTERE!! \n"; - + # Setup our environment + $logger = $globals->{'logger'}; # Spawn a web server on port 8088 of all interfaces. POE::Component::Server::TCP->new( @@ -69,6 +92,8 @@ sub init # Function to handle HTTP requests (as we passing through a filter) ClientInput => \&handle_request ); + + $logger->log(LOG_NOTICE,"[WEBSERVER] OpenTrafficShaper Webserver Module v".VERSION." - Copyright (c) 2013, AllWorldIT") } @@ -77,42 +102,204 @@ sub handle_request my ($kernel, $heap, $request) = @_[KERNEL, HEAP, ARG0]; + # We going to init these as system so we know whats a parsing issue + my $module = "system"; + my $action = "parse"; + # This is our response + my $response; + # We may have a response from the filter indicating an error if ($request->isa("HTTP::Response")) { $heap->{client}->put($request); - $kernel->yield("shutdown"); - return; + goto END; } - my $response = HTTP::Response->new(200); - $response->push_header('Content-type', 'text/html'); - - my $content = - # Break the HTML tag for the wiki. - "<html><head><title>Your Request</title></head>" - ; - + # Split off the URL into a module and action + my (undef,$dmodule,$daction,@dparams) = split(/\//,$request->uri); + # If any is blank, set it to the default + $dmodule = "index" if (!defined($dmodule)); + $daction = "default" if (!defined($daction)); + # Sanitize + ($module = $dmodule) =~ s/[^A-Za-z0-9]//g; + ($action = $daction) =~ s/[^A-Za-z0-9]//g; + # If module name is sneaky? then just block it + if ($module ne $dmodule) { + $response = httpDisplayFault(HTTP_FORBIDDEN,"Method Not Allowed","The requested resource '".encode_entities($module)."' is not allowed."); + goto END; + } - $content .= "<table>"; - $content .= " <tr><td>User</td><td>IP</td></tr>"; - foreach my $user (keys %{$globals->{'users'}}) { - $content .= " <tr><td>".$user."</td><td>".$globals->{'users'}->{$user}."</td></tr>"; + # This is the function we going to call + # If we have a function name, try use it + if (defined($pages->{$module})) { + # If there is no specific action for this use the catchall + if (!defined($pages->{$module}->{$action}) && defined($pages->{$module}->{'_catchall'})) { + $action = "_catchall"; } - $content .= "</table>"; + # Check if it exists first + if (defined($pages->{$module}->{$action})) { + my ($res,$content,$extra) = $pages->{$module}->{$action}->($globals,$module,$daction,$request); + + # Module return undef if they don't want to handle the request + if (!defined($res)) { + $response = httpDisplayFault(HTTP_NOT_FOUND,"Resource Not found","The requested resource '".encode_entities($daction)."' cannot be found"); + } elsif (ref($res) eq "HTTP::Response") { + $response = $res; + # TODO: This is a bit dirty + } elsif ($res == HTTP_OK) { + $response = httpCreateResponse($content); + # TODO - redirect? + } else { + httpDisplayFault($res,$content,$extra); + } + } else { + $response = httpDisplayFault(HTTP_NOT_FOUND,"Method Not found","The requested method '".encode_entities($action)."' cannot be found in '".encode_entities($module)."'"); + } + } - $content .= "</body></html>"; + if (!defined($response)) { + $response = httpDisplayFault(HTTP_NOT_FOUND,"Resource Not found","The requested resource '".encode_entities($module)."' cannot be found"); + } - $response->content($content); - # Once the content has been built, send it back to the client - # and schedule a shutdown. +END: + $logger->log(LOG_INFO,"[WEBSERVER] Access: ".$response->code." [$module/$action] - ".$request->method." ".$request->uri." ".$request->protocol); $heap->{client}->put($response); $kernel->yield("shutdown"); } +# Display fault +sub httpDisplayFault +{ + my ($code,$msg,$description) = @_; + + + # Throw out message to client to authenticate first + my $headers = HTTP::Headers->new; + $headers->content_type("text/html"); + + my $resp = HTTP::Response->new( + $code,$msg, + $headers, + <<EOF); +<!DOCTYPE html> +<html> + <head> + <title>$code $msg</title> + </head> + + <body> + <h1>$msg</h1> + <p>$description</p> + </body> +</html> +EOF + return $resp; +} + + + +# Create a response object +sub httpCreateResponse +{ + my ($msg) = @_; + + + # Throw out message to client to authenticate first + my $headers = HTTP::Headers->new; + $headers->content_type("text/html"); + + my $resp = HTTP::Response->new( + HTTP_OK,"Ok", + $headers, + <<EOF); +<!DOCTYPE html> + <head> + <title>Bootstrap 101 Template</title> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <!-- Assets --> + <link href="/static/jquery-ui/css/ui-lightness/jquery-ui.min.css" rel="stylesheet" media="screen"> + <link href="/static/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen"> + + <style type="text/css"> + body { + padding-top: 60px; + padding-bottom: 40px; + } + .sidebar-nav { + padding: 9px 0; + } + \@media (max-width: 980px) { + /* Enable use of floated navbar text */ + .navbar-text.pull-right { + float: none; + padding-left: 5px; + padding-right: 5px; + } + } + </style> + + <!-- End Assets --> + <link href="/static/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet" media="screen"> + </head> + <body> + + <div class="navbar navbar-inverse navbar-fixed-top"> + <div class="navbar-inner"> + <div class="container-fluid"> + <button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse"> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + </button> + <a class="brand" href="#">OpenTrafficShaper</a> + <div class="nav-collapse collapse"> + <p class="navbar-text pull-right">Logged in as <a href="#" class="navbar-link">Username</a> </p> + <ul class="nav"> + <li class="active"><a href="#">Home</a></li> + <li><a href="/users">Users</a></li> + </ul> + </div><!--/.nav-collapse --> + </div> + </div> + </div> + + <div class="container-fluid"> + <div class="row-fluid"> + <div class="span1"> + <div class="well sidebar-nav"> + <ul class="nav nav-list"> + <li class="nav-header">Sidebar</li> + <li class="active"><a href="#">Link</a></li> + <li><a href="#">Link</a></li> + <li class="nav-header">Sidebar</li> + <li><a href="#">Link</a></li> + </ul> + </div><!--/.well --> + </div><!--/span--> + <div class="span10"> + $msg + </div><!--/span--> + </div><!--/row--> + <hr> + <footer> + <p>© AllWorldIT 2013</p> + </footer> + </div><!--/.fluid-container--> + + <!-- Javascript --> + <script src="/static/jquery/js/jquery.min.js"></script> + <script src="/static/jquery-ui/js/jquery-ui.min.js"></script> + <script src="/static/bootstrap/js/bootstrap.min.js"></script> + </body> +</html> +EOF + return $resp; +} + + 1; # vim: ts=4