diff --git a/frontend_routes.pm b/frontend_routes.pm index beef4c0..3134e8a 100644 --- a/frontend_routes.pm +++ b/frontend_routes.pm @@ -21,6 +21,7 @@ use frontend_session; use logger; use Digest::SHA; +use threads::shared; use feature qw(switch); use strict; @@ -368,6 +369,73 @@ sub handlePath { logger::createLogger($parameters{"name"}, $parameters{"address"}, $port, ()); return 1; } + when("/manage_channel_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 $query = $aConnection->prepare(qq(select privileges from users where name=?;)); + $query->execute($session->{"username"}); + my @row = $query->fetchrow_array(); + if($row[0]<2) { + frontend::sendForbidden($aClient, "Insufficient permissions to perform this operation"); + return 1; + } + + my %parameters = frontend::parsePathParameters($aRequest->{"content"}); + if(!defined($parameters{"channel"})) { + frontend::sendBadRequest($aClient, "Channel name required"); + return 1; + } + if(!defined($parameters{"server"})) { + frontend::sendBadRequest($aClient, "Server ID required"); + return 1; + } + + $query = $aConnection->prepare(qq(select name from servers where id=?;)); + $query->execute($parameters{"server"}); + @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"}); + my $actionQueue = logger::getActionQueueByServerName($serverName); + push(@$actionQueue, "JOIN", $parameters{"channel"}); + + frontend::redirect($aClient, "/channel_added.html"); + return 1; + } when("/view_logs") { my $channelID = $aRequest->{"path"}{"parameters"}{"channel"}; if(!defined($channelID)) { diff --git a/logger.pm b/logger.pm index 5ec2bae..7a24af1 100644 --- a/logger.pm +++ b/logger.pm @@ -17,10 +17,12 @@ package logger; use IO::Socket; +use IO::Select; use List::Util; use Time::Piece; use File::Path; use threads; +use threads::shared; use lib "."; use configuration; @@ -34,13 +36,35 @@ sub connectToServer { my $aPort = $_[1]; my $aServerName = $_[2]; - my $socket = new IO::Socket::INET(PeerAddr=>$aServer, PeerPort=>$aPort, Proto=>"tcp"); + my $socket = IO::Socket::INET->new(PeerAddr=>$aServer, PeerPort=>$aPort, Proto=>"tcp"); $socket->send(sprintf("PASS %s\r\n", $configuration::botPassword)); $socket->send(sprintf("NICK %s\r\n", $configuration::botNick)); $socket->send(sprintf("USER %s %s %s :%s\r\n", $configuration::botUsername, $configuration::botHostname, $aServerName, $configuration::botName)); return $socket; } +sub readLineFromBuffer { + my $aBuffer = $_[0]; + + my $output = ""; + my $bufferLength = length($aBuffer); + foreach my $i (0..$bufferLength-1) { + my $char = substr($aBuffer, $i, 1); + if($char eq "\n" || ($char eq "\r" && $i+1<$bufferLength && substr($aBuffer, $i+1, 1) eq "\n")) { + my $outputLength = length($output); + if($char eq "\r" && $i+1<$bufferLength && substr($aBuffer, $i+1, 1) eq "\n") { + $outputLength+=2; + } + else { + $outputLength++; + } + return ($output, substr($aBuffer, $outputLength, $bufferLength-$outputLength)); + } + $output.=$char; + } + return ("", $aBuffer); +} + sub stripPrefix { my $aLine = $_[0]; @@ -257,43 +281,75 @@ sub joinChannels { } } +our @connections :shared; + sub connectionWorker { my $aHost = $_[0]; my $aPort = $_[1]; my $aServerName = $_[2]; my $aChannels = $_[3]; - my $aConnection = $_[4]; my %logFiles; my $stream = connectToServer($aHost, $aPort, $aServerName); - while(!eof($stream) && $aConnection->[1]) { - my $line = readline($stream); - my @command = parseIRCCommand($line); - printf(":: Server -> %s", $line); + my $streamSelect = IO::Select->new($stream); + my $buffer = ""; + my @actionQueue :shared; + my @connection :shared = ($aServerName, \@actionQueue); + push(@connections, \@connection); + while(!eof($stream)) { + if(scalar(@actionQueue)>0) { + given($actionQueue[0]) { + when("JOIN") { + joinChannel($stream, $actionQueue[1]); + } + } + @actionQueue = (); + } - given($command[0]) { - when("PING") { handlePing($stream, \@command); } - when("PRIVMSG") { handlePrivMsg($stream, \@command, $aServerName, $aChannels, \%logFiles); } - when("JOIN") { handleJoin(\@command, $aServerName, \%logFiles); } - when("QUIT") { handleQuit(\@command, $aServerName, $aChannels, \%logFiles); } - when("PART") { handlePart(\@command, $aServerName, \%logFiles); } - when("376") { joinChannels($stream, $aChannels); } # end of MOTD + my @canRead = $streamSelect->can_read(0); + if(scalar(@canRead)==0) { + next; + } + my $tempBuffer; + $stream->recv($tempBuffer, 512); + $buffer.=$tempBuffer; + my ($line, $remaining) = readLineFromBuffer($buffer); + $buffer = $remaining; + while(length($line)>0) { + my @command = parseIRCCommand($line); + #printf(":: Server -> %s\n", $line); + given($command[0]) { + when("PING") { handlePing($stream, \@command); } + when("PRIVMSG") { handlePrivMsg($stream, \@command, $aServerName, $aChannels, \%logFiles); } + when("JOIN") { handleJoin(\@command, $aServerName, \%logFiles); } + when("QUIT") { handleQuit(\@command, $aServerName, $aChannels, \%logFiles); } + when("PART") { handlePart(\@command, $aServerName, \%logFiles); } + when("376") { joinChannels($stream, $aChannels); } # end of MOTD + } + ($line, $remaining) = readLineFromBuffer($buffer); + $buffer = $remaining; } } close($stream); } -our @connections; - sub createLogger { my $aName = $_[0]; my $aHost = $_[1]; my $aPort = $_[2]; my $aChannels = $_[3]; - my @connection = ($aName, 1); - push(@connection, threads->create("connectionWorker", $aHost, $aPort, $aName, $aChannels, \@connection)); - push(@connections, @connection); + threads->create("connectionWorker", $aHost, $aPort, $aName, $aChannels); +} + +sub getActionQueueByServerName { + my $aServerName = $_[0]; + + foreach my $connection (@connections) { + if($connection->[0] eq $aServerName) { + return $connection->[1]; + } + } } my $db = DBI->connect("DBI:SQLite:dbname=$configuration::database", "", "", {RaiseError=>1}); diff --git a/static/channel_added.html b/static/channel_added.html new file mode 100644 index 0000000..a2cb836 --- /dev/null +++ b/static/channel_added.html @@ -0,0 +1,10 @@ + + +
+Channel successfully added
+ Return to user panel + +