#
# The frame/story info interface
#
-# Copyright (C) 2017, 2023 Balthasar Szczepański
+# Copyright (C) 2017, 2023, 2024 Balthasar Szczepański
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# along with this program. If not, see <http://www.gnu.org/licenses/>.
use strict;
-#use warnings;
+use utf8;
+# use Encode::Locale ('decode_argv');
+use Encode ('encode', 'decode');
+
###PERL_LIB: use lib /botm/lib/bsta
-use bsta_lib qw(failpage gethttpheader getcgi readdatafile printdatafile);
-use File::Copy;
+use botm_common (
+ 'read_header_env',
+ 'read_data_file', 'write_data_file',
+ 'url_query_decode',
+ 'join_path'
+);
+use bsta_lib (
+ 'STATE',
+ 'fail_method', 'fail_content_type',
+ 'get_password',
+ 'merge_settings'
+);
+
+###PERL_PATH_SEPARATOR: PATH_SEPARATOR = /
###PERL_DATA_PATH: DATA_PATH = /botm/data/bsta
+###PERL_DATA_ATTACH_PATH: DATA_ATTACH_PATH = /botm/data/bsta/a
###PERL_DATA_DEFAULT_PATH: DATA_DEFAULT_PATH = /botm/data/bsta/default
###PERL_DATA_NOACCESS_PATH: DATA_NOACCESS_PATH = /botm/data/bsta/noaccess
###PERL_DATA_SETTINGS_PATH: DATA_SETTINGS_PATH = /botm/data/bsta/settings
###PERL_DATA_STATE_PATH: DATA_STATE_PATH = /botm/data/bsta/state
+binmode STDIN, ':encoding(UTF-8)';
+binmode STDOUT, ':encoding(UTF-8)';
+binmode STDERR, ':encoding(UTF-8)';
+# decode_argv();
+
+my $time = time();
+srand ($time-$$);
+
my %http;
my %cgi;
-my %framedata;
-my %nextframedata;
+my %frame_data;
+my %next_frame_data;
my %default;
my %settings;
my %state;
-my $time = time();
-srand ($time-$$);
-
my $method;
-my $frame;
+my $frame = '';
+my $attachment = '';
my $password;
-my $passwordOK;
+my $password_ok;
my $access;
-my $showcommand;
+my $show_command;
my $ongtime;
-my $seconds;
+my $timer;
+my $frame_data_path;
+my $next_frame_data_path;
+my $ong_state;
+my $last_frame;
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
###PERL_SET_PATH: $ENV{'PATH'} = /usr/local/bin:/usr/bin:/bin;
if ($ENV{'REQUEST_METHOD'} =~ /^(HEAD|GET|POST)$/) {
- $method=$1;
+ $method = $1;
}
else{
- exit failpage("Status: 405 Method Not Allowed\nAllow: GET, POST, HEAD\n","405 Method Not Allowed","The interface does not support the $ENV{'REQUEST_METHOD'} method.",$method);
+ exit fail_method($ENV{'REQUEST_METHOD'}, 'GET, POST, HEAD');
}
-%http = gethttpheader (\%ENV);
-%cgi = getcgi($ENV{'QUERY_STRING'});
+%http = read_header_env(\%ENV);
+%cgi = url_query_decode($ENV{'QUERY_STRING'});
if ($method eq 'POST') {
if ($http{'content-type'} eq 'application/x-www-form-urlencoded') {
- my %cgipost=getcgi( <STDIN> );
- foreach my $ind (keys %cgipost) {
- $cgi{$ind}=$cgipost{$ind};
- }
+ my %cgi_post = url_query_decode( <STDIN> );
+ %cgi = merge_settings(\%cgi, \%cgi_post);
}
# multipart not supported
else{
- exit failpage("Status: 415 Unsupported Media Type\n","415 Unsupported Media Type","Unsupported Content-type: $http{'content-type'}.");
+ exit fail_content_type($method, $http{'content-type'});
}
}
-if ($cgi{'f'} =~ /^(.+)$/) {
- $frame=int($1);
-}
-elsif ($ENV{'PATH_INFO'} =~ /^\/(.+)$/) {
- $frame=int($1);
+if ($cgi{'f'} =~ /^.+$/) {
+ $frame = int($&);
}
-else {
- $frame = '';
+elsif ($cgi{'i'} =~ /^.+$/) {
+ $attachment = int($&);
}
-
-if ($cgi{'p'} =~ /^(.+)$/) {
- $password=$1;
+elsif ($ENV{'PATH_INFO'} =~ /^\/a(.+)$/) {
+ $attachment = int($1);
}
-else {
- $password='';
+elsif ($ENV{'PATH_INFO'} =~ /^\/(.+)$/) {
+ $frame = int($1);
}
-%settings=readdatafile(DATA_SETTINGS_PATH);
-%default=readdatafile(DATA_DEFAULT_PATH);
-%framedata=readdatafile(DATA_PATH.$frame);
-%state=readdatafile(DATA_STATE_PATH);
-if($password eq $settings{'password'}){
- $passwordOK = 1;
+$password = get_password(\%cgi);
+
+%settings = read_data_file(DATA_SETTINGS_PATH());
+%default = read_data_file(DATA_DEFAULT_PATH());
+%state = read_data_file(DATA_STATE_PATH());
+
+$ong_state = int($state{'state'});
+$last_frame = int($state{'last'});
+
+$password_ok = ($password eq $settings{'password'});
+
+# attachment info, not frame
+if ($attachment ne '') {
+ %frame_data = read_data_file(DATA_ATTACH_PATH().$attachment);
+ $frame = ($frame_data{'frame'} ne '') ? int($frame_data{'frame'}) : -1;
}
-else{
- $passwordOK = 0;
+# frame info
+elsif ($frame ne '') {
+ if ($frame < 0) {
+ $frame = $last_frame + $frame +1;
+ }
+ $frame_data_path = join_path(PATH_SEPARATOR(), DATA_PATH(), $frame);
+ $next_frame_data_path = join_path(PATH_SEPARATOR(), DATA_PATH(), $frame+1);
+ %frame_data = read_data_file($frame_data_path);
+ %next_frame_data = read_data_file($next_frame_data_path);
+ %frame_data = merge_settings(\%default, \%frame_data);
+ %next_frame_data = merge_settings(\%default, \%next_frame_data);
+
+ $timer = int($state{'nextong'}) - $time;
+ $ongtime = int($state{'ongtime'});
+ if($ongtime == 0) {
+ $ongtime = int($settings{'ongtime'})
+ }
+ $show_command = ($timer < ($ongtime * 3600 / 3));
}
-
-if ($frame eq '') {
- unless($passwordOK) {
+# state info, not frame
+else {
+ unless ($password_ok) {
+ # just show if IP was saved, not its value
if ($state{'ip1'} ne '') {
- $state{'ip1'}=1;
+ $state{'ip1'} = 1;
}
if ($state{'ip2'} ne '') {
- $state{'ip2'}=1;
+ $state{'ip2'} = 1;
}
if ($state{'ip3'} ne '') {
- $state{'ip3'}=1;
+ $state{'ip3'} = 1;
}
}
print "Content-type: text/plain\n\n";
- if($method eq 'HEAD') {
+ if ($method eq 'HEAD') {
exit;
}
- printdatafile(%state);
+ write_data_file(\*STDOUT, '', '', \%state);
+ exit;
}
-else {
- if($frame<0) {
- $frame = int($state{'last'}) + $frame +1;
- %framedata=readdatafile(DATA_PATH.$frame);
- }
-
- %nextframedata=readdatafile(DATA_PATH.($frame+1));
+if (
+ $password_ok || (
+ ($ong_state >= STATE->{'waiting'}) &&
+ ($frame <= $last_frame) &&
+ ($frame >= 0)
+ )
+) {
+ $access = 1;
- foreach my $ind (keys %default) {
- unless(defined($framedata{$ind})){
- $framedata{$ind}=$default{$ind};
- }
- unless(defined($nextframedata{$ind})){
- $nextframedata{$ind}=$default{$ind};
- }
- }
- $seconds=int($state{'nextong'})-$time;
- $ongtime=int($state{'ongtime'});
- if($ongtime == 0) {
- $ongtime=int($settings{'ongtime'})
+ if (
+ ($attachment eq '') &&
+ ($frame_data{'command'} eq '') && (
+ $password_ok ||
+ ($frame < $last_frame) || (
+ ($ong_state >= STATE->{'ready'}) &&
+ $show_command
+ )
+ )
+ ) {
+ $frame_data{'command'} = $next_frame_data{'title'};
}
- $showcommand = ($seconds < ($ongtime*3600/3));
-
- if ($passwordOK || (int($state{'state'}) >= 1 && $frame <= int($state{'last'}) && $frame >= 0)) {
- $access=1;
-
- if ($passwordOK || $frame<int($state{'last'}) || (int($state{'state'}) >= 2 && $showcommand)) {
- $framedata{'command'}=$nextframedata{'title'};
- }
- $framedata{'frame'}=sprintf($settings{'frame'},$frame,$framedata{'ext'});
+}
+else {
+ $access = 0;
+ if ($attachment ne '') {
+ %frame_data = ();
}
else {
- $access=0;
- %framedata = readdatafile(DATA_NOACCESS_PATH);
- foreach my $ind (keys %default) {
- unless(defined($framedata{$ind})){
- $framedata{$ind}=$default{$ind};
- }
- }
- }
-
- # $framedata{'frame'}=sprintf($settings{'frame'},$frame,$framedata{'ext'});
-
- print "Content-type: text/plain\n";
- if(!$access) {
- print "Status: 403 Forbidden\n";
+ %frame_data = read_data_file(DATA_NOACCESS_PATH());
+ %frame_data = merge_settings(\%default, \%frame_data);
}
- print "\n";
- if($method eq 'HEAD') {
- exit;
- }
- printdatafile(%framedata);
}
+if (
+ ($frame_data{'frame'} eq '') &&
+ ($attachment eq '')
+) {
+ $frame_data{'frame'} = sprintf($settings{'frame'}, $frame, $frame_data{'ext'});
+}
+
+print "Content-type: text/plain\n";
+if (!$access) {
+ print "Status: 403 Forbidden\n";
+}
+print "\n";
+if($method eq 'HEAD') {
+ exit;
+}
+write_data_file(\*STDOUT, '', '', \%frame_data);