diff --git a/frontend.pl b/frontend.pm similarity index 66% rename from frontend.pl rename to frontend.pm index 3e8b3b4..36a9b50 100644 --- a/frontend.pl +++ b/frontend.pm @@ -14,9 +14,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +package frontend; + use IO::Socket; use File::Spec; use Time::Piece; +use DBI; use lib "."; use configuration; @@ -50,6 +53,59 @@ sub stringToHTTPMethod { } } +use constant { + PPATH_URL => 0, + PPATH_GET_KEY => 1, + PPATH_GET_VALUE => 2 +}; +sub parsePath { + my $aPath = $_[0]; + + my $pathLength = length($aPath); + my $state = PPATH_URL; + my $currentString = ""; + my $currentString2 = ""; + my %output; + foreach my $i (0..$pathLength-1) { + my $char = substr($aPath, $i, 1); + given($state) { + when(PPATH_URL) { + if($char eq "?") { + $output{"url"} = $currentString; + $currentString = ""; + $state = PPATH_GET_KEY; + next; + } + $currentString.=$char; + if($i==$pathLength-1) { + $output{"url"} = $currentString; + } + } + when(PPATH_GET_KEY) { + if($char eq "=") { + $state = PPATH_GET_VALUE; + next; + } + $currentString.=$char; + } + when(PPATH_GET_VALUE) { + if($char eq "&") { + $state = PPATH_GET_KEY; + $output{"parameters"}{$currentString} = $currentString2; + $currentString = ""; + $currentString2 = ""; + next; + } + $currentString2.=$char; + if($i==$pathLength-1) { + $output{"parameters"}{$currentString} = $currentString2; + } + } + } + } + return %output; +} + use constant { PHTTP_METHOD => 0, PHTTP_PATH => 1, @@ -80,7 +136,7 @@ sub parseHTTPRequest { } when(PHTTP_PATH) { if($char eq " ") { - $output{"path"} = $currentString; + $output{"path"} = { parsePath($currentString) }; $currentString = ""; $state = PHTTP_VERSION; next; @@ -153,6 +209,18 @@ sub sendNotFound { $aClient->send($response); } +sub sendBadRequest { + my $aClient = $_[0]; + my $aMessage = $_[1]; + + my $content = "

400 Bad Request

irclogger_web
Error: $aMessage"; + my $response = getBaseResponse(400, "Bad Request"); + $response.="Content-Type: text/html, charset=utf-8\r\n"; + $response.="Content-Length: ".length($content)."\r\n\r\n"; + $response.=$content; + $aClient->send($response); +} + use constant { PREPROCESSOR_STATE_TEXT => 0, PREPROCESSOR_STATE_VAR => 1, @@ -208,7 +276,7 @@ sub preprocessHTML { if($nextChar eq "]") { $index++; if(open(my $file, "<", $currentString)) { - $output.=readFullFile($file); + $output.=preprocessHTML(readFullFile($file), $aVariables); close($file); } else { @@ -242,6 +310,7 @@ sub sendTemplate { my $response = getBaseResponse(200, "OK"); $response.="Content-Length: $length\r\n\r\n"; $response.=$content; + print("$response\n"); $aClient->send($response); } @@ -249,10 +318,54 @@ sub handlePath { my $aClient = $_[0]; my $aPath = $_[1]; my $aRequest = $_[2]; + my $aConnection = $_[3]; given($aPath) { when("/") { - sendTemplate("templates/index.html", $aClient, {"var"=>"value"}); + my $query = $aConnection->prepare(qq(select channels.id, channels.name, servers.name from channels inner join servers on channels.server_id=servers.id where channels.public=1;)); + $query->execute(); + my $table = ""; + while(my @row = $query->fetchrow_array()) { + my $channelID = $row[0]; + my $channelName = $row[1]; + my $serverName = $row[2]; + + $table.="$channelName$serverName"; + } + sendTemplate("templates/index.html", $aClient, {"publicChannels"=>$table}); + return 1; + } + when("/view_logs") { + my $channelID = $aRequest->{"path"}{"parameters"}{"channel"}; + if(!defined($channelID)) { + sendBadRequest($aClient, "view_log requires channel URL parameter"); + return 1; + } + + my $query = $aConnection->prepare(qq(select channels.name, servers.name from channels inner join servers on channels.server_id=servers.id where channels.id=?;)); + $query->execute($channelID); + my @row = $query->fetchrow_array(); + if(scalar(@row)==0) { + sendBadRequest($aClient, "Unknown channel with ID $channelID"); + return 1; + } + my $channelName = $row[0]; + my $serverName = $row[1]; + my $logsPath = "logs/".$serverName."/".$channelName; + + my $result = opendir(my $folder, $logsPath); + if(!$result) { + sendBadRequest($aClient, "Channel $channelName on $serverName doesn't have any logs"); + return 1; + } + my @entries = grep(!/^\.\.?$/, readdir($folder)); + + my $table = ""; + foreach my $entry (@entries) { + $table.="$entry"; + } + + sendTemplate("templates/view_logs.html", $aClient, {"channel"=>$channelName, "server"=>$serverName, "logs"=>$table}); return 1; } } @@ -262,6 +375,7 @@ sub handlePath { sub sendResponse { my $aClient = $_[0]; my $aRequest = $_[1]; + my $aConnection = $_[2]; if($aRequest->{"version"} ne "HTTP/1.1") { sendNotImplemented($aClient); @@ -270,11 +384,11 @@ sub sendResponse { given($aRequest->{"method"}) { when(HTTP_METHOD_GET) { - my $path = File::Spec->canonpath($aRequest->{"path"}); + my $path = File::Spec->canonpath($aRequest->{"path"}{"url"}); if($path eq "/index.html" || $path eq "/index.htm") { $path = "/"; } - if(handlePath($aClient, $path, $aRequest)) { + if(handlePath($aClient, $path, $aRequest, $aConnection)) { return; } my $filePath = "static".$path; @@ -300,6 +414,7 @@ sub sendResponse { } sub httpServerWorker { + my $db = DBI->connect("DBI:SQLite:dbname=$configuration::database", "", "", {RaiseError=>1}); my $server = new IO::Socket::INET(LocalHost=>"localhost", LocalPort=>$configuration::httpServerPort, Proto=>"tcp", Listen=>1, Reuse=>1); if(!$server) { print("Failed to open HTTP server on port $configuration::httpServerPort\n"); @@ -314,9 +429,9 @@ sub httpServerWorker { next; } my %request = parseHTTPRequest($buffer); - sendResponse($client, \%request); + sendResponse($client, \%request, $db); close($client); } } -httpServerWorker(); +1; diff --git a/logger.pl b/logger.pl index d552db7..a36b5ea 100644 --- a/logger.pl +++ b/logger.pl @@ -18,7 +18,6 @@ use IO::Socket; use List::Util; use Time::Piece; use File::Path; -use DBI; use threads; use lib "."; diff --git a/main.pl b/main.pl new file mode 100644 index 0000000..4f618a9 --- /dev/null +++ b/main.pl @@ -0,0 +1,26 @@ +# irclogger_web +# Copyright (C) 2023 mrkubax10 + +# 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 . + +use lib "."; +use frontend; + +use threads; + +use strict; +use warnings; + +my $frontendThread = threads->create("frontend::httpServerWorker"); +$frontendThread->join(); diff --git a/templates/index.html b/templates/index.html index b12ef73..54b535b 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1 +1,13 @@ -

{{var}}

+ + + + irclogger_web + + +

Channel list

+ + + {{publicChannels}} +
ChannelNetwork
+ + diff --git a/templates/view_logs.html b/templates/view_logs.html new file mode 100644 index 0000000..b2556a9 --- /dev/null +++ b/templates/view_logs.html @@ -0,0 +1,12 @@ + + + + Logs for {{channel}} at {{server}} + + + + + {{logs}} +
File
+ +