diff options
Diffstat (limited to 'web/NetUseAdminWeb/lib')
-rw-r--r-- | web/NetUseAdminWeb/lib/NetUseAdminWeb.pm | 302 |
1 files changed, 302 insertions, 0 deletions
diff --git a/web/NetUseAdminWeb/lib/NetUseAdminWeb.pm b/web/NetUseAdminWeb/lib/NetUseAdminWeb.pm new file mode 100644 index 0000000..9524309 --- /dev/null +++ b/web/NetUseAdminWeb/lib/NetUseAdminWeb.pm @@ -0,0 +1,302 @@ +package NetUseAdminWeb; + +# NetUseMod is a program to moderate Usenet posts via web interface. +# Copyright (C) 2024 Salahuddin <salahuddin@member.fsf.org> +# +# 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 <https://www.gnu.org/licenses/>. + +use Dancer2; +use File::Copy 'move'; +use HTML::Entities; + +our $VERSION = '0.1'; + +my $post_to_usenet = 1; # toggle for testing +my $usenet_host = 'YOUR_USENET_PROVIDER'; +my $usenet_port = '119'; # production 536 ssl +my $rnews_location = '/usr/bin/rnews'; +# set user and password in /etc/news/passwd.nntp +# to submit news to your newsgroup provider. + +my $send_rec_ack = 1; +my $noreply_mail = 'no-reply@YOUR_DOMAIN'; + +my $mail_format = 'YOUR_HOME_DIR/NetUseMod/scripts/mail_format.sh'; +my $admin_user = 'XXXXXXXX'; +my $admin_pass = 'XXXXXXXX'; + +# example: /home/username/ +my $dir_base = 'YOUR_HOME_DIR'; +my $dir_incoming = 'incoming_news'; +my $dir_processed = 'processed_news'; +my $dir_rejected = 'rejected_news'; + + +sub sendReceiveAck { + my ($from, $subject, $description) = @_; + system('echo "' . $description . '." | mail -s "Re: ' . $subject . '" -r ' . $noreply_mail . ' \'' . $from . '\''); +} + +sub postToNewsGroup { + my $processed_dest_file = shift; + system($rnews_location . ' -h ' . $usenet_host . ' -P ' . $usenet_port . ' -S ' . $usenet_host . ' < ' . $processed_dest_file); +} + +sub formatNews { + my ($filename, $processed_dest_file) = @_; + system('cat ' . $filename . ' | ' . $mail_format . ' > ' . $processed_dest_file); +} + +sub getFileData { + my $filename = shift; + if ($filename eq '') { + return ''; + } + open(FH, '<', $filename) or die $!; + my $file_data = ''; + while(<FH>){ + $file_data .= $_; + } + close(FH); + return $file_data; +} + +hook before => sub { + if (!session('user') && + request->path !~ m{^/login} ) { + forward '/login', { requested_path => request->path }; + } +}; + +get '/' => sub { + template 'index'; +}; + +sub get_news_list { + my $dir_type = shift; + my $dirname = $dir_base . $dir_type . "/"; + opendir my($dh), $dirname or die "Couldn't open dir '$dirname': $!"; + my @files = readdir $dh; + closedir $dh; + # remve .. . entries. + for(my $index = 0; $index <= $#files; $index++){ + if ($files[$index] eq '.') { + splice(@files, $index, 1); + #index value changed + last; + } + } + for(my $index = 0; $index <= $#files; $index++){ + if ($files[$index] eq '..') { + splice(@files, $index, 1); + #index value changed + last; + } + } + my %file_list; + foreach(@files) { + my $fh = $dirname . '/' . $_; + my $epoch_timestamp = (stat($fh))[8]; + my $timestamp = localtime($epoch_timestamp); + $file_list{$_} = $timestamp; + } + return %file_list; +} + +get '/listincomingnews' => sub { + my %file_list = get_news_list($dir_incoming); + my $listnews = ''; + for(keys %file_list){ + $listnews .= "<tr>"; + $listnews .= '<td>' . $_ . '</td>'; + $listnews .= '<td>' . $file_list{$_} . '</td>'; # created time + $listnews .= '<td><a href="/viewfile?filename=' . $_ . '&dirtype=' .$dir_incoming . '">view</a></td>'; + $listnews .= "</tr>"; + } + template 'listnews', { listnews => $listnews }; +}; + +get '/viewfile' => sub { + my $filename_param = query_parameters->get('filename'); + my $dir_type = query_parameters->get('dirtype'); + my $filename = $dir_base . $dir_type . '/' . $filename_param; + my $file_data = ''; + $file_data = getFileData($filename); + if ($file_data eq '') { + template 'generic', { message => "emtpy" }; + } + $file_data = encode_entities($file_data); + $file_data =~ s/\n/<br>/g; + my $accept_reject_html_block = ''; + if ($dir_type eq $dir_incoming) { + my $accept_html_button = '<a href="/acceptnews?filename=' . $filename_param . '">Accept</a>'; + my $reject_html_button = '<a href="/rejectnews?filename=' . $filename_param . '">Reject</a>'; + $accept_reject_html_block = $accept_html_button . ' ' . $reject_html_button; + } + template 'viewnews', { file_data => $file_data, accept_reject_html_block => $accept_reject_html_block }; +}; + +get '/listprocessednews' => sub { + my %file_list = get_news_list($dir_processed); + my $listnews = ''; + for(keys %file_list){ + $listnews .= "<tr>"; + $listnews .= '<td>' . $_ . '</td>'; + $listnews .= '<td>' . $file_list{$_} . '</td>'; # created time + $listnews .= '<td><a href="/viewfile?filename=' . $_ . '&dirtype=' . $dir_processed . '">view</a></td>'; + $listnews .= "</tr>"; + } + template 'listnews', { listnews => $listnews }; +}; + +get '/listrejectednews' => sub { + my %file_list = get_news_list($dir_rejected); + my $listnews = ''; + for(keys %file_list){ + $listnews .= "<tr>"; + $listnews .= '<td>' . $_ . '</td>'; + $listnews .= '<td>' . $file_list{$_} . '</td>'; # created time + $listnews .= '<td><a href="/viewfile?filename=' . $_ . '&dirtype=' . $dir_rejected . '">view</a></td>'; + $listnews .= "</tr>"; + } + template 'listnews', { listnews => $listnews }; +}; + +get '/acceptnews' => sub { + my $filename_param = query_parameters->get('filename'); + my $filename = $dir_base . '/' . $dir_incoming . '/' . $filename_param; + my $file_data = ''; + $file_data = getFileData($filename); + if ($file_data eq '') { + template 'generic', { message => "emtpy" }; + } + $file_data = encode_entities($file_data); + $file_data =~ s/\n/<br>/g; + template 'acceptnews', { file_data => $file_data, filename_param => $filename_param }; +}; + +post '/acceptnews' => sub { + my $filename_param = body_parameters->get('filename_param'); + my $filename = $dir_base . '/' . $dir_incoming . '/' . $filename_param; + my $dest_filename = $dir_base . '/' . $dir_processed . '/' . $filename_param; + my $processed_dest_file = $dest_filename . '_processed'; + formatNews($filename, $processed_dest_file); + if ($post_to_usenet eq 1) { + postToNewsGroup($processed_dest_file); + } + move($filename, $dest_filename)or die "The move operation failed: $!"; + template 'generic', { message => 'Success' }; +}; + +get '/rejectnews' => sub { + my $filename_param = query_parameters->get('filename'); + my $filename = $dir_base . '/' . $dir_incoming . '/' . $filename_param; + my $file_data = ''; + $file_data = getFileData($filename); + if ($file_data eq '') { + template 'generic', { message => "emtpy" }; + } + $file_data = encode_entities($file_data); + $file_data =~ s/\n/<br>/g; + template 'rejectnews', { file_data => $file_data, filename_param => $filename_param }; +}; + +sub parseFromAndSubject { + my $message = shift; + my $from = ''; + my $subject = ''; + my @message_array = split(/\n/, $message); + + foreach(@message_array) { + # first empty line + if(/^$/) { + last; + } + if( /^From: / ) { + $from = $_; + $from=~ s/^From: //i; + } elsif( /^Subject: / ) { + $subject = $_; + $subject =~ s/^Subject: //i; + } + } + # trim + $from =~ s/^\s+|\s+$//g; + $subject =~ s/^\s+|\s+$//g; + return ($from, $subject); +} + +post '/rejectnews' => sub { + my $submitbutton = body_parameters->get('submitbutton'); + my $filename_param = body_parameters->get('filename_param'); + my $reject_reason = body_parameters->get('reject_reason'); + my $filename = $dir_base . '/' . $dir_incoming . '/' . $filename_param; + my $dest_filename = $dir_base . '/' . $dir_rejected . '/' . $filename_param; + + if (index($submitbutton, 'Send reject notification') != -1) { + # TODO: send notification + if ($send_rec_ack eq 1) { + open(FH, '<', $filename) or die $!; + my $file_data = ''; + while(<FH>){ + # first empty line + if(/^$/) { + last; + } + $file_data .= $_; + } + close(FH); + my ($from, $subject) = parseFromAndSubject($file_data); + # TODO: check return value + sendReceiveAck($from, $subject, $reject_reason); + } + } + # add error in error variable + move($filename, $dest_filename)or die "The move operation failed: $!"; + + my $message = 'Success'; + template 'generic', { message => $message }; +}; + +get '/login' => sub { + if (session('user')) { + redirect '/'; + } else { + template 'login', { path => param('requested_path') }; + } +}; + +post '/login' => sub { + my $user_value = body_parameters->get('user'); + my $pass_value = body_parameters->get('pass'); + my $error_message = ''; + + if($user_value eq $admin_user && $pass_value eq $admin_pass) { + session user => $user_value; + redirect body_parameters->get('path') || '/'; + } else { + $error_message = 'Invalid login/password.'; + } + + if($error_message ne '') { + return template 'generic', { message => $error_message}; + } +}; + +get '/logout' => sub { + app->destroy_session; + redirect '/'; +}; + +true; |