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>&copy; 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