]> bicyclesonthemoon.info Git - ott/bsta/blobdiff - viewer.1.pl
fix ONG; fix update; total reset
[ott/bsta] / viewer.1.pl
index d535fe40bfe1cb7bf160771d54cc893ca475686d..280a0f9423057468cb4d6f455284bc4661103bcb 100644 (file)
@@ -5,7 +5,7 @@
 #
 # The viewer interface
 #
-# Copyright (C) 2016-2017, 2019-2020, 2023  Balthasar Szczepański
+# Copyright (C) 2016, 2017, 2019, 2020, 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
@@ -27,22 +27,29 @@ use Encode ('encode', 'decode');
 
 ###PERL_LIB: use lib /botm/lib/bsta
 use botm_common (
+       'HTTP_STATUS',
        'read_header_env',
        'read_data_file', 'write_data_file',
        'url_query_decode',
-       'join_path'
+       'join_path',
+       'open_encoded', '_x_encoded',
+       'http_header_status',
+       'merge_url'
 );
 use bsta_lib (
        'STATE', 'TEXT_MODE', 'INTF_STATE',
-       'fail_method', 'fail_content_type',
+       'fail_method', 'fail_content_type', 'redirect',
        'get_remote_addr', 'get_frame', 'get_password',
        'merge_settings',
-       'print_viewer_page', 'write_index'
+       'print_viewer_page', 'write_index', 'write_static_goto',
+       'ong'
 );
-use File::Copy;
 
 ###PERL_PATH_SEPARATOR:     PATH_SEPARATOR     = /
 
+###PERL_CGI_PATH:           CGI_PATH           = /bsta/
+###PERL_CGI_VIEWER_PATH:    CGI_VIEWER_PATH    = /bsta/v
+
 ###PERL_DATA_PATH:          DATA_PATH          = /botm/data/bsta/
 ###PERL_DATA_DEFAULT_PATH:  DATA_DEFAULT_PATH  = /botm/data/bsta/default
 ###PERL_DATA_LIST_PATH:     DATA_LIST_PATH     = /botm/data/bsta/list
@@ -50,8 +57,9 @@ use File::Copy;
 ###PERL_DATA_SETTINGS_PATH: DATA_SETTINGS_PATH = /botm/data/bsta/settings
 ###PERL_DATA_STATE_PATH:    DATA_STATE_PATH    = /botm/data/bsta/state
 ###PERL_DATA_STORY_PATH:    DATA_STORY_PATH    = /botm/data/bsta/story
+###PERL_DATA_WORDS_PATH:    DATA_WORDS_PATH    = /botm/data/bsta/words/
 
-###PERL_WWW_PATH:           WWW_PATH           = /botm/www/1190/bsta/
+###PERL_WWW_PATH:           WWW_PATH           = /botm/www/
 
 binmode STDIN,  ':encoding(UTF-8)';
 binmode STDOUT, ':encoding(UTF-8)';
@@ -64,16 +72,19 @@ srand ($time-$$);
 my %http;
 my %cgi;
 my %frame_data;
+my %prev_frame_data;
 my %next_frame_data;
 my %default;
 my %settings;
 my %state;
 my %new_state;
 my %goto_list;
+my %words_data;
 
 my $method;
 my $frame;
 my $frame_data_path;
+my $prev_frame_data_path;
 my $next_frame_data_path;
 my $password;
 my $password_ok;
@@ -81,10 +92,14 @@ my $IP;
 my $access;
 my $timer;
 my $timer_unlocked;
-my $state_file;
+my $fh;
 my $show_command;
 my $ongtime;
 my $text_mode;
+my $words_page;
+my $words_data_path;
+my $no_cgi;
+my $force_redirect;
 
 delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
 ###PERL_SET_PATH: $ENV{'PATH'} = /usr/local/bin:/usr/bin:/bin;
@@ -93,11 +108,12 @@ if ($ENV{'REQUEST_METHOD'} =~ /^(HEAD|GET|POST)$/) {
        $method = $1;
 }
 else{
-       exit fail_method($ENV{'REQUEST_METHOD'}, 'GET, POST, HEAD');
+       exit fail_method($ENV{'REQUEST_METHOD'}, ['GET', 'POST', 'HEAD']);
 }
 
 %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 %cgi_post = url_query_decode( <STDIN> );
@@ -108,6 +124,8 @@ if ($method eq 'POST') {
                exit fail_content_type($method, $http{'content-type'});
        }
 }
+$no_cgi = (scalar (keys %cgi) == 0);
+
 $IP = get_remote_addr();
 $frame = get_frame(\%cgi);
 $password = get_password(\%cgi);
@@ -123,15 +141,18 @@ if ($frame >= 0) {
 $password_ok = ($password eq $settings{'password'});
 
 # state & activation logic
-if (open ($state_file, "+<:encoding(UTF-8)", DATA_STATE_PATH())) {
-       if (flock($state_file, 2)) {
+if (open_encoded($fh, "+<:encoding(UTF-8)", DATA_STATE_PATH())) {
+       if (flock($fh, 2)) {
                
-               %state = read_data_file($state_file);
+               %state = read_data_file($fh);
                
                if ($frame < 0) {
                        $frame = int($state{'last'}) + $frame +1;
-                       $frame_data_path = join_path(PATH_SEPARATOR(), DATA_PATH(), $frame);
-                       %frame_data = read_data_file($frame_data_path);
+                       if ($frame >= 0) {
+                               $force_redirect = 1;
+                               $frame_data_path = join_path(PATH_SEPARATOR(), DATA_PATH(), $frame);
+                               %frame_data = read_data_file($frame_data_path);
+                       }
                }
                
                if (
@@ -157,11 +178,13 @@ if (open ($state_file, "+<:encoding(UTF-8)", DATA_STATE_PATH())) {
                                elsif ($state{'ip3'} eq '') {
                                        $new_state{'ip3'} = $IP;
                                        $new_state{'state'} = STATE->{'ready'};
+                                       write_static_goto(\%new_state, \%settings, '');
                                }
                                else {
                                        $new_state{'state'} = STATE->{'ready'};
+                                       write_static_goto(\%new_state, \%settings, '');
                                }
-                               write_data_file($state_file, '', '', \%new_state);
+                               write_data_file($fh, \%new_state);
                        }
                }
                elsif (
@@ -171,14 +194,11 @@ if (open ($state_file, "+<:encoding(UTF-8)", DATA_STATE_PATH())) {
                        # ready to activate?
                        # NOTE: at this point frame 0 is already ONGed.
                        my %story;
-                       my $frame_file;
-                       my $in_path;
-                       my $out_path;
-                       my $ext;
                        my $ong_time = int($settings{'firstongtime'});
+                       my $r;
                        
                        %story     = read_data_file(DATA_STORY_PATH());
-                       %goto_list = read_data_file(DATA_LIST_PATH);
+                       %goto_list = read_data_file(DATA_LIST_PATH());
                        
                        if (
                                (int($story{'state'}) == INTF_STATE->{'>|'} ) &&
@@ -186,11 +206,6 @@ if (open ($state_file, "+<:encoding(UTF-8)", DATA_STATE_PATH())) {
                        ) {
                                # conditions met; ACTIVATE!
                                
-                               # update ONG time of frame 1
-                               $frame_data{'ongtime'} = $time;
-                               $frame_data{'timer'} = $ong_time;
-                               write_data_file($frame_data_path, '', '', \%frame_data);
-                               
                                # set initial state
                                $state{'state'} = STATE->{'waiting'};
                                $state{'last'}  = 1; 
@@ -202,24 +217,27 @@ if (open ($state_file, "+<:encoding(UTF-8)", DATA_STATE_PATH())) {
                                
                                # prepare to ONG frame 1
                                
-                               # determine frame file & paths
-                               $ext = (defined($frame_data{'ext'})) ?
-                                       $frame_data{'ext'} :
-                                       $default{'ext'};
-                               $frame_file = sprintf($settings{'frame'}, $frame, $ext);
-                               $in_path  = join_path(PATH_SEPARATOR(), DATA_PATH(), $frame_file);
-                               $out_path = join_path(PATH_SEPARATOR(), WWW_PATH(),  $frame_file);
-                               
-                               # update the GOTO list with frame 1
-                               $goto_list{'title-1'}   = $frame_data{'title'};
-                               $goto_list{'ongtime-1'} = $frame_data{'ongtime'};
-                               
-                               if (copy ($in_path, $out_path)) {
-                                       write_index(\%state, \%settings);
-                                       write_data_file($state_file,     '','', \%state);
-                                       write_data_file(DATA_LIST_PATH(),'','', \%goto_list);
+                               $r = ong(
+                                       1,         # frame ID
+                                       $time,     # ONG time,
+                                       $ong_time, # timer
+                                       0,         # update
+                                       0,         # print
+                                       \%settings,
+                                       \%default,
+                                       \%frame_data,
+                                       \%goto_list
+                               );
+                               if ($r) {
+                                       $r = write_index(\%state, \%settings);
                                }
-                               else {
+                               if ($r) {
+                                       $r = write_static_goto(\%state, \%settings, \%goto_list);
+                               }
+                               if ($r) {
+                                       $r = write_data_file($fh, \%state);
+                               }
+                               unless ($r) {
                                        # FAILED ONG! Story as if it was inactive!
                                        $state{'state'} = STATE->{'inactive'};
                                }
@@ -230,18 +248,78 @@ if (open ($state_file, "+<:encoding(UTF-8)", DATA_STATE_PATH())) {
                # FAILED GET STATE! Story as if it was inactive!
                $state{'state'} = STATE->{'inactive'};
        }
-       close ($state_file);
+       close ($fh);
 }
 else {
        $state{'state'} = STATE->{'inactive'};
 }
 
-$next_frame_data_path = join_path(PATH_SEPARATOR(), DATA_PATH(), $frame+1);
-%next_frame_data = read_data_file($next_frame_data_path);
+$access = (
+       $password_ok || (
+               (int($state{'state'}) >= STATE->{'waiting'}) &&
+               ($frame <= int($state{'last'})) &&
+               ($frame >= 0)
+       )
+);
 
-# apply defaults
-%frame_data      = merge_settings(\%default,      \%frame_data);
-%next_frame_data = merge_settings(\%default, \%next_frame_data);
+if ($access) {
+       if ($no_cgi) {
+               # no CGI - static page is OK
+               if ($frame == 0) {
+                       exit redirect($method, CGI_PATH(), HTTP_STATUS->{'see_other'});
+               }
+               elsif ($frame < int($state{'last'})) {
+                       my $page_file;
+                       if ($frame_data{'page'} ne '') {
+                               $page_file = $frame_data{'page'};
+                       }
+                       else {
+                               $page_file = sprintf(
+                                       $settings{'frame'},
+                                       $frame, 'htm'
+                               );
+                       }
+                       if (_x_encoded('-f',
+                               join_path(PATH_SEPARATOR(), WWW_PATH() , $page_file)
+                       )) {
+                               my $static_url = merge_url(
+                                       {'path' => CGI_PATH()},
+                                       {'path' => $page_file}
+                               );
+                               exit redirect($method, $static_url, HTTP_STATUS->{'see_other'});
+                       }
+               }
+       }
+       if ($force_redirect) {
+               my $redirect_url = merge_url(
+                       {'path' => CGI_VIEWER_PATH()},
+                       {'path' => $frame}
+               );
+               unless ($no_cgi) {
+                       $redirect_url = merge_url(
+                               {'path' => $redirect_url},
+                               {'query' => \%cgi}
+                       );
+               }
+               exit redirect($method, $redirect_url, HTTP_STATUS->{'see_other'});
+       }
+       
+       if ($frame > 0) {
+               $prev_frame_data_path = join_path(PATH_SEPARATOR(), DATA_PATH(), $frame-1);
+               %prev_frame_data = read_data_file($prev_frame_data_path);
+       }
+       $next_frame_data_path = join_path(PATH_SEPARATOR(), DATA_PATH(), $frame+1);
+       %next_frame_data = read_data_file($next_frame_data_path);
+       
+       %frame_data      = merge_settings(\%default,      \%frame_data);
+       %prev_frame_data = merge_settings(\%default, \%prev_frame_data);
+       %next_frame_data = merge_settings(\%default, \%next_frame_data);
+}
+else {
+       # replace frame data with fail state replacement
+       %frame_data = read_data_file(DATA_NOACCESS_PATH());
+       %frame_data = merge_settings(\%default, \%frame_data);
+}
 
 $timer   = int($state{'nextong'}) - $time;
 $ongtime = int($state{'ongtime'});
@@ -266,32 +344,25 @@ else {
        $timer_unlocked = 0;
 }
 
-if (
-               $password_ok || (
-                       (int($state{'state'}) >= STATE->{'waiting'}) &&
-                       ($frame <= int($state{'last'})) &&
-                       ($frame >= 0)
-               )
-       ) {
-       $access = 1;
-}
-else {
-       $access = 0;
-       # replace frame data with fail state replacement
-       %frame_data = read_data_file(DATA_NOACCESS_PATH());
-       %frame_data = merge_settings(\%default, \%frame_data);
-}
-
 $text_mode = int($cgi{'b'});
-if($text_mode > 2) {
+if($text_mode > TEXT_MODE->{'words'}) {
        $text_mode = TEXT_MODE->{'normal'};
 }
+$words_page = int($cgi{'i'});
+
+$words_data_path = join_path(PATH_SEPARATOR(), DATA_WORDS_PATH(), $frame);
+%words_data = read_data_file(
+       $words_data_path, # file
+       '', # encoding
+       0,  # no header
+       ($text_mode != TEXT_MODE->{'words'}), # header only
+       1,  # as list
+);
 
-print "Content-type: text/html\n";
-if(!$access) {
-       print "Status: 403 Forbidden\n";
+if (!$access) {
+       print http_header_status(HTTP_STATUS->{'forbidden'});
 }
-print "\n";
+print "Content-type: text/html; charset=UTF-8\n\n";
 if($method eq 'HEAD') {
        exit;
 }
@@ -308,9 +379,12 @@ print_viewer_page (
                'static'        => 0,
                'show_command'  => $show_command,
                'text_mode'     => $text_mode,
+               'words_page'    => $words_page
        },
        \%state,
        \%settings,
        \%frame_data,
-       \%next_frame_data
+       $access ? \%prev_frame_data : \%frame_data,
+       $access ? \%next_frame_data : \%frame_data,
+       \%words_data,
 );