]> bicyclesonthemoon.info Git - ott/bsta/blobdiff - viewer.1.pl
move merge_settings to common library
[ott/bsta] / viewer.1.pl
index 7afe8a15e66d1ded7b0b2480e940bbadc850b9c4..f0ed2c2524dd6832b42029dbdd9c9ffe5b3c3588 100644 (file)
-###PERL;
-#
+###RUN_PERL: #!/usr/bin/perl
+
 # /bsta/v
 # viewer is generated from viewer.1.pl.
-# 09.01.2023
 #
 # 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
-#    published by the Free Software Foundation, either version 3 of the
-#    License, or (at your option) any later version.
+# 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.
+# 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 <http://www.gnu.org/licenses/>.
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 use strict;
-#use warnings;
-###LIB;
-use bsta_lib qw(failpage gethttpheader getcgi entityencode readdatafile writedatafile printdatafileht urlencode bb2ht bb2bb linehtml);
-use File::Copy;
+use utf8;
+# use Encode::Locale ('decode_argv');
+use Encode ('encode', 'decode');
+
+###PERL_LIB: use lib /botm/lib/bsta
+use botm_common (
+       'HTTP_STATUS',
+       'fail_method', 'fail_content_type', 'redirect',
+       'read_header_env',
+       'url_query_decode',
+       'join_path',
+       'open_encoded', '_x_encoded',
+       'http_header_status',
+       'merge_settings',
+       'merge_url'
+);
+use bsta_lib (
+       'STATE', 'TEXT_MODE', 'INTF_STATE',
+       'get_remote_addr', 'get_frame', 'get_password',
+       'print_viewer_page',
+       'write_index', 'write_static_goto', 'write_static_viewer_page',
+       'ong',
+       'read_frame_data', 'read_default', 'read_noaccess',
+       'read_words_list', 'read_settings', 'read_story', 'read_goto',
+       'read_state', 'write_state',
+       'get_page_file'
+);
+
+###PERL_PATH_SEPARATOR:     PATH_SEPARATOR     = /
+
+###PERL_CGI_PATH:           CGI_PATH           = /bsta/
+###PERL_CGI_GOTO_PATH:      CGI_GOTO_PATH      = /bsta/g
+###PERL_CGI_VIEWER_PATH:    CGI_VIEWER_PATH    = /bsta/v
+
+###PERL_DATA_STATE_PATH:    DATA_STATE_PATH    = /botm/data/bsta/state
+
+###PERL_WWW_PATH:           WWW_PATH           = /botm/www/
+
+binmode STDIN,  ':encoding(UTF-8)';
+binmode STDOUT, ':encoding(UTF-8)';
+binmode STDERR, ':encoding(UTF-8)';
+# decode_argv();
 
-###DATA_PATH;
-###DEFAULT_PATH;
-###SETTINGS_PATH;
-###STATE_PATH;
-###LOGO_PATH;
-###FAVICON_PATH;
-###WEBSITE;
-###WEBSITE_NAME;
-###CSS_PATH;
-###CGI_PATH;
-###FRAME_PATH;
-###VIEWER_PATH;
-###NOACCESS_PATH;
-###STORY_PATH;
-###WWW_PATH;
-###INDEX_PATH;
-###ATTACH_PATH;
-###INFO_PATH;
-###BBCODE_PATH;
-###GOTO_PATH;
-###LIST_PATH;
-###TIMER_PATH;
+my $time = time();
+srand ($time-$$);
 
 my %http;
 my %cgi;
-my %framedata;
-my %nextframedata;
+my %frame_data;
+my %prev_frame_data;
+my %next_frame_data;
 my %default;
 my %settings;
 my %state;
-my %newstate;
-my %gotolist;
-
-my $time = time();
-srand ($time-$$);
+my %new_state;
+my %goto_list;
+my %words_data;
 
 my $method;
 my $frame;
+my $prev_frame_data_path;
+my $next_frame_data_path;
 my $password;
-my $passwordOK;
+my $password_ok;
 my $IP;
 my $access;
-my $seconds;
-my $minutes;
-my $hours;
-my $statefile;
-my $showcommand;
+my $timer;
+my $timer_unlocked;
+my $fh;
+my $show_command;
 my $ongtime;
-my $textmode;
+my $goto;
+my $text_mode;
+my $words_page;
+my $words_data_path;
+my $no_cgi;
+my $force_redirect;
 
 delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
-###PATH;
+###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 ($ENV{'HTTP_X_FORWARDED_FOR'} =~ /^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$/) {
-       $IP=$1;
-}
-elsif ($ENV{'REMOTE_ADDR'} =~ /^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$/) {
-       $IP=$1;
-}
-else {
-       $IP='0.0.0.0';
-}
+$no_cgi = (scalar (keys %cgi) == 0);
 
-if ($cgi{'f'} =~ /^(.+)$/) {
-       $frame=int($1);
-}
-elsif ($ENV{'PATH_INFO'} =~ /^\/(.+)$/) {
-       $frame=int($1);
-}
-else {
-       $frame = 0;
-}
+$IP = get_remote_addr();
+$frame = get_frame(\%cgi);
+$password = get_password(\%cgi);
 
-if ($cgi{'p'} =~ /^(.+)$/) {
-       $password=$1;
-}
-else {
-       $password='';
-}
-# print "Content-type: text/plain\n\n";
+%settings  = read_settings();
+%default   = read_default();
 
-%settings=readdatafile(SETTINGS_PATH);
-%default=readdatafile(DEFAULT_PATH);
-%framedata=readdatafile(DATA_PATH.$frame);
-if($password eq $settings{'password'}){
-       $passwordOK = 1;
+if ($frame >= 0) {
+       %frame_data= read_frame_data($frame);
 }
-else{
-       $passwordOK = 0;
+
+$password_ok = ($password eq $settings{'password'});
+
+if (
+       (defined $cgi{'f'}) &&
+       ($cgi{'f'} eq '') &&
+       ($cgi{'g'} ne '')
+) { # GOTO with no value
+       my $goto_url = CGI_GOTO_PATH();
+       if ($password_ok) {
+               $goto_url = merge_url(
+                       {'path' => $goto_url},
+                       {'query' => {'p' => $password}}
+               );
+       }
+       exit redirect($method, $goto_url, HTTP_STATUS->{'see_other'});
 }
 
-if (open ($statefile,"+<",STATE_PATH)){
-       if (flock($statefile,2)) {
+
+# state & activation logic
+if (open_encoded($fh, "+<:encoding(UTF-8)", DATA_STATE_PATH())) {
+       if (flock($fh, 2)) {
                
-               %state=readdatafile($statefile);
+               %state = read_state($fh);
                
-               if($frame<0) {
+               if ($frame < 0) {
                        $frame = int($state{'last'}) + $frame +1;
-                       %framedata=readdatafile(DATA_PATH.$frame);
+                       if ($frame >= 0) {
+                               $force_redirect = 1;
+                               %frame_data = read_frame_data($frame);
+                       }
                }
                
-               if(int($state{'state'})==1 && $frame == int($state{'last'}) && $method ne 'HEAD' && !$passwordOK){
-                       my %newstate=%state;
-                       if($state{'ip1'} ne $IP) {
+               if (
+                       (int($state{'state'}) == STATE->{'waiting'}) &&
+                       ($frame == int($state{'last'})) &&
+                       ($method ne 'HEAD') &&
+                       (!$password_ok)
+               ) {
+                       # register IP for progress
+                       my %new_state = %state;
+                       unless (
+                               ($state{'ip1'} eq $IP) ||
+                               ($state{'ip2'} eq $IP) ||
+                               ($state{'ip3'} eq $IP)
+                       )
+                       {
                                if ($state{'ip1'} eq '') {
-                                       $newstate{'ip1'} = $IP;
-                                       writedatafile($statefile,%newstate);
+                                       $new_state{'ip1'} = $IP;
                                }
-                               elsif($state{'ip2'} ne $IP) {
-                                       if ($state{'ip2'} eq '') {
-                                               $newstate{'ip2'} = $IP;
-                                               writedatafile($statefile,%newstate);
-                                       }
-                                       else {
-                                               $newstate{'state'}=2;
-                                               $newstate{'ip3'} = $IP;
-                                               writedatafile($statefile,%newstate);
-                                       }
+                               elsif ($state{'ip2'} eq '') {
+                                       $new_state{'ip2'} = $IP;
                                }
+                               elsif ($state{'ip3'} eq '') {
+                                       $new_state{'ip3'} = $IP;
+                                       $new_state{'state'} = STATE->{'ready'};
+                               }
+                               else {
+                                       $new_state{'state'} = STATE->{'ready'};
+                               }
+                               if ($new_state{'state'} == STATE->{'ready'}) {
+                                       write_static_goto(\%new_state, \%settings, '');
+                                       write_static_viewer_page(
+                                               $frame-1,
+                                               \%new_state,
+                                               \%settings,
+                                               \%default,
+                                               '', # frame data
+                                               '', # prev frame data
+                                               \%frame_data, # next frame data,
+                                               '' # words data
+                                       );
+                               }
+                               write_state($fh, \%new_state);
                        }
                }
-               elsif(int($state{'state'})==0 && $frame == 1) {
+               elsif (
+                       (int($state{'state'}) == STATE->{'inactive'}) &&
+                       ($frame == 1) &&
+                       (!$password_ok)
+               ) {
+                       # ready to activate?
+                       # NOTE: at this point frame 0 is already ONGed.
                        my %story;
-                       my $inpath;
-                       my $outpath;
+                       my $ong_time = int($settings{'firstongtime'});
+                       my $r;
                        
-                       %story = readdatafile(STORY_PATH);
-                       %gotolist=readdatafile(LIST_PATH);
-                       if(int($story{'state'}) == 17 && int($story{'pass'}) == 1) {
-                               #ACTIVATE!
-                               
-                               $framedata{'ongtime'} = $time;
-                               writedatafile(DATA_PATH.$frame,%framedata);
-                               $state{'state'} = 1;
-                               $state{'last'} = 1;
-                               $state {'ip1'} = '0.0.0.0';
-                               $state {'ip2'} = '0.0.0.0';
-                               $state {'ip3'} = '';
-                               $state {'nextong'} = (int($time / 3600) + int($settings{'firstongtime'})) * 3600 ;
-                               $state{'ongtime'} = int($settings{'firstongtime'});
-                               
-                               unless(defined($framedata{'ext'})){
-                                       $framedata{'ext'}=$default{'ext'};
-                               }
+                       %story     = read_story();
+                       %goto_list = read_goto();
+                       
+                       if (
+                               (int($story{'state'}) == INTF_STATE->{'>|'} ) &&
+                               (int($story{'pass'}) == 1)
+                       ) {
+                               # conditions met; ACTIVATE!
                                
-                               $inpath = DATA_PATH.sprintf($settings{'frame'},$frame,$framedata{'ext'});
-                               $outpath = WWW_PATH.sprintf($settings{'frame'},$frame,$framedata{'ext'});
+                               # set initial state
+                               $state{'state'} = STATE->{'waiting'};
+                               $state{'last'}  = 1; 
+                               $state{'ip1'}   = '0.0.0.0';
+                               $state{'ip2'}   = '0.0.0.0';
+                               $state{'ip3'}   = '';
+                               $state{'nextong'} = (int($time / 3600) + int($settings{'firstongtime'})) * 3600 ;
+                               $state{'ongtime'} = $ong_time;
                                
-                               $gotolist{'title-1'}=$framedata{'title'};
-                               $gotolist{'ongtime-1'}=$framedata{'ongtime'};
+                               # prepare to ONG frame 1
                                
-                               if(copy ($inpath, $outpath)) {
-                                       writeindex(INDEX_PATH);
-                                       writedatafile($statefile,%state);
-                                       writedatafile(LIST_PATH,%gotolist);
+                               $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 {
-                                       $state{'state'} = 0;
+                               if ($r) {
+                                       $r = write_static_goto(\%state, \%settings, \%goto_list);
+                               }
+                               if ($r) {
+                                       $r = write_state($fh, \%state);
+                               }
+                               unless ($r) {
+                                       # FAILED ONG! Story as if it was inactive!
+                                       $state{'state'} = STATE->{'inactive'};
                                }
                        }
                }
        }
        else {
-               $state{'state'} = 0;
+               # FAILED GET STATE! Story as if it was inactive!
+               $state{'state'} = STATE->{'inactive'};
        }
-       close ($statefile);
+       close ($fh);
 }
 else {
-       $state{'state'} = 0;
-}
-
-%nextframedata=readdatafile(DATA_PATH.($frame+1));
-foreach my $ind (keys %default) {
-       unless(defined($framedata{$ind})){
-               $framedata{$ind}=$default{$ind};
-       }
-       unless(defined($nextframedata{$ind})){
-               $nextframedata{$ind}=$default{$ind};
+       $state{'state'} = STATE->{'inactive'};
+}
+
+$access = (
+       $password_ok || (
+               (int($state{'state'}) >= STATE->{'waiting'}) &&
+               ($frame <= int($state{'last'})) &&
+               ($frame >= 0)
+       )
+);
+
+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 = get_page_file($frame, \%frame_data, \%settings);
+                       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'});
+                       }
+               }
        }
-}
-
-$hours=int($state{'nextong'})-$time;
-$ongtime=int($state{'ongtime'});
-if($ongtime == 0) {
-       $ongtime=int($settings{'ongtime'})
-}
-
-$showcommand = ($hours < ($ongtime*3600/3));
-if($hours>0){
-       $seconds = sprintf('%02d',$hours % 60);
-       $hours = int($hours/60);
-       $minutes = sprintf('%02d',$hours % 60);
-       $hours = sprintf('%02d',int($hours/60));
-}
-elsif(($hours>=-15) && (int($state{'state'}) == 2)){
-       $seconds='NG';
-       $minutes='00';
-       $hours='00';
-}
-else{
-       $seconds='EE';
-       $minutes='EE';
-       $hours='EE';
-}
-
-
-if ($passwordOK || (int($state{'state'}) >= 1 && $frame <= int($state{'last'}) && $frame >= 0)) {
-       $access=1;
-}
-else {
-       $access=0;
-       %framedata = readdatafile(NOACCESS_PATH);
-       foreach my $ind (keys %default) {
-               unless(defined($framedata{$ind})){
-                       $framedata{$ind}=$default{$ind};
+       if ($force_redirect) {
+               my $redirect_url = merge_url(
+                       {'path' => CGI_VIEWER_PATH()},
+                       {'path' => $frame}
+               );
+               unless ($no_cgi) {
+                       delete $cgi{'f'}; # to avoid infinite loop
+                       $redirect_url = merge_url(
+                               {'path' => $redirect_url},
+                               {'query' => \%cgi}
+                       );
                }
+               exit redirect($method, $redirect_url, HTTP_STATUS->{'see_other'});
        }
-}
-
-$textmode = int($cgi{'b'});
-if($textmode > 2) {
-       $textmode = 0;
-}
-
-# print "Content-type: text/plain\n\n";
-# print 'frame='.$frame."\n";
-# print 'password='.$password."\n";
-# print 'passwordOK='.$passwordOK."\n";
-# print 'access='.$access."\n";
-# print "\n>>>ENV<<<\n\n";
-# foreach my $ind (keys %ENV) {
-       # print $ind.'='.$ENV{$ind}."\n";
-# }
-# print "\n>>>HTTP<<<\n\n";
-# foreach my $ind (keys %http) {
-       # print $ind.': '.$http{$ind}."\n";
-# }
-# print "\n>>>CGI<<<\n\n";
-# foreach my $ind (keys %cgi) {
-       # print $ind.'='.$cgi{$ind}."\n";
-# }
-# print "\n>>>FRAMEDATA<<<\n\n";
-# foreach my $ind (keys %framedata) {
-       # print $ind.': '.$framedata{$ind}."\n";
-# }
-# print "\n>>>NEXTFRAMEDATA<<<\n\n";
-# foreach my $ind (keys %nextframedata) {
-       # print $ind.': '.$nextframedata{$ind}."\n";
-# }
-# print "\n>>>SETTINGS<<<\n\n";
-# foreach my $ind (keys %settings) {
-       # print $ind.': '.$settings{$ind}."\n";
-# }
-# print "\n>>>STATE<<<\n\n";
-# foreach my $ind (keys %state) {
-       # print $ind.': '.$state{$ind}."\n";
-# }
-
-print "Content-type: text/html\n";
-if(!$access) {
-       print "Status: 403 Forbidden\n";
-}
-print "\n";
-if($method eq 'HEAD') {
-       exit;
-}
-
-print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "">'."\n";
-print '<html lang="en"><head>'."\n";
-print '<meta http-equiv="Content-type" content="text/html; charset=UTF-8">'."\n";
-print '<title>';
-unless($framedata{'title'} eq '' || $framedata{'title'} eq $settings{'story'}) {
-       print entityencode($framedata{'title'}).' &bull; ';
-}
-print entityencode($settings{'story'}).' &bull; '.WEBSITE_NAME.'</title>'."\n";
-print '<link rel="icon" type="image/png" href="'.FAVICON_PATH.'">'."\n";
-print '<link rel="stylesheet" href="'.CSS_PATH.'">'."\n";
-print '<link rel="index" href="'.GOTO_PATH.($passwordOK?('?p='.urlencode($password)):'').'">'."\n";
-print '<link rel="start" href="'.VIEWER_PATH.'/0'.($passwordOK?('?p='.urlencode($password)):'').'">'."\n";
-if($frame>0 && $access) {
-       print '<link rel="prev" href="'.VIEWER_PATH.'/'.($frame - 1).($passwordOK?('?p='.urlencode($password)):'').'">'."\n";
-}
-if ($passwordOK || $frame<int($state{'last'})) {
-       print '<link rel="next" href="'.VIEWER_PATH.'/'.($frame + 1).($passwordOK?('?p='.urlencode($password)):'').'">'."\n";
-       print '<link rel="prefetch" href="'.VIEWER_PATH.'/'.($frame + 1).($passwordOK?('?p='.urlencode($password)):'').'">'."\n";
-}
-if($frame==int($state{'last'}) && int($state{'state'}) >= 1 && int($state{'state'}) < 3) {
-       print '<!-- <script src="'.TIMER_PATH.'"></script> -->'."\n";
-}
-print '</head><body>'."\n";
-print '<a href="/"><img id="botmlogo" src="'.LOGO_PATH.'" alt="'.WEBSITE.'"></a>'."\n";
-print '<div id="all">'."\n";
-
-print '<div id="inst" class="ins">'."\n";
-
-print '<div id="title">'."\n";
-print '<H1 id="titletext">'.entityencode($framedata{'title'}).'</H1>'."\n";
-print '</div>'."\n";
-
-print '</div><div id="framespace">'."\n";
-if(!$access) {
-       print '<img src="'.CGI_PATH.$framedata{'frame'}.'" id ="frame" alt="'.$frame.'" title="'.entityencode($framedata{'title'}).'">'."\n";
-}
-else {
-       print'<img src="';
-       if($frame<=int($state{'last'}) && int($state{'state'}) >= 1) {
-               print CGI_PATH.sprintf($settings{'frame'},$frame,$framedata{'ext'});
+       
+       if ($frame > 0) {
+               %prev_frame_data = read_frame_data($frame-1, \%default);
        }
        else {
-               print FRAME_PATH.'/'.$frame.($passwordOK?('?p='.urlencode($password)):'');
+               %prev_frame_data = %default;
        }
-       print '" id="frame" alt="'.$frame.'" title="'.entityencode($framedata{'title'}).'">'."\n";
+       %next_frame_data = read_frame_data($frame+1, \%default);
+       %frame_data      = merge_settings(\%default, \%frame_data);
 }
-print '</div><div id="insb" class="ins">'."\n";
-
-if($textmode==2){
-       print '<div id="chat">'."\n";
-       
-       if ($passwordOK || $frame<int($state{'last'}) || (int($state{'state'}) >= 2 && $showcommand)) {
-               $framedata{'command'}=$nextframedata{'title'};
-       }
-       if ($access) {
-               $framedata{'frame'}=sprintf($settings{'frame'},$frame,$framedata{'ext'});
-       }
-       
-       printdatafileht(%framedata);
-       print '</div>'."\n";
-}
-elsif($textmode==1){
-       print '<div id="chat">'."\n";
-       print '[quote][center][size=200]'.entityencode($framedata{'title'}).'[/size]<br>'."\n";
-       print '[url=http://'.WEBSITE.VIEWER_PATH.'/'.$frame.'][img]http://'.WEBSITE.CGI_PATH.($access?sprintf($settings{'frame'},$frame,$framedata{'ext'}):$framedata{'frame'}).'[/img][/url][/center]<br>'."\n";
-       print bb2bbf($framedata{'content'}).'[/quote]</div>'."\n";
+else {
+       # replace frame data with fail state replacement
+       %frame_data = read_noaccess(\%default);
 }
-elsif($framedata{'content'} ne ''){
-       print '<div id="undertext">'."\n";
-       print bb2htf($framedata{'content'})."\n";
-       print '</div>'."\n";
+
+$timer   = int($state{'nextong'}) - $time;
+$ongtime = int($state{'ongtime'});
+if($ongtime == 0) {
+       $ongtime = int($settings{'ongtime'})
 }
 
-print '<div id="command">'."\n";
-if($frame==int($state{'last'}) && int($state{'state'}) >= 1 && int($state{'state'}) < 3) {
-       print '[<span id="ongh" class="';
-       if(int($state{'state'}) >= 2 || $state{'ip1'} ne '') {
-               print 'br';
-       }
-       else {
-               print 'ni';
-       }
-       print '">'.$hours.'</span>:<span id="ongm" class="';
-       if(int($state{'state'}) >= 2 || $state{'ip2'} ne '') {
-               print 'br';
-       }
-       else {
-               print 'ni';
-       }
-       print '">'.$minutes.'</span>:<span id="ongs" class="';
-       if(int($state{'state'}) >= 2 || $state{'ip3'} ne '') {
-               print 'br';
-       }
-       else {
-               print 'ni';
-       }
-       print '">'.$seconds.'</span>]<br>';
+$show_command = ($timer < ($ongtime*3600/3));
+if ($state{'state'} >= STATE->{'ready'}) {
+       $timer_unlocked = 3;
 }
-print '&gt;';
-if (!$access){
-       print '<a href="'.VIEWER_PATH.'/-1">'.entityencode($framedata{'command'}).'</a><br>'."\n";
+elsif ($state{'ip3'} ne '') {
+       $timer_unlocked = 3;
 }
-else {
-       if ($passwordOK || $frame<int($state{'last'}) || (int($state{'state'}) >= 2 && $showcommand)) {
-               if ($passwordOK || $frame<int($state{'last'})) {
-                       print '<a href="'.VIEWER_PATH.'/'.($frame + 1).($passwordOK?('?p='.urlencode($password)):'').'">';
-               }
-               if($nextframedata{'title'} ne '') {
-                       print entityencode($nextframedata{'title'});
-               }
-               elsif ($passwordOK || $frame<int($state{'last'})) {
-                       print'<span class="inp">_</span>';
-               }
-               if ($passwordOK || $frame<int($state{'last'})) {
-                       print '</a>';
-               }
-               else {
-                       print'<span class="inp">_</span>';
-               }
-       }
-       else {
-               print'<span class="inp">_</span>';
-       }
-       print '<br>'."\n";
+elsif ($state{'ip2'} ne '') {
+       $timer_unlocked = 2;
 }
-print '</div>'."\n";
-
-print '<div id="underlinks">'."\n";
-print '<a href="'.CGI_PATH.'">Once again</a>';
-if($frame>0 && $access) {
-       print ' | <a href="'.VIEWER_PATH.'/'.($frame-1).($passwordOK?('?p='.urlencode($password)):'').'">Before</a>';
+elsif ($state{'ip1'} ne '') {
+       $timer_unlocked = 1;
 }
-if($frame != int($state{'last'})) {
-       print ' | <a href="'.VIEWER_PATH.'/'.int($state{'last'}).($passwordOK?('?p='.urlencode($password)):'').'">Now</a>';
+else {
+       $timer_unlocked = 0;
 }
-print ' | <a href="'.GOTO_PATH.($passwordOK?('?p='.urlencode($password)):'').'">GOTO</a>'."\n";
-print '<span style="float: right;">'."\n";
 
-if ($textmode!=0) {
-       print '<a href="'.VIEWER_PATH.'/'.$frame.($passwordOK?('?p='.urlencode($password)):'').'">Without</a> | ';
+$text_mode = int($cgi{'b'});
+if($text_mode > TEXT_MODE->{'words'}) {
+       $text_mode = TEXT_MODE->{'normal'};
 }
+$words_page = int($cgi{'i'});
+$goto = int($cgi{'g'});
 
-print '<a href="'.(($textmode==2)?(INFO_PATH.'/'.$frame.($passwordOK?('?p='.urlencode($password)):'')):(VIEWER_PATH.'/'.$frame.'?b=2'.($passwordOK?('&amp;p='.urlencode($password)):''))).'">Info</a>';
-print ' | <a href="'.(($textmode==1)?(BBCODE_PATH.'/'.$frame.($passwordOK?('?p='.urlencode($password)):'')):(VIEWER_PATH.'/'.$frame.'?b=1'.($passwordOK?('&amp;p='.urlencode($password)):''))).'">BB</a>';
-print "\n</span>\n";
-
-print '</div>'."\n";
+%words_data = read_words_list(
+       $frame,
+       ($text_mode != TEXT_MODE->{'words'})
+);
 
-print '</div>'."\n";
-
-print '</div>'."\n";
-print '<a href="/" class="cz">'.WEBSITE.'</a>'."\n";
-
-print '</body></html>'."\n";
-
-sub writeindex {
-       (my $indexpath) = @_;
-       my $indexfile;
-       my %framedata;
-       my %nextframedata;
-       my %default;
-       
-       if(ref($indexpath)) {
-               $indexfile=$indexpath;
-               unless (seek($indexfile, 0, 0)) {
-                       return 0;
-               }
-       }
-       else {
-               unless (open ($indexfile, ">", $indexpath)) {
-                       return 0;
-               }
-       }
-       
-       %framedata = readdatafile(DATA_PATH.0);
-       %nextframedata = readdatafile(DATA_PATH.1);
-       %default=readdatafile(DEFAULT_PATH);
-       
-       foreach my $ind (keys %default) {
-               unless(defined($framedata{$ind})){
-                       $framedata{$ind}=$default{$ind};
-               }
-               unless(defined($nextframedata{$ind})){
-                       $nextframedata{$ind}=$default{$ind};
-               }
-       }
-       
-       print $indexfile '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "">'."\n";
-       print $indexfile '<html lang="en"><head>'."\n";
-       print $indexfile '<meta http-equiv="Content-type" content="text/html; charset=UTF-8">'."\n";
-       print $indexfile '<title>'.entityencode($settings{'story'}).' &bull; '.WEBSITE_NAME.'</title>'."\n";
-       print $indexfile '<link rel="icon" type="image/png" href="'.FAVICON_PATH.'">'."\n";
-       print $indexfile '<link rel="stylesheet" href="'.CSS_PATH.'">'."\n";
-       print $indexfile '<link rel="index" href="'.GOTO_PATH.'">'."\n";
-       print $indexfile '<link rel="start" href="'.VIEWER_PATH.'/0">'."\n";
-       print $indexfile '<link rel="next" href="'.VIEWER_PATH.'/1">'."\n";
-       print $indexfile '<link rel="prefetch" href="'.VIEWER_PATH.'/1">'."\n";
-       print $indexfile '</head><body>'."\n";
-       print $indexfile '<a href="/"><img id="botmlogo" src="'.LOGO_PATH.'" alt="'.WEBSITE.'"></a>'."\n";
-       print $indexfile '<div id="all">'."\n";
-       
-       print $indexfile '<div id="inst" class="ins">'."\n";
-       
-       print $indexfile '<div id="title">'."\n";
-       print $indexfile '<H1 id="titletext">'.entityencode($settings{'story'}).'</H1>'."\n";
-       print $indexfile '</div>'."\n";
-       
-       print $indexfile '</div><div id="framespace">'."\n";
-       print $indexfile '<img src="'.CGI_PATH.sprintf($settings{'frame'},0,$framedata{'ext'}).'" title="'.entityencode($framedata{'title'}).'" alt="0" id="frame">'."\n";
-       
-       print $indexfile '</div><div id="insb" class="ins">'."\n";
-       print $indexfile '<div id="undertext">'."\n";
-       print $indexfile bb2htf($framedata{'content'})."\n";
-       print $indexfile '</div>'."\n";
-       
-       print $indexfile '<div id="command">'."\n";
-       print $indexfile '&gt;<a href="'.VIEWER_PATH.'/1">'.entityencode($nextframedata{'title'}).'</a>'."\n";
-       print $indexfile '</div>'."\n";
-       
-       print $indexfile '<div id="underlinks">'."\n";
-
-       
-       # <span style="float: right;"><a href="/bsta/v/0?b=2">Info</a> | <a href="/bsta/v/0?b=1">BB</a></span>
-       
-       print $indexfile '<a href="'.VIEWER_PATH.'/-1">Now</a> | <a href="'.GOTO_PATH.'">GOTO</a>';
-       print $indexfile '<span style="float: right;"><a href="'.INFO_PATH.'/0?b=2">Info</a> | <a href="'.VIEWER_PATH.'/0?b=1">BB</a></span>'."\n";
-       print $indexfile '</div>'."\n";
-       
-       print $indexfile '</div>'."\n";
-       
-       print $indexfile '</div>'."\n";
-       print $indexfile '<a href="/" class="cz">'.WEBSITE.'</a>'."\n";
-       
-       print $indexfile '</body></html>'."\n";
-       
-       unless (ref($indexpath)) {
-               close ($indexfile);
-       }
-       else {
-               truncate ($indexfile , tell($indexfile));
-       }
-       
-       return 1;
+if (!$access) {
+       print http_header_status(HTTP_STATUS->{'forbidden'});
 }
-
-sub bb2htf {
-       (my $bb) = @_;
-       my $tag;
-       my $tagvalue;
-       my $pretext;
-       my $posttext;
-       
-       while($bb =~ m/(###([^#;]*);)/g) {
-               $tag = $1;
-               $tagvalue = $2;
-               $pretext = substr($bb,0,pos ($bb)-length($tag));
-               $posttext = substr ($bb,pos ($bb));
-               
-               if ($tagvalue =~ /^att&([0-9]+)$/) {
-                       $tagvalue = ATTACH_PATH.'/'.int($1).($passwordOK?('?p='.urlencode($password)):'');
-               }
-               elsif ($tagvalue =~ /^vw&([0-9]+)$/) {
-                       $tagvalue = VIEWER_PATH.'/'.int($1).($passwordOK?('?p='.urlencode($password)):'');
-               }
-               elsif ($tagvalue =~ /^fr&([0-9]+)$/) {
-                       $tagvalue = FRAME_PATH.'/'.int($1).($passwordOK?('?p='.urlencode($password)):'');
-               }
-               else {
-                       $tagvalue = '';
-               }
-               
-               $bb = $pretext.$tagvalue.$posttext;
-       }
-       
-       return bb2ht ($bb);
+print "Content-type: text/html; charset=UTF-8\n\n";
+if($method eq 'HEAD') {
+       exit;
 }
 
-sub bb2bbf {
-       (my $bb) = @_;
-       my $tag;
-       my $tagvalue;
-       my $pretext;
-       my $posttext;
-       
-       while($bb =~ m/(###([^#;]*);)/g) {
-               $tag = $1;
-               $tagvalue = $2;
-               $pretext = substr($bb,0,pos ($bb)-length($tag));
-               $posttext = substr ($bb,pos ($bb));
-               
-               if ($tagvalue =~ /^att&([0-9]+)$/) {
-                       $tagvalue = 'http://'.WEBSITE.ATTACH_PATH.'/'.int($1);
-               }
-               elsif ($tagvalue =~ /^vw&([0-9]+)$/) {
-                       $tagvalue = 'http://'.WEBSITE.VIEWER_PATH.'/'.int($1);
-               }
-               elsif ($tagvalue =~ /^fr&([0-9]+)$/) {
-                       $tagvalue = 'http://'.WEBSITE.FRAME_PATH.'/'.int($1);
-               }
-               else {
-                       $tagvalue = '';
-               }
-               
-               $bb = $pretext.$tagvalue.$posttext;
-       }
-       
-       return linehtml(bb2bb ($bb));
-}
+print_viewer_page (
+       \*STDOUT,
+       {
+               'launch'        => 0,
+               'frame'         => $frame,
+               'access'        => $access,
+               'password_ok'   => $password_ok,
+               'timer_unlocked'=> $timer_unlocked,
+               'timer'         => $timer,
+               'static'        => 0,
+               'show_command'  => $show_command,
+               'text_mode'     => $text_mode,
+               'words_page'    => $words_page,
+               'goto'          => $goto
+       },
+       \%state,
+       \%settings,
+       \%frame_data,
+       $access ? \%prev_frame_data : \%frame_data,
+       $access ? \%next_frame_data : \%frame_data,
+       \%words_data,
+);