2023-09-05 19:46:15 +02:00
|
|
|
# irclogger_web
|
|
|
|
# Copyright (C) 2023 mrkubax10 <mrkubax10@onet.pl>
|
|
|
|
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
2023-09-11 18:55:32 +02:00
|
|
|
# it under the terms of the GNU Affero General Public License as
|
|
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
|
|
# License, or (at your option) any later version.
|
2023-09-05 19:46:15 +02:00
|
|
|
|
|
|
|
# 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
|
2023-09-11 18:55:32 +02:00
|
|
|
# GNU Affero General Public License for more details.
|
2023-09-05 19:46:15 +02:00
|
|
|
|
2023-09-11 18:55:32 +02:00
|
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
2023-09-05 19:46:15 +02:00
|
|
|
|
2023-09-06 21:51:11 +02:00
|
|
|
package frontend;
|
|
|
|
|
2023-09-05 19:46:15 +02:00
|
|
|
use IO::Socket;
|
2023-09-11 10:08:12 +02:00
|
|
|
use Digest::SHA;
|
2023-09-05 19:46:15 +02:00
|
|
|
use File::Spec;
|
|
|
|
use Time::Piece;
|
2023-09-06 21:51:11 +02:00
|
|
|
use DBI;
|
2023-09-05 19:46:15 +02:00
|
|
|
|
|
|
|
use lib ".";
|
|
|
|
use configuration;
|
2023-09-08 22:05:21 +02:00
|
|
|
use frontend_routes;
|
2023-09-05 19:46:15 +02:00
|
|
|
|
|
|
|
use feature qw(switch);
|
|
|
|
use strict;
|
|
|
|
use warnings;
|
|
|
|
|
2023-09-06 20:09:20 +02:00
|
|
|
sub readFullFile {
|
|
|
|
my $aFile = $_[0];
|
|
|
|
|
|
|
|
my $content = "";
|
|
|
|
while(!eof($aFile)) {
|
|
|
|
$content.=readline($aFile);
|
|
|
|
}
|
|
|
|
return $content;
|
|
|
|
}
|
|
|
|
|
2023-09-05 19:46:15 +02:00
|
|
|
use constant {
|
|
|
|
HTTP_METHOD_UNKNOWN => 0,
|
|
|
|
HTTP_METHOD_GET => 1,
|
|
|
|
HTTP_METHOD_POST => 2
|
|
|
|
};
|
|
|
|
sub stringToHTTPMethod {
|
|
|
|
my $aMethod = $_[0];
|
|
|
|
|
|
|
|
given($aMethod) {
|
|
|
|
when("GET") { return HTTP_METHOD_GET; }
|
|
|
|
when("POST") { return HTTP_METHOD_POST; }
|
|
|
|
default { return HTTP_METHOD_UNKNOWN; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-06 21:51:11 +02:00
|
|
|
use constant {
|
|
|
|
PPATH_URL => 0,
|
|
|
|
PPATH_GET_KEY => 1,
|
|
|
|
PPATH_GET_VALUE => 2
|
|
|
|
};
|
2023-09-08 22:05:21 +02:00
|
|
|
sub parsePathParameters {
|
2023-09-06 21:51:11 +02:00
|
|
|
my $aPath = $_[0];
|
|
|
|
|
|
|
|
my $pathLength = length($aPath);
|
2023-09-08 22:05:21 +02:00
|
|
|
my $state = PPATH_GET_KEY;
|
2023-09-06 21:51:11 +02:00
|
|
|
my $currentString = "";
|
|
|
|
my $currentString2 = "";
|
|
|
|
my %output;
|
|
|
|
foreach my $i (0..$pathLength-1) {
|
|
|
|
my $char = substr($aPath, $i, 1);
|
|
|
|
given($state) {
|
|
|
|
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;
|
2023-09-08 22:05:21 +02:00
|
|
|
$output{$currentString} = $currentString2;
|
2023-09-06 21:51:11 +02:00
|
|
|
$currentString = "";
|
|
|
|
$currentString2 = "";
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
$currentString2.=$char;
|
|
|
|
if($i==$pathLength-1) {
|
2023-09-08 22:05:21 +02:00
|
|
|
$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;
|
2023-09-06 21:51:11 +02:00
|
|
|
}
|
2023-09-08 22:05:21 +02:00
|
|
|
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;
|
2023-09-06 21:51:11 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return %output;
|
|
|
|
}
|
|
|
|
|
2023-09-05 19:46:15 +02:00
|
|
|
use constant {
|
|
|
|
PHTTP_METHOD => 0,
|
|
|
|
PHTTP_PATH => 1,
|
|
|
|
PHTTP_VERSION => 2,
|
|
|
|
PHTTP_HEADER => 3,
|
2023-09-08 22:05:21 +02:00
|
|
|
PHTTP_VALUE => 4,
|
|
|
|
PHTTP_CONTENT => 5
|
2023-09-05 19:46:15 +02:00
|
|
|
};
|
|
|
|
sub parseHTTPRequest {
|
|
|
|
my $aRequest = $_[0];
|
|
|
|
|
|
|
|
my $requestLength = length($aRequest);
|
|
|
|
my $index = 0;
|
|
|
|
my $state = PHTTP_METHOD;
|
|
|
|
my $currentString = "";
|
|
|
|
my $currentString2 = "";
|
|
|
|
my %output;
|
|
|
|
while($index<$requestLength) {
|
|
|
|
my $char = substr($aRequest, $index++, 1);
|
|
|
|
given($state) {
|
|
|
|
when(PHTTP_METHOD) {
|
|
|
|
if($char eq " ") {
|
|
|
|
$output{"method"} = stringToHTTPMethod($currentString);
|
|
|
|
$currentString = "";
|
|
|
|
$state = PHTTP_PATH;
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
$currentString.=$char;
|
|
|
|
}
|
|
|
|
when(PHTTP_PATH) {
|
|
|
|
if($char eq " ") {
|
2023-09-06 21:51:11 +02:00
|
|
|
$output{"path"} = { parsePath($currentString) };
|
2023-09-05 19:46:15 +02:00
|
|
|
$currentString = "";
|
|
|
|
$state = PHTTP_VERSION;
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
$currentString.=$char;
|
|
|
|
}
|
|
|
|
when(PHTTP_VERSION) {
|
|
|
|
if($char eq "\r") {
|
|
|
|
$index++;
|
|
|
|
$output{"version"} = $currentString;
|
|
|
|
$currentString = "";
|
|
|
|
$state = PHTTP_HEADER;
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
$currentString.=$char;
|
|
|
|
}
|
|
|
|
when(PHTTP_HEADER) {
|
|
|
|
if($char eq ":") {
|
|
|
|
while(substr($aRequest, ++$index, 1) eq " ") {}
|
|
|
|
$state = PHTTP_VALUE;
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
$currentString.=$char;
|
|
|
|
}
|
|
|
|
when(PHTTP_VALUE) {
|
|
|
|
if($char eq "\r") {
|
|
|
|
$index++;
|
2023-09-08 22:05:21 +02:00
|
|
|
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;
|
|
|
|
}
|
2023-09-05 19:46:15 +02:00
|
|
|
next;
|
|
|
|
}
|
|
|
|
$currentString2.=$char;
|
|
|
|
}
|
2023-09-08 22:05:21 +02:00
|
|
|
when(PHTTP_CONTENT) {
|
|
|
|
$output{"content"}.=$char;
|
|
|
|
}
|
2023-09-05 19:46:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return %output;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub getBaseResponse {
|
|
|
|
my $aStatusCode = $_[0];
|
|
|
|
my $aStatusText = $_[1];
|
|
|
|
|
|
|
|
my $response = "HTTP/1.1 $aStatusCode $aStatusText\r\n";
|
|
|
|
$response.="Date: ".localtime->strftime("%a, %d %b %Y %H:%M:%S %Z")."\r\n";
|
|
|
|
$response.="Server: irclogger_web\r\n";
|
|
|
|
return $response;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub sendNotImplemented {
|
|
|
|
my $aClient = $_[0];
|
|
|
|
|
|
|
|
my $content = "<h1>501 Not Implemented</h1><h6>irclogger_web</h6>";
|
|
|
|
my $response = getBaseResponse(501, "Not Implemented");
|
|
|
|
$response.="Content-Type: text/html, charset=utf-8\r\n";
|
|
|
|
$response.="Content-Length: ".length($content)."\r\n\r\n";
|
|
|
|
$response.=$content;
|
|
|
|
$aClient->send($response);
|
|
|
|
}
|
|
|
|
|
2023-09-06 20:09:20 +02:00
|
|
|
sub sendNotFound {
|
|
|
|
my $aClient = $_[0];
|
|
|
|
|
|
|
|
my $content = "<h1>404 Not Found</h1><h6>irclogger_web</h6>";
|
|
|
|
my $response = getBaseResponse(404, "Not Found");
|
|
|
|
$response.="Content-Type: text/html, charset=utf-8\r\n";
|
|
|
|
$response.="Content-Length: ".length($content)."\r\n\r\n";
|
|
|
|
$response.=$content;
|
|
|
|
$aClient->send($response);
|
|
|
|
}
|
|
|
|
|
2023-09-06 21:51:11 +02:00
|
|
|
sub sendBadRequest {
|
|
|
|
my $aClient = $_[0];
|
|
|
|
my $aMessage = $_[1];
|
|
|
|
|
|
|
|
my $content = "<h1>400 Bad Request</h1><h6>irclogger_web</h6>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);
|
|
|
|
}
|
|
|
|
|
2023-09-12 20:21:15 +02:00
|
|
|
sub sendUnauthorized {
|
|
|
|
my $aClient = $_[0];
|
|
|
|
my $aMessage = $_[1];
|
|
|
|
|
|
|
|
my $content = "<h1>401 Unauthorized</h1><h6>irclogger_web</h6>Error: $aMessage";
|
|
|
|
my $response = getBaseResponse(401, "Unauthorized");
|
|
|
|
$response.="Content-Type: text/html, charset=utf-8\r\n";
|
|
|
|
$response.="Content-Length: ".length($content)."\r\n\r\n";
|
|
|
|
$response.=$content;
|
|
|
|
$aClient->send($response);
|
|
|
|
}
|
|
|
|
|
2023-09-12 10:19:46 +02:00
|
|
|
sub sendForbidden {
|
|
|
|
my $aClient = $_[0];
|
|
|
|
my $aMessage = $_[1];
|
|
|
|
|
|
|
|
my $content = "<h1>403 Forbidden</h1><h6>irclogger_web</h6>Error: $aMessage";
|
|
|
|
my $response = getBaseResponse(403, "Forbidden");
|
|
|
|
$response.="Content-Type: text/html, charset=utf-8\r\n";
|
|
|
|
$response.="Content-Length: ".length($content)."\r\n\r\n";
|
|
|
|
$response.=$content;
|
|
|
|
$aClient->send($response);
|
|
|
|
}
|
|
|
|
|
|
|
|
sub sendConflict {
|
|
|
|
my $aClient = $_[0];
|
|
|
|
my $aMessage = $_[1];
|
|
|
|
|
|
|
|
my $content = "<h1>409 Conflict</h1><h6>irclogger_web</h6>Error: $aMessage";
|
|
|
|
my $response = getBaseResponse(409, "Conflict");
|
|
|
|
$response.="Content-Type: text/html, charset=utf-8\r\n";
|
|
|
|
$response.="Content-Length: ".length($content)."\r\n\r\n";
|
|
|
|
$response.=$content;
|
|
|
|
$aClient->send($response);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-09-08 22:05:21 +02:00
|
|
|
sub redirect {
|
|
|
|
my $aClient = $_[0];
|
|
|
|
my $aLocation = $_[1];
|
|
|
|
|
2023-09-12 11:38:11 +02:00
|
|
|
my $response = getBaseResponse(307, "Temporary Redirect");
|
2023-09-08 22:05:21 +02:00
|
|
|
$response.="Content-Length: 0\r\n";
|
|
|
|
$response.="Location: $aLocation\r\n";
|
|
|
|
$aClient->send($response);
|
|
|
|
}
|
|
|
|
|
2023-09-06 20:09:20 +02:00
|
|
|
use constant {
|
|
|
|
PREPROCESSOR_STATE_TEXT => 0,
|
|
|
|
PREPROCESSOR_STATE_VAR => 1,
|
|
|
|
PREPROCESSOR_STATE_INC => 2
|
|
|
|
};
|
|
|
|
sub preprocessHTML {
|
|
|
|
my $aContent = $_[0];
|
|
|
|
my $aVariables = $_[1];
|
|
|
|
|
|
|
|
my $contentLength = length($aContent);
|
|
|
|
my $state = PREPROCESSOR_STATE_TEXT;
|
|
|
|
my $currentString = "";
|
|
|
|
my $index = 0;
|
|
|
|
my $output = "";
|
|
|
|
while($index<$contentLength) {
|
|
|
|
my $char = substr($aContent, $index++, 1);
|
|
|
|
given($state) {
|
|
|
|
when(PREPROCESSOR_STATE_TEXT) {
|
|
|
|
if($char eq "{" && $index<$contentLength) {
|
|
|
|
my $nextChar = substr($aContent, $index, 1);
|
|
|
|
if($nextChar eq "{") {
|
|
|
|
$index++;
|
|
|
|
$state = PREPROCESSOR_STATE_VAR;
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if($char eq "[" && $index<$contentLength) {
|
|
|
|
my $nextChar = substr($aContent, $index, 1);
|
|
|
|
if($nextChar eq "[") {
|
|
|
|
$index++;
|
|
|
|
$state = PREPROCESSOR_STATE_INC;
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$output.=$char;
|
|
|
|
}
|
|
|
|
when(PREPROCESSOR_STATE_VAR) {
|
|
|
|
if($char eq "}" && $index<$contentLength) {
|
|
|
|
my $nextChar = substr($aContent, $index, 1);
|
|
|
|
if($nextChar eq "}") {
|
|
|
|
$index++;
|
|
|
|
$output.=$aVariables->{$currentString};
|
|
|
|
$currentString = "";
|
|
|
|
$state = PREPROCESSOR_STATE_TEXT;
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$currentString.=$char;
|
|
|
|
}
|
|
|
|
when(PREPROCESSOR_STATE_INC) {
|
|
|
|
if($char eq "]" && $index<$contentLength) {
|
|
|
|
my $nextChar = substr($aContent, $index, 1);
|
|
|
|
if($nextChar eq "]") {
|
|
|
|
$index++;
|
|
|
|
if(open(my $file, "<", $currentString)) {
|
2023-09-06 21:51:11 +02:00
|
|
|
$output.=preprocessHTML(readFullFile($file), $aVariables);
|
2023-09-06 20:09:20 +02:00
|
|
|
close($file);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$output.="[[Include file not found]";
|
|
|
|
}
|
|
|
|
$currentString = "";
|
|
|
|
$state = PREPROCESSOR_STATE_TEXT;
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$currentString.=$char;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $output;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub sendTemplate {
|
|
|
|
my $aFilePath = $_[0];
|
|
|
|
my $aClient = $_[1];
|
|
|
|
my $aVariables = $_[2];
|
|
|
|
|
|
|
|
my $result = open(my $file, "<", $aFilePath);
|
|
|
|
if(!$result) {
|
|
|
|
sendNotFound($aClient);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
my $content = preprocessHTML(readFullFile($file), $aVariables);
|
|
|
|
close($file);
|
|
|
|
my $length = length($content);
|
|
|
|
my $response = getBaseResponse(200, "OK");
|
|
|
|
$response.="Content-Length: $length\r\n\r\n";
|
|
|
|
$response.=$content;
|
|
|
|
$aClient->send($response);
|
|
|
|
}
|
|
|
|
|
2023-09-05 19:46:15 +02:00
|
|
|
sub sendResponse {
|
|
|
|
my $aClient = $_[0];
|
|
|
|
my $aRequest = $_[1];
|
2023-09-06 21:51:11 +02:00
|
|
|
my $aConnection = $_[2];
|
2023-09-05 19:46:15 +02:00
|
|
|
|
2023-09-12 20:21:15 +02:00
|
|
|
if($aRequest->{"version"} ne "HTTP/1.0" && $aRequest->{"version"} ne "HTTP/1.1") {
|
2023-09-05 19:46:15 +02:00
|
|
|
sendNotImplemented($aClient);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
given($aRequest->{"method"}) {
|
|
|
|
when(HTTP_METHOD_GET) {
|
2023-09-06 21:51:11 +02:00
|
|
|
my $path = File::Spec->canonpath($aRequest->{"path"}{"url"});
|
2023-09-06 20:09:20 +02:00
|
|
|
if($path eq "/index.html" || $path eq "/index.htm") {
|
|
|
|
$path = "/";
|
2023-09-05 19:46:15 +02:00
|
|
|
}
|
2023-09-08 22:05:21 +02:00
|
|
|
if(frontend_routes::handlePath($aClient, $path, $aRequest, $aConnection)) {
|
2023-09-06 20:09:20 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
my $filePath = "static".$path;
|
2023-09-05 19:46:15 +02:00
|
|
|
my $result = open(my $file, "<", $filePath);
|
|
|
|
if(!$result) {
|
2023-09-06 20:09:20 +02:00
|
|
|
sendNotFound($aClient);
|
2023-09-05 19:46:15 +02:00
|
|
|
return;
|
|
|
|
}
|
2023-09-06 20:09:20 +02:00
|
|
|
my $content = readFullFile($file);
|
2023-09-05 19:46:15 +02:00
|
|
|
close($file);
|
|
|
|
my $response = getBaseResponse(200, "OK");
|
|
|
|
# TODO
|
|
|
|
#my $mime = File::Type->new()->mime_type($filePath);
|
|
|
|
#$response.="Content-Type: $mime\r\n";
|
|
|
|
$response.="Content-Length: ".length($content)."\r\n\r\n";
|
|
|
|
$response.=$content;
|
|
|
|
$aClient->send($response);
|
|
|
|
}
|
2023-09-08 22:05:21 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2023-09-05 19:46:15 +02:00
|
|
|
default {
|
|
|
|
sendNotImplemented($aClient);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-15 21:01:26 +02:00
|
|
|
sub createUser {
|
|
|
|
my $aName = $_[0];
|
|
|
|
my $aPassword = $_[1];
|
|
|
|
my $aPrivileges = $_[2];
|
|
|
|
my $aConnection = $_[3];
|
|
|
|
|
|
|
|
my $id = 0;
|
|
|
|
my $query = $aConnection->prepare(qq(select id from users order by rowid desc limit 1;));
|
|
|
|
$query->execute();
|
|
|
|
my @row = $query->fetchrow_array();
|
|
|
|
if(scalar(@row)>0) {
|
|
|
|
$id = $row[0]+1;
|
|
|
|
}
|
|
|
|
|
|
|
|
my $password = Digest::SHA::sha256_hex($aPassword);
|
|
|
|
$query = $aConnection->prepare(qq(insert into users values($id, ?, ?, ?);));
|
|
|
|
$query->execute($aName, $aPassword, $aPrivileges);
|
|
|
|
}
|
|
|
|
|
2023-09-05 19:46:15 +02:00
|
|
|
sub httpServerWorker {
|
2023-09-06 21:51:11 +02:00
|
|
|
my $db = DBI->connect("DBI:SQLite:dbname=$configuration::database", "", "", {RaiseError=>1});
|
2023-09-11 10:08:12 +02:00
|
|
|
my $query = $db->prepare(qq(select id from users;));
|
|
|
|
$query->execute();
|
|
|
|
my @row = $query->fetchrow_array();
|
|
|
|
if(scalar(@row)==0) {
|
|
|
|
# Create default user
|
2023-09-15 21:01:26 +02:00
|
|
|
createUser("admin", "admin", 2, $db);
|
2023-09-11 10:08:12 +02:00
|
|
|
}
|
|
|
|
|
2023-09-05 19:46:15 +02:00
|
|
|
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");
|
|
|
|
return;
|
|
|
|
}
|
2023-09-11 10:08:12 +02:00
|
|
|
|
2023-09-05 19:46:15 +02:00
|
|
|
while(1) {
|
|
|
|
my $client = $server->accept();
|
|
|
|
my $buffer;
|
|
|
|
$client->recv($buffer, 4096);
|
|
|
|
if(length($buffer)==0) {
|
|
|
|
close($client);
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
my %request = parseHTTPRequest($buffer);
|
2023-09-06 21:51:11 +02:00
|
|
|
sendResponse($client, \%request, $db);
|
2023-09-05 19:46:15 +02:00
|
|
|
close($client);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-06 21:51:11 +02:00
|
|
|
1;
|