Frontend: Implement user logging in

This commit is contained in:
2023-09-08 22:05:21 +02:00
parent 0a6e4d1d15
commit 606b13470e
4 changed files with 341 additions and 117 deletions

View File

@@ -23,6 +23,7 @@ use DBI;
use lib ".";
use configuration;
use frontend_routes;
use feature qw(switch);
use strict;
@@ -58,29 +59,17 @@ use constant {
PPATH_GET_KEY => 1,
PPATH_GET_VALUE => 2
};
sub parsePath {
sub parsePathParameters {
my $aPath = $_[0];
my $pathLength = length($aPath);
my $state = PPATH_URL;
my $state = PPATH_GET_KEY;
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;
@@ -91,27 +80,96 @@ sub parsePath {
when(PPATH_GET_VALUE) {
if($char eq "&") {
$state = PPATH_GET_KEY;
$output{"parameters"}{$currentString} = $currentString2;
$output{$currentString} = $currentString2;
$currentString = "";
$currentString2 = "";
next;
}
$currentString2.=$char;
if($i==$pathLength-1) {
$output{"parameters"}{$currentString} = $currentString2;
$output{$currentString} = $currentString2;
}
}
}
}
return %output;
}
sub parsePath {
my $aPath = $_[0];
my $pathLength = length($aPath);
my $state = PPATH_URL;
my $currentString = "";
my %output;
my $index = 0;
while($index<$pathLength) {
my $char = substr($aPath, $index++, 1);
if($char eq "?") {
$output{"url"} = $currentString;
$state = PPATH_GET_KEY;
last;
}
$currentString.=$char;
}
$output{"url"} = $currentString;
if($state==PPATH_GET_KEY) {
$output{"parameters"} = { parsePathParameters(substr($aPath, $index, $pathLength-$index)) };
}
return %output;
}
use constant {
PCOOKIE_NAME => 0,
PCOOKIE_VALUE => 1
};
sub parseCookies {
my $aCookies = $_[0];
my $cookiesLength = length($aCookies);
my $state = PCOOKIE_NAME;
my $currentString = "";
my $currentString2 = "";
my %output;
foreach my $i (0..$cookiesLength-1) {
my $char = substr($aCookies, $i, 1);
given($state) {
when(PCOOKIE_NAME) {
if($char eq " ") {
next;
}
if($char eq "=") {
$state = PCOOKIE_VALUE;
next;
}
$currentString.=$char;
}
when(PCOOKIE_VALUE) {
if($char eq ";" || $i==$cookiesLength-1) {
if(length($currentString)>0 && length($currentString2)>0) {
if($i==$cookiesLength-1) {
$currentString2.=$char;
}
$output{$currentString} = $currentString2;
}
$currentString = "";
$currentString2 = "";
$state = PCOOKIE_NAME;
next;
}
$currentString2.=$char;
}
}
}
return %output;
}
use constant {
PHTTP_METHOD => 0,
PHTTP_PATH => 1,
PHTTP_VERSION => 2,
PHTTP_HEADER => 3,
PHTTP_VALUE => 4
PHTTP_VALUE => 4,
PHTTP_CONTENT => 5
};
sub parseHTTPRequest {
my $aRequest = $_[0];
@@ -164,14 +222,31 @@ sub parseHTTPRequest {
when(PHTTP_VALUE) {
if($char eq "\r") {
$index++;
$output{"headers"}{$currentString} = $currentString2;
$currentString = "";
$currentString2 = "";
$state = PHTTP_HEADER;
if($currentString eq "Cookie") {
$output{"cookies"} = { parseCookies($currentString2) };
}
else {
$output{"headers"}{$currentString} = $currentString2;
}
if($index+1<$requestLength && substr($aRequest, $index, 1) eq "\r") {
if(defined($output{"headers"}{"Content-Length"})) {
$index+=2;
$state = PHTTP_CONTENT;
$output{"content"} = "";
}
}
else {
$currentString = "";
$currentString2 = "";
$state = PHTTP_HEADER;
}
next;
}
$currentString2.=$char;
}
when(PHTTP_CONTENT) {
$output{"content"}.=$char;
}
}
}
return %output;
@@ -221,6 +296,16 @@ sub sendBadRequest {
$aClient->send($response);
}
sub redirect {
my $aClient = $_[0];
my $aLocation = $_[1];
my $response = getBaseResponse(301, "Moved Permanently");
$response.="Content-Length: 0\r\n";
$response.="Location: $aLocation\r\n";
$aClient->send($response);
}
use constant {
PREPROCESSOR_STATE_TEXT => 0,
PREPROCESSOR_STATE_VAR => 1,
@@ -313,101 +398,6 @@ sub sendTemplate {
$aClient->send($response);
}
sub handlePath {
my $aClient = $_[0];
my $aPath = $_[1];
my $aRequest = $_[2];
my $aConnection = $_[3];
given($aPath) {
when("/") {
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.="<tr><td><a href=\"view_logs?channel=$channelID\">$channelName</a></td><td>$serverName</td></tr>";
}
sendTemplate("templates/index.html", $aClient, {"publicChannels"=>$table});
return 1;
}
when("/view_logs") {
my $channelID = $aRequest->{"path"}{"parameters"}{"channel"};
if(!defined($channelID)) {
sendBadRequest($aClient, "view_logs 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.="<tr><td><a href=\"view_log?channel=$channelID&file=$entry\">$entry</a></td></tr>";
}
sendTemplate("templates/view_logs.html", $aClient, {"channel"=>$channelName, "server"=>$serverName, "logs"=>$table});
return 1;
}
when("/view_log") {
my $channelID = $aRequest->{"path"}{"parameters"}{"channel"};
if(!defined($channelID)) {
sendBadRequest($aClient, "view_log requires channel URL parameter");
return 1;
}
my $logFile = $aRequest->{"path"}{"parameters"}{"file"};
if(!defined($channelID)) {
sendBadRequest($aClient, "view_log requires file 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 $logFilePath = "logs/".$serverName."/".$channelName."/".$logFile;
my $result = open(my $file, "<", $logFilePath);
if(!$result) {
sendBadRequest($aClient, "No log file $logFile for channel $channelName at $serverName");
return 1;
}
my $content = readFullFile($file);
close($file);
my $response = getBaseResponse(200, "OK");
$response.="Content-Type: text/plain;charset=utf-8\r\n";
$response.="Content-Length: ".length($content)."\r\n\r\n";
$response.=$content;
$aClient->send($response);
}
}
return 0;
}
sub sendResponse {
my $aClient = $_[0];
my $aRequest = $_[1];
@@ -424,7 +414,7 @@ sub sendResponse {
if($path eq "/index.html" || $path eq "/index.htm") {
$path = "/";
}
if(handlePath($aClient, $path, $aRequest, $aConnection)) {
if(frontend_routes::handlePath($aClient, $path, $aRequest, $aConnection)) {
return;
}
my $filePath = "static".$path;
@@ -443,6 +433,15 @@ sub sendResponse {
$response.=$content;
$aClient->send($response);
}
when(HTTP_METHOD_POST) {
my $path = File::Spec->canonpath($aRequest->{"path"}{"url"});
if($path eq "/index.html" || $path eq "/index.htm") {
$path = "/";
}
if(!frontend_routes::handlePath($aClient, $path, $aRequest, $aConnection)) {
sendNotFound($aClient);
}
}
default {
sendNotImplemented($aClient);
}