irclogger_web/frontend_routes.pm

833 lines
31 KiB
Perl

# irclogger_web
# Copyright (C) 2023 mrkubax10 <mrkubax10@onet.pl>
# This program is free software: you can redistribute it and/or modify
# 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.
# 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 Affero General Public License for more details.
# 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/>.
package frontend_routes;
use lib ".";
use frontend_session;
use logger;
use Digest::SHA;
use threads::shared;
use feature qw(switch);
use strict;
use warnings;
no warnings qw(experimental::smartmatch);
sub verifyRequestPrivileges {
my $aRequest = $_[0];
my $aClient = $_[1];
my $aPrivileges = $_[2];
my $aConnection = $_[3];
if(!defined($aRequest->{"cookies"}{"session"}) || !frontend_session::isValidSession($aRequest->{"cookies"}{"session"})) {
frontend::redirect($aClient, "/");
return 0;
}
if(defined($aRequest->{"headers"}{"Content-Type"}) && $aRequest->{"headers"}{"Content-Type"} ne "application/x-www-form-urlencoded") {
frontend::sendBadRequest($aClient, "Unsupported form Content-Type (application/x-www-form-urlencoded required)");
return 0;
}
if(!defined($aRequest->{"content"})) {
frontend::sendBadRequest($aClient, "Request content required");
return 0;
}
my $session = $frontend_session::sessions{$aRequest->{"cookies"}{"session"}};
my $query = $aConnection->prepare(qq(select privileges from users where name=?;));
$query->execute($session->{"username"});
my @row = $query->fetchrow_array();
if($row[0]<$aPrivileges) {
frontend::sendForbidden($aClient, "Insufficient permissions to perform this operation");
return 0;
}
return 1;
}
sub verifyChannelAccess {
my $aRequest = $_[0];
my $aClient = $_[1];
my $aConnection = $_[2];
my $aChannelID = $_[3];
if(!defined($aRequest->{"cookies"}{"session"}) || !frontend_session::isValidSession($aRequest->{"cookies"}{"session"})) {
frontend::sendUnauthorized($aClient, "You need to log in to view this resource");
return 0;
}
my $session = $frontend_session::sessions{$aRequest->{"cookies"}{"session"}};
my $query = $aConnection->prepare(qq(select id, privileges from users where name=?;));
$query->execute($session->{"username"});
my @row = $query->fetchrow_array();
my $userID = $row[0];
my $privileges = $row[1];
if($privileges>0) {
return 1;
}
$query = $aConnection->prepare(qq(select user_id from accessors where channel_id=$aChannelID and user_id=$userID;));
$query->execute();
@row = $query->fetchrow_array();
if(scalar(@row)==0) {
frontend::sendForbidden($aClient, "You don't have access to this channel logs");
return 0;
}
return 1;
}
sub enumerateServers {
my $aConnection = $_[0];
my $output = "<select name=\"server\">";
my $query = $aConnection->prepare(qq(select id, name from servers;));
$query->execute();
while(my @row = $query->fetchrow_array()) {
my $serverID = $row[0];
my $server = $row[1];
$output.="<option value=\"$serverID\">$server</option>";
}
$output.="</select>";
return $output;
}
sub enumerateChannels {
my $aConnection = $_[0];
my $output = "<select name=\"channel\">";
my $query = $aConnection->prepare(qq(select channels.id, channels.name, servers.name from channels inner join servers on channels.server_id=servers.id;));
$query->execute();
while(my @row = $query->fetchrow_array()) {
my $channelID = $row[0];
my $channel = $row[1];
my $server = $row[2];
$output.="<option value=\"$channelID\">$channel at $server</option>";
}
$output.="</select>";
return $output;
}
sub enumerateUsers {
my $aConnection = $_[0];
my $aSession = $_[1];
my $output = "<select name=\"user\">";
my $query = $aConnection->prepare(qq(select id, name from users;));
$query->execute();
while(my @row = $query->fetchrow_array()) {
my $id = $row[0];
my $name = $row[1];
if($name eq $aSession->{"username"}) {
next;
}
$output.="<option value=\"$id\">$name</option>";
}
$output.="</select>";
return $output;
}
sub handlePath {
my $aClient = $_[0];
my $aPath = $_[1];
my $aRequest = $_[2];
my $aConnection = $_[3];
given($aPath) {
when("/") {
my $userbar;
my $logged = 0;
if(defined($aRequest->{"cookies"}) && defined($aRequest->{"cookies"}{"session"})) {
my $session = $aRequest->{"cookies"}{"session"};
if(frontend_session::isValidSession($session) && defined($frontend_session::sessions{$session}{"username"}) && $frontend_session::sessions{$session}{"logged"}) {
my $username = $frontend_session::sessions{$session}{"username"};
$userbar = "<a href=\"panel\">$username</a> | ";
$userbar.="<a href=\"logout_action\">Log out</a>";
$logged = 1;
}
}
if(!$logged) {
$userbar = "<form action=\"login_action\" method=\"POST\">";
$userbar.="<input name=\"username\" type=\"text\" placeholder=\"Username\" /> ";
$userbar.="<input name=\"password\" type=\"password\" placeholder=\"Password\" /> ";
$userbar.="<input name=\"login\" type=\"submit\" value=\"Login\" />";
$userbar.="</form>";
}
my $query = $aConnection->prepare(qq(select channels.id, channels.name, channels.enabled, servers.name, servers.enabled 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 $channelEnabled = $row[2] && $row[4];
my $serverName = $row[3];
$channelName =~ s/%23/#/;
my $status = $channelEnabled?"<span style=\"color:green\">Enabled</span>":"<span style=\"color:gray\">Disabled</span>";
$table.="<tr><td><a href=\"view_logs?channel=$channelID\">$channelName</a></td><td>$serverName</td><td>$status</td></tr>";
}
my $privateChannels = "";
if($logged) {
$query = $aConnection->prepare(qq(select id, privileges from users where name=?;));
$query->execute($frontend_session::sessions{$aRequest->{"cookies"}{"session"}}{"username"});
my @row = $query->fetchrow_array();
if(scalar(@row)==0) {
frontend::redirect($aClient, "/");
return 1;
}
my $id = $row[0];
my $privileges = $row[1];
if($privileges>0) {
$query = $aConnection->prepare(qq(select id from channels where public=0;));
$query->execute();
}
else {
$query = $aConnection->prepare(qq(select channel_id from accessors where user_id=?;));
$query->execute($id);
}
while(@row = $query->fetchrow_array()) {
my $channelID = $row[0];
my $channelQuery = $aConnection->prepare(qq(select channels.name, channels.enabled, servers.name, servers.enabled from channels inner join servers on channels.server_id=servers.id where channels.id=$channelID;));
$channelQuery->execute();
@row = $channelQuery->fetchrow_array();
my $channelName = $row[0];
$channelName =~ s/%23/#/;
my $channelEnabled = $row[1] && $row[3];
my $serverName = $row[2];
my $status = $channelEnabled?"<span style=\"color:green\">Enabled</span>":"<span style=\"color:gray\">Disabled</span>";
$privateChannels.="<tr><td><a href=\"view_logs?channel=$channelID\">$channelName</a></td><td>$serverName</td><td>$status</td></tr>";
}
}
frontend::sendTemplate("templates/index.html", $aClient, {"userbar"=>$userbar, "publicChannels"=>$table, "privateChannels"=>$privateChannels});
return 1;
}
when("/login_action") {
if(defined($aRequest->{"cookies"}{"session"}) && frontend_session::isValidSession($aRequest->{"cookies"}{"session"})) {
frontend::redirect($aClient, "/");
return 1;
}
if(defined($aRequest->{"headers"}{"Content-Type"}) && $aRequest->{"headers"}{"Content-Type"} ne "application/x-www-form-urlencoded") {
frontend::sendBadRequest($aClient, "Unsupported form Content-Type (application/x-www-form-urlencoded required)");
return 1;
}
if(!defined($aRequest->{"content"})) {
frontend::sendBadRequest($aClient, "Request content required");
return 1;
}
my %parameters = frontend::parsePathParameters($aRequest->{"content"});
if(!defined($parameters{"username"})) {
frontend::sendBadRequest($aClient, "Username parameter required");
return 1;
}
if(!defined($parameters{"password"})) {
frontend::sendBadRequest($aClient, "Password parameter required");
return 1;
}
my $username = $parameters{'username'};
my $hashedPassword = Digest::SHA::sha256_hex($parameters{"password"});
my $query = $aConnection->prepare(qq(select name, password from users where name=?;));
$query->execute($username);
my @row = $query->fetchrow_array();
if(scalar(@row)==0) {
frontend::sendBadRequest($aClient, "Unknown user $username");
return 1;
}
if($row[1] ne $hashedPassword) {
frontend::sendBadRequest($aClient, "Wrong password");
return 1;
}
my $token = frontend_session::newSessionToken();
$frontend_session::sessions{$token}{"username"} = $username;
$frontend_session::sessions{$token}{"logged"} = 1;
my $response = frontend::getBaseResponse(301, "OK");
$response.="Location: /\r\n";
$response.="Content-Length: 0\r\n";
$response.="Set-Cookie: session=$token;expires=".localtime(time()+7*24*3600)."\r\n\r\n";
$aClient->send($response);
return 1;
}
when("/logout_action") {
if(defined($aRequest->{"cookies"}{"session"})) {
frontend_session::deleteSession($aRequest->{"cookies"}{"session"});
}
frontend::redirect($aClient, "/");
return 1;
}
when("/panel") {
if(!defined($aRequest->{"cookies"}{"session"}) || !frontend_session::isValidSession($aRequest->{"cookies"}{"session"})) {
frontend::redirect($aClient, "/");
return 1;
}
my $session = $frontend_session::sessions{$aRequest->{"cookies"}{"session"}};
my $query = $aConnection->prepare(qq(select privileges from users where name=?;));
$query->execute($session->{"username"});
my @row = $query->fetchrow_array();
if(scalar(@row)==0) {
frontend::redirect($aClient, "/");
return 1;
}
my $privileges = $row[0];
my $manageChannelAccess = "";
my $addUser = "";
my $updateUser = "";
if($privileges>=1) { # moderator
$manageChannelAccess.="<h3>Manage channel access</h3>";
$manageChannelAccess.="<form action=\"manage_access_action\" method=\"POST\">";
$manageChannelAccess.=enumerateUsers($aConnection, $session)." ";
$manageChannelAccess.=enumerateChannels($aConnection)."<br />";
$manageChannelAccess.="<input name=\"grant\" type=\"submit\" value=\"Grant access\" /> ";
$manageChannelAccess.="<input name=\"revoke\" type=\"submit\" value=\"Revoke access\" />";
$manageChannelAccess.="</form>";
$addUser.="<h3>Add user</h3>";
$addUser.="<form action=\"add_user_action\" method=\"POST\">";
$addUser.="<input name=\"name\" type=\"text\" placeholder=\"Username\" /><br />";
$addUser.="<input name=\"password\" type=\"password\" placeholder=\"Password\" /><br />";
$addUser.="<input name=\"confirmPassword\" type=\"password\" placeholder=\"Confirm password\" /><br />";
$addUser.="<input name=\"operator\" type=\"checkbox\" />Operator<br />";
$addUser.="<input type=\"submit\" value=\"Add\" />";
$addUser.="</form>";
$updateUser.="<h3>Update user</h3>";
$updateUser.="<form action=\"update_user_action\" method=\"POST\">";
$updateUser.=enumerateUsers($aConnection, $session)."<br />";
$updateUser.="<input name=\"operator\" type=\"checkbox\" />Operator<br />";
$updateUser.="<input name=\"update\" type=\"submit\" value=\"Update\" /> ";
$updateUser.="<input name=\"delete\" type=\"submit\" value=\"Delete\" />";
$updateUser.="</form>";
}
my $addServer = "";
my $updateServer = "";
if($privileges==2) {
$addServer.="<h3>Add server</h3>";
$addServer.="<form action=\"add_server_action\" method=\"POST\">";
$addServer.="<input name=\"name\" type=\"text\" placeholder=\"Server name\" /><br />";
$addServer.="<input name=\"address\" type=\"text\" placeholder=\"Server address\" /> ";
$addServer.="<input name=\"port\" type=\"number\" placeholder=\"Server port (optional)\" /><br />";
$addServer.="<input type=\"submit\" value=\"Add\" />";
$addServer.="</form>";
$updateServer.="<h3>Update server</h3>";
$updateServer.="<form action=\"update_server_action\" method=\"POST\">";
$updateServer.=enumerateServers($aConnection)."<br />";
$updateServer.="<input name=\"enabled\" type=\"checkbox\" checked=\"true\" />Enabled<br />";
$updateServer.="<input type=\"submit\" value=\"Update\" />";
$updateServer.="</form>";
}
my $addChannel = "";
my $updateChannel = "";
if($privileges==2) {
$addChannel.="<h3>Add channel</h3>";
$addChannel.="<form action=\"add_channel_action\" method=\"POST\">";
$addChannel.="<input name=\"channel\" type=\"text\" placeholder=\"Channel\" /> at ";
$addChannel.="<select name=\"server\">";
$query = $aConnection->prepare(qq(select id, name from servers;));
$query->execute();
while(@row = $query->fetchrow_array()) {
my $id = $row[0];
my $name = $row[1];
$addChannel.="<option value=\"$id\">$name</option>";
}
$addChannel.="</select><br />";
$addChannel.="<input name=\"public\" type=\"checkbox\" />Public<br />";
$addChannel.="<input type=\"submit\" value=\"Add\" />";
$addChannel.="</form>";
$updateChannel.="<h3>Update channel</h3>";
$updateChannel.="<form action=\"update_channel_action\" method=\"POST\">";
$updateChannel.=enumerateChannels($aConnection)."<br />";
$updateChannel.="<input name=\"public\" type=\"checkbox\" />Public<br />";
$updateChannel.="<input name=\"enabled\" type=\"checkbox\" checked=\"true\" />Enabled<br />";
$updateChannel.="<input type=\"submit\" value=\"Update\" />";
$updateChannel.="</form>";
}
frontend::sendTemplate("templates/panel.html", $aClient, {
"username"=>$session->{"username"},
"manageChannelAccess"=>$manageChannelAccess,
"addUser"=>$addUser,
"updateUser"=>$updateUser,
"addServer"=>$addServer,
"updateServer"=>$updateServer,
"addChannel"=>$addChannel,
"updateChannel"=>$updateChannel
});
return 1;
}
when("/change_password_action") {
if(!defined($aRequest->{"cookies"}{"session"}) || !frontend_session::isValidSession($aRequest->{"cookies"}{"session"})) {
frontend::redirect($aClient, "/");
return 1;
}
if(defined($aRequest->{"headers"}{"Content-Type"}) && $aRequest->{"headers"}{"Content-Type"} ne "application/x-www-form-urlencoded") {
frontend::sendBadRequest($aClient, "Unsupported form Content-Type (application/x-www-form-urlencoded required)");
return 1;
}
if(!defined($aRequest->{"content"})) {
frontend::sendBadRequest($aClient, "Request content required");
return 1;
}
my $session = $frontend_session::sessions{$aRequest->{"cookies"}{"session"}};
my %parameters = frontend::parsePathParameters($aRequest->{"content"});
if(!defined($parameters{"currentPassword"})) {
frontend::sendBadRequest($aClient, "Current password parameter required");
return 1;
}
if(!defined($parameters{"newPassword"})) {
frontend::sendBadRequest($aClient, "New password parameter required");
return 1;
}
my $query = $aConnection->prepare(qq(select password from users where name=?;));
$query->execute($session->{"username"});
my @row = $query->fetchrow_array();
my $password = $row[0];
if($password ne Digest::SHA::sha256_hex($parameters{"currentPassword"})) {
frontend::sendBadRequest($aClient, "Wrong password");
return 1;
}
if($parameters{"newPassword"} eq $password) {
frontend::sendBadRequest($aClient, "New password and current password match");
return 1;
}
$query = $aConnection->prepare(qq(update users set password=? where name=?;));
$query->execute(Digest::SHA::sha256_hex($parameters{"newPassword"}), $session->{"username"});
frontend::redirect($aClient, "/password_changed.html");
return 1;
}
when("/delete_account_action") {
if(!defined($aRequest->{"cookies"}{"session"}) || !frontend_session::isValidSession($aRequest->{"cookies"}{"session"})) {
frontend::redirect($aClient, "/");
return 1;
}
my $session = $frontend_session::sessions{$aRequest->{"cookies"}{"session"}};
my %parameters = frontend::parsePathParameters($aRequest->{"content"});
if(!defined($parameters{"password"})) {
frontend::sendBadRequest($aClient, "Password parameter required");
return 1;
}
my $query = $aConnection->prepare(qq(select id, password from users where name=?;));
$query->execute($session->{"username"});
my @row = $query->fetchrow_array();
my $id = $row[0];
my $password = $row[1];
if($id==0) {
frontend::sendBadRequest($aClient, "Cannot delete user with ID 0 (admin)");
return 1;
}
if($password ne Digest::SHA::sha256_hex($parameters{"password"})) {
frontend::sendBadRequest($aClient, "Wrong password");
return 1;
}
frontend::deleteUser($id, $aConnection);
frontend_session::deleteSession($aRequest->{"cookies"}{"session"});
frontend::redirect($aClient, "/account_deleted.html");
return 1;
}
when("/manage_access_action") {
if(!verifyRequestPrivileges($aRequest, $aClient, 1, $aConnection)) {
return 1;
}
my %parameters = frontend::parsePathParameters($aRequest->{"content"});
if(!defined($parameters{"user"}) || length($parameters{"user"})==0) {
frontend::sendBadRequest($aClient, "User required");
return 1;
}
if(!defined($parameters{"channel"}) || length($parameters{"channel"})==0) {
frontend::sendBadRequest($aClient, "Channel required");
return 1;
}
if(defined($parameters{"grant"})) {
my $query = $aConnection->prepare(qq(select id from channels where id=?;));
$query->execute($parameters{"channel"});
my @row = $query->fetchrow_array();
if(scalar(@row)==0) {
frontend::sendBadRequest($aClient, "Channel with ID $parameters{'channel'} doesn't exist");
return 1;
}
if($row[0]==1) {
frontend::sendBadRequest($aClient, "Channel with ID $parameters{'channel'} is public");
return 1;
}
$query = $aConnection->prepare(qq(select id from users where id=?;));
$query->execute($parameters{"user"});
@row = $query->fetchrow_array();
if(scalar(@row)==0) {
frontend::sendBadRequest($aClient, "User with ID $parameters{'user'} doesn't exist");
return 1;
}
$query = $aConnection->prepare(qq(select user_id from accessors where user_id=? and channel_id=?;));
$query->execute($parameters{"user"}, $parameters{"channel"});
@row = $query->fetchrow_array();
if(scalar(@row)>0) {
frontend::sendBadRequest($aClient, "User with ID $parameters{'user'} already has access to channel with ID $parameters{'channel'}");
return 1;
}
$query = $aConnection->prepare(qq(insert into accessors values(?, ?);));
$query->execute($parameters{"user"}, $parameters{"channel"});
frontend::redirect($aClient, "/channel_access_granted.html");
}
elsif(defined($parameters{"revoke"})) {
my $query = $aConnection->prepare(qq(select id from channels where id=?;));
$query->execute($parameters{"channel"});
my @row = $query->fetchrow_array();
if(scalar(@row)==0) {
frontend::sendBadRequest($aClient, "Channel with ID $parameters{'channel'} doesn't exist");
return 1;
}
$query = $aConnection->prepare(qq(select id from users where id=?;));
$query->execute($parameters{"user"});
@row = $query->fetchrow_array();
if(scalar(@row)==0) {
frontend::sendBadRequest($aClient, "User with ID $parameters{'user'} doesn't exist");
return 1;
}
$query = $aConnection->prepare(qq(select user_id from accessors where user_id=? and channel_id=?;));
$query->execute($parameters{"user"}, $parameters{"channel"});
@row = $query->fetchrow_array();
if(scalar(@row)==0) {
frontend::sendBadRequest($aClient, "User with ID $parameters{'user'} doesn't have access to channel with ID $parameters{'channel'}");
return 1;
}
$query = $aConnection->prepare(qq(delete from accessors where user_id=? and channel_id=?;));
$query->execute($parameters{"user"}, $parameters{"channel"});
frontend::redirect($aClient, "/channel_access_revoked.html");
}
else {
frontend::sendBadRequest($aClient, "Action (grant or revoke) required");
}
return 1;
}
when("/add_user_action") {
if(!verifyRequestPrivileges($aRequest, $aClient, 1, $aConnection)) {
return 1;
}
my %parameters = frontend::parsePathParameters($aRequest->{"content"});
if(!defined($parameters{"name"}) || length($parameters{"name"})==0) {
frontend::sendBadRequest($aClient, "Username required");
return 1;
}
if(!defined($parameters{"password"}) || length($parameters{"password"})==0) {
frontend::sendBadRequest($aClient, "Password required");
return 1;
}
if(!defined($parameters{"confirmPassword"}) || length($parameters{"confirmPassword"})==0) {
frontend::sendBadRequest($aClient, "Confirm password required");
return 1;
}
if($parameters{"password"} ne $parameters{"confirmPassword"}) {
frontend::sendBadRequest($aClient, "Password and confirm password don't match");
return 1;
}
my $query = $aConnection->prepare(qq(select id from users where name=?;));
$query->execute($parameters{"name"});
my @row = $query->fetchrow_array();
if(scalar(@row)>0) {
frontend::sendConflict($aClient, "User $parameters{'name'} already exists");
return 1;
}
frontend::createUser($parameters{"name"}, $parameters{"password"}, defined($parameters{"operator"}), $aConnection);
frontend::redirect($aClient, "/user_added.html");
return 1;
}
when("/update_user_action") {
if(!verifyRequestPrivileges($aRequest, $aClient, 1, $aConnection)) {
return 1;
}
my %parameters = frontend::parsePathParameters($aRequest->{"content"});
if(!defined($parameters{"user"}) || length($parameters{"user"})==0) {
frontend::sendBadRequest($aClient, "User required");
return 1;
}
my $query = $aConnection->prepare(qq(select privileges from users where id=?;));
$query->execute($parameters{"user"});
my @row = $query->fetchrow_array();
if(scalar(@row)==0) {
frontend::sendBadRequest($aClient, "User with ID $parameters{'user'} doesn't exist");
return 1;
}
if($row[0]>1 && !verifyRequestPrivileges($aRequest, $aClient, 2, $aConnection)) {
return 1;
}
if(defined($parameters{"update"})) {
$query = $aConnection->prepare(qq(update users set privileges=? where id=?;));
$query->execute(defined($parameters{"operator"})?1:0, $parameters{"user"});
}
elsif(defined($parameters{"delete"})) {
frontend::deleteUser($parameters{"user"}, $aConnection);
}
else {
frontend::sendBadRequest($aClient, "Action (update or delete) required");
return 1;
}
frontend::redirect($aClient, "/user_updated.html");
return 1;
}
when("/add_server_action") {
if(!verifyRequestPrivileges($aRequest, $aClient, 2, $aConnection)) {
return 1;
}
my %parameters = frontend::parsePathParameters($aRequest->{"content"});
if(!defined($parameters{"name"}) || length($parameters{"name"})==0) {
frontend::sendBadRequest($aClient, "Server name required");
return 1;
}
if(!defined($parameters{"address"}) || length($parameters{"address"})==0) {
frontend::sendBadRequest($aClient, "Server address required");
return 1;
}
my $port = 6667;
if(defined($parameters{"port"}) && length($parameters{"port"})>0) {
$port = $parameters{"port"};
}
my $query = $aConnection->prepare(qq(select id from servers where name=?;));
$query->execute($parameters{"name"});
my @row = $query->fetchrow_array();
if(scalar(@row)>0) {
frontend::sendConflict($aClient, "Server with name $parameters{'name'} already exists");
return 1;
}
$query = $aConnection->prepare(qq(select id from servers order by rowid desc limit 1;));
$query->execute();
@row = $query->fetchrow_array();
my $lastID = 0;
if(scalar(@row)>0) {
$lastID = $row[0]+1;
}
$query = $aConnection->prepare(qq(insert into servers values($lastID, ?, ?, ?, 1);));
$query->execute($parameters{"name"}, $parameters{"address"}, $port);
frontend::redirect($aClient, "/server_added.html");
logger::createLogger($parameters{"name"}, $parameters{"address"}, $port, ());
return 1;
}
when("/update_server_action") {
if(!verifyRequestPrivileges($aRequest, $aClient, 2, $aConnection)) {
return 1;
}
my %parameters = frontend::parsePathParameters($aRequest->{"content"});
if(!defined($parameters{"server"}) || length($parameters{"server"})==0) {
frontend::sendBadRequest($aClient, "Server required");
return 1;
}
my $query = $aConnection->prepare(qq(select name, host, port, enabled from servers where id=?;));
$query->execute($parameters{"server"});
my @row = $query->fetchrow_array();
if(scalar(@row)==0) {
frontend::sendBadRequest($aClient, "Server with ID $parameters{'server'} doesn't exist");
return 1;
}
my $server = $row[0];
my $serverEnabled = $row[3];
if(defined($parameters{"enabled"}) && !$serverEnabled) {
my $host = $row[1];
my $port = $row[2];
$query = $aConnection->prepare(qq(select name, enabled from channels where server_id=?;));
$query->execute($parameters{"server"});
my @channels;
while(@row = $query->fetchrow_array()) {
if(!$row[1]) {
next;
}
push(@channels, $row[0]);
}
logger::createLogger($server, $host, $port, \@channels);
}
elsif($serverEnabled) {
my $actionQueue = logger::getActionQueueByServerName($server);
push(@$actionQueue, "QUIT");
}
$query = $aConnection->prepare(qq(update servers set enabled=? where id=?;));
$query->execute(defined($parameters{"enabled"})?1:0, $parameters{"server"});
frontend::redirect($aClient, "/server_updated.html");
return 1;
}
when("/add_channel_action") {
if(!verifyRequestPrivileges($aRequest, $aClient, 2, $aConnection)) {
return 1;
}
my %parameters = frontend::parsePathParameters($aRequest->{"content"});
if(!defined($parameters{"channel"}) || length($parameters{"channel"})==0) {
frontend::sendBadRequest($aClient, "Channel name required");
return 1;
}
if(!defined($parameters{"server"}) || length($parameters{"server"})==0) {
frontend::sendBadRequest($aClient, "Server ID required");
return 1;
}
my $query = $aConnection->prepare(qq(select name from servers where id=?;));
$query->execute($parameters{"server"});
my @row = $query->fetchrow_array();
if(scalar(@row)==0) {
frontend::sendBadRequest($aClient, "Invalid server ID");
return 1;
}
my $serverName = $row[0];
$query = $aConnection->prepare(qq(select id from channels where name=? and server_id=?;));
$query->execute($parameters{"channel"}, $parameters{"server"});
@row = $query->fetchrow_array();
$parameters{"channel"}=~s/%23/#/;
if(scalar(@row)>0) {
frontend::sendConflict($aClient, "Channel $parameters{'channel'} already exists on server $serverName");
return 1;
}
$query = $aConnection->prepare(qq(select id from channels order by rowid desc limit 1;));
$query->execute();
@row = $query->fetchrow_array();
my $lastID = 0;
if(scalar(@row)>0) {
$lastID = $row[0]+1;
}
$query = $aConnection->prepare(qq(insert into channels values($lastID, ?, ?, ?, 1);));
$query->execute($parameters{"server"}, $parameters{"channel"}, defined($parameters{"public"})?1:0);
my $actionQueue = logger::getActionQueueByServerName($serverName);
push(@$actionQueue, "JOIN", $parameters{"channel"});
frontend::redirect($aClient, "/channel_added.html");
return 1;
}
when("/update_channel_action") {
if(!verifyRequestPrivileges($aRequest, $aClient, 2, $aConnection)) {
return 1;
}
my %parameters = frontend::parsePathParameters($aRequest->{"content"});
if(!defined($parameters{"channel"}) || length($parameters{"channel"})==0) {
frontend::sendBadRequest($aClient, "Channel required");
return 1;
}
my $query = $aConnection->prepare(qq(select name, server_id, enabled from channels where id=?;));
$query->execute($parameters{"channel"});
my @row = $query->fetchrow_array();
if(scalar(@row)==0) {
frontend::sendBadRequest($aClient, "Channel with ID $parameters{'channel'} doesn't exist");
return 1;
}
my $channel = $row[0];
my $channelEnabled = $row[2];
$query = $aConnection->prepare(qq(select name from servers where id=?;));
$query->execute($row[1]);
@row = $query->fetchrow_array();
my $actionQueue = logger::getActionQueueByServerName($row[0]);
if(defined($parameters{"enabled"}) && !$channelEnabled) {
push(@$actionQueue, "JOIN", $channel);
}
elsif($channelEnabled) {
push(@$actionQueue, "PART", $channel);
}
$query = $aConnection->prepare(qq(update channels set public=?, enabled=? where id=?;));
$query->execute(defined($parameters{"public"})?1:0, defined($parameters{"enabled"})?1:0, $parameters{"channel"});
frontend::redirect($aClient, "/channel_updated.html");
return 1;
}
when("/view_logs") {
my $channelID = $aRequest->{"path"}{"parameters"}{"channel"};
if(!defined($channelID)) {
frontend::sendBadRequest($aClient, "view_logs requires channel URL parameter");
return 1;
}
my $query = $aConnection->prepare(qq(select channels.name, channels.public, 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) {
frontend::sendBadRequest($aClient, "Unknown channel with ID $channelID");
return 1;
}
my $channelName = $row[0];
$channelName =~ s/%23/#/;
my $channelPublic = $row[1];
if(!$channelPublic && !verifyChannelAccess($aRequest, $aClient, $aConnection, $channelID)) {
return 1;
}
my $serverName = $row[2];
my $logsPath = "logs/".$serverName."/".$channelName;
my $result = opendir(my $folder, $logsPath);
if(!$result) {
frontend::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>";
}
frontend::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)) {
frontend::sendBadRequest($aClient, "view_log requires channel URL parameter");
return 1;
}
my $logFile = $aRequest->{"path"}{"parameters"}{"file"};
if(!defined($channelID)) {
frontend::sendBadRequest($aClient, "view_log requires file URL parameter");
return 1;
}
my $query = $aConnection->prepare(qq(select channels.name, channels.public, 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) {
frontend::sendBadRequest($aClient, "Unknown channel with ID $channelID");
return 1;
}
my $channelName = $row[0];
$channelName =~ s/%23/#/;
my $channelPublic = $row[1];
if(!$channelPublic && !verifyChannelAccess($aRequest, $aClient, $aConnection, $channelID)) {
return 1;
}
my $serverName = $row[2];
my $logFilePath = "logs/".$serverName."/".$channelName."/".$logFile;
my $result = open(my $file, "<", $logFilePath);
if(!$result) {
frontend::sendBadRequest($aClient, "No log file $logFile for channel $channelName at $serverName");
return 1;
}
my $content = frontend::readFullFile($file);
close($file);
my $response = frontend::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;
}
1;