]> bicyclesonthemoon.info Git - ott/bsta/commitdiff
Viewer ready, not tested
authorb <rowerynaksiezycu@gmail.com>
Sun, 8 Oct 2023 22:15:36 +0000 (22:15 +0000)
committerb <rowerynaksiezycu@gmail.com>
Sun, 8 Oct 2023 22:15:36 +0000 (22:15 +0000)
bsta_lib.1.pm
settings.txt
viewer.1.pl

index 2bcf82efcda4c29ebf98fd025c15c9acb656a34f..4d5450cb040b47c6cf3d110cc66bbc38c20161e6 100644 (file)
@@ -30,13 +30,18 @@ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
 our @ISA         = qw(Exporter);
 our @EXPORT      = ();
 our @EXPORT_OK   = (
-       'STATE',
-       'entityencode', # TO REMOVE
+       'STATE', 'TEXT_MODE',
        'failpage', 'fail_method', 'fail_content_type',
+       'get_remote_addr', 'get_frame', 'get_password',
+       'merge_settings',
+       'print_viewer_page',
+       'write_index',
+       
+       'readdatafile', 'writedatafile', 'printdatafile', # TO REMOVE
+       'entityencode', # TO REMOVE
+       'printdatafileht', # TO REMOVE ???
        'gethttpheader', 'getcgi', # TO REMOVE
        'urldecode', # TO REMOVE
-       'readdatafile', 'writedatafile', 'printdatafile',
-       'printdatafileht', # TO REMOVE ???
        'urlencode', # TO REMOVE
        'linehtml', # TO REMOVE
        'bb2ht', 'bb2bb' # TO REMOVE
@@ -52,11 +57,19 @@ use botm_common (
        'read_data_file', 'write_data_file'
 );
 
+###PERL_CGI_PATH:           CGI_PATH           = /bsta/
+###PERL_CGI_BBCODE_PATH:    CGI_BBCODE_PATH    = /bsta/b
+###PERL_CGI_CSS_PATH:       CGI_CSS_PATH       = /bsta/bsta.css
+###PERL_CGI_FRAME_PATH:     CGI_FRAME_PATH     = /bsta/f
 ###PERL_CGI_GOTO_PATH:      CGI_GOTO_PATH      = /bsta/g
+###PERL_CGI_INFO_PATH:      CGI_INFO_PATH      = /bsta/i
 ###PERL_CGI_LOGO_PATH:      CGI_LOGO_PATH      = /bsta/botmlogo.png
+###PERL_CGI_TIMER_PATH:     CGI_TIMER_PATH     = /bsta/timer.js
+###PERL_CGI_VIEWER_PATH:    CGI_VIEWER_PATH    = /bsta/v
 
+###PERL_SCHEME:             SCHEME             = http
 ###PERL_WEBSITE:            WEBSITE            = 1190.bicyclesonthemoon.info
-###PERL_SCHEME:             SCHEME             = 1190.bicyclesonthemoon.info
+###PERL_FAVICON_PATH:       FAVICON_PATH       = /img/favicon.png
 
 
 use constant STATE => {
@@ -192,6 +205,56 @@ sub fail_content_type
        );
 }
 
+# function to obtain address of remote agent
+sub get_remote_addr {
+       if ($ENV{'HTTP_X_FORWARDED_FOR'} =~ /^.+$/) {
+               return $&;
+       }
+       elsif ($ENV{'REMOTE_ADDR'} =~ /^.+$/) {
+               return $&;
+       }
+       else {
+               return '0.0.0.0';
+       }
+}
+
+# function to obtain frame number
+sub get_frame {
+       (my $cgi) = @_;
+       
+       if ($cgi{'f'} =~ /^.+$/) {
+               return int($&);
+       }
+       elsif ($ENV{'PATH_INFO'} =~ /^\/(.+)$/) {
+               return int($1);
+       }
+       else {
+               return 0;
+       }
+}
+
+# function to obtain password
+sub get_password {
+       (my $cgi) = @_;
+       
+       if ($cgi{'p'} =~ /^.+$/) {
+               return $&;
+       }
+       else {
+               return '';
+       }
+}
+
+sub merge_settings {
+               my %final_settings;
+       
+       foreach my $settings (@_) {
+               foreach my $ind (keys %$settings) {
+                       $final_settings{$ind} = $settings->{$ind};
+               }
+       }
+       return %final_settings;
+}
 
 # TO REMOVE
 # function to encode entities, decimal, 
@@ -738,7 +801,6 @@ sub print_html_data {
 sub print_viewer_page {
        (
                my $file,
-               my $frame,
                my $context,
                my $state,
                my $settings,
@@ -751,11 +813,14 @@ sub print_viewer_page {
        # my $prev_frame = $frame - 1;
        my $next_frame = $frame + 1;
        
+       my $title   =      $frame_data->{'title'};
+       my $command = $next_frame_data->{'title'};
+       
        my $access      = $context->{'access'};
        my $password_ok = $context->{'password_ok'};
-       my $text_mode   = $context->{'text_mode'}
        my $static      = $context->{'static'};
        
+       my $text_mode      = int($context->{'text_mode'});
        my $timer_unlocked = int($context->{'timer_unlocked'});
        my $timer          = int($context->{'timer'};
        
@@ -808,6 +873,8 @@ sub print_viewer_page {
                        $context->{'show_command'}
                )
        );
+       my $show_command_link = ($next_available || (!$access));
+       my $show_command_cursor = (($frame == $last_frame) || ($command eq ''));
        my $frame_indirect = !(
                (!$access) || (
                        ($frame <= $last_frame) &&
@@ -818,13 +885,20 @@ sub print_viewer_page {
        
        my $password_query;
        
-       my $goto_url = CGI_GOTO_PATH;
-       my $timer_url = CGI_TIMER_PATH;
+       my $base_url   = CGI_PATH;
+       my $goto_url   = CGI_GOTO_PATH;
+       my $info_url   = CGI_INFO_PATH;
+       my $bbcode_url = CGI_BBCODE_PATH;
+       my $timer_url  = CGI_TIMER_PATH;
        my $viewer_full_url = merge_url(
                {'scheme' => SCHEME(), 'host' => WEBSITE()},
                {'path' => CGI_VIEWER_PATH()},
                {'path' => $frame}
        );
+       my $viewer_url = merge_url(
+               {'path' => CGI_VIEWER_PATH()},
+               {'path' => $frame}
+       );
        my $viewer_0_url = merge_url(
                {'path' => CGI_VIEWER_PATH()},
                {'path' => 0}
@@ -837,6 +911,26 @@ sub print_viewer_page {
                {'path' => CGI_VIEWER_PATH()},
                {'path' => $next_frame}
        );
+       my $viewer_last_url = merge_url(
+               {'path' => CGI_VIEWER_PATH()},
+               {'path' => ($static ? -1 : $last_frame)}
+       );
+       if ($text_mode != TEXT_MODE->{'bb'}) {
+               $bbcode_url = merge_url(
+                       $viewer_url,
+                       {'query'=>{
+                               'b' = TEXT_MODE->{'bb'}
+                       }}
+               );
+       }
+       if ($text_mode != TEXT_MODE->{'info'}) {
+               $info_url = merge_url(
+                       $viewer_url,
+                       {'query'=>{
+                               'b' = TEXT_MODE->{'info'}
+                       }}
+               );
+       }
        my $frame_file = '';
        my $frame_url;
        my $frame_next_url;
@@ -886,9 +980,13 @@ sub print_viewer_page {
        if ($password_ok) {
                $password_query = url_query_encode({'p', $settings->{'password'}});
                $goto_url        = merge_url($goto_url       , {'query' => $password_query});
+               $info_url        = merge_url($info_url       , {'query' => $password_query});
+               $bbcode_url      = merge_url($bbcode_url     , {'query' => $password_query});
+               $viewer_url      = merge_url($viewer_url     , {'query' => $password_query});
                $viewer_0_url    = merge_url($viewer_0_url   , {'query' => $password_query});
                $viewer_prev_url = merge_url($viewer_prev_url, {'query' => $password_query});
                $viewer_next_url = merge_url($viewer_next_url, {'query' => $password_query});
+               $viewer_last_url = merge_url($viewer_last_url, {'query' => $password_query});
                if ($frame_indirect) {
                        $frame_url = merge_url($frame_url     , {'query' => $password_query});
                }
@@ -896,21 +994,27 @@ sub print_viewer_page {
                        $frame_url = merge_url($frame_next_url, {'query' => $password_query});
                }
        }
+       my $_base_url        = html_entity_encode_dec($base_url       , 1);
        my $_goto_url        = html_entity_encode_dec($goto_url       , 1);
+       my $_info_url        = html_entity_encode_dec($info_url       , 1);
+       my $_bbcode_url      = html_entity_encode_dec($bbcode_url     , 1);
        my $_timer_url       = html_entity_encode_dec($timer_url      , 1);
        my $_viewer_full_url = html_entity_encode_dec($viewer_full_url, 1);
+       my $_viewer_url      = html_entity_encode_dec($viewer_url     , 1);
        my $_viewer_0_url    = html_entity_encode_dec($viewer_0_url   , 1);
        my $_viewer_prev_url = html_entity_encode_dec($viewer_prev_url, 1);
        my $_viewer_next_url = html_entity_encode_dec($viewer_next_url, 1);
+       my $_viewer_last_url = html_entity_encode_dec($viewer_last_url, 1);
        my $_frame_url       = html_entity_encode_dec($frame_url      , 1);
        my $_frame_next_url  = html_entity_encode_dec($frame_next_url , 1);
        my $_frame_full_url  = html_entity_encode_dec($frame_full_url , 1);
        
-       my $_title = html_entity_encode_dec($framedata->{'title'}, 1);
+       my $_title   = html_entity_encode_dec($title,   1);
+       my $_command = html_entity_encode_dec($command, 1);
        
        if ($text_mode == TEXT_MODE->{'info'}) {
                if ($show_command) {
-                       $frame_data->{'command'} = $next_frame_data->{'title'};
+                       $frame_data->{'command'} = $command;
                }
                if ($context->{'access'}) {
                        $frame_data->{'frame'} = $frame_file;
@@ -991,8 +1095,44 @@ sub print_viewer_page {
        if ($show_timer) {
                print $fh '[<span id="ongh" class="'.$timer_color_h.'">'.$timer_h.'</span>';
                print $fh ':<span id="ongm" class="'.$timer_color_m.'">'.$timer_m.'</span>';
-               print $fh ':<span id="ongm" class="'.$timer_color_s.'">'.$timer_s.'</span>]';
+               print $fh ':<span id="ongs" class="'.$timer_color_s.'">'.$timer_s.'</span>]<br>'."\n";
+       }
+       print '&gt';
+       if ($show_command_link) {
+               print $fh '<a href="'.($access : $_viewer_next_url : $_viewer_last_url).'">';
+       }
+       if ($show_command) {
+               print $fh $_command;
+       }
+       if ($show_command_cursor) {
+               print $fh '<span class="inp">_</span>';
+       }
+       if ($show_command_link) {
+               print $fh '</a>';
+       }
+       print $fh "<br>\n</div>\n";
+       
+       print $fh '<div id="underlinks">'."\n";
+       
+       unless (($frame == 0) && $Static) {
+               print $fh '<a href="'.$_base_url.'">Once again</a> | ';
+       }
+       if ($prev_available) {
+               print $fh '<a href="'.$_viewer_prev_url.'">Before</a> | ';
+       }
+       unless ($frame == $last_frame) {
+               print $fh '<a href="'.$_viewer_last_url.'">Now</a> | ';
        }
+       print $fh '<a href="'.$_goto_url.'">GOTO</a>'."\n";
+       print $fh '<span style="float: right;">'."\n";
+       if ($text_mode != TEXT_MODE->{'normal'}) {
+               print $fh '<a href="'.$_viewer_url.'">Without</a> | ';
+       }
+       print $fh '<a href="'.$_info_path.'">Info</a> | ';
+       print $fh '<a href="'.$_bbcode_path.'">BB</a>';
+       print $fh "\n</span>\n";
+       
+       print $fh "</div>\n</div>\n";
        
        print_html_body_end($fh);
        print_html_end($fh);
@@ -1008,4 +1148,43 @@ sub print_viewer_page {
        return 1;
 }
 
+
+sub write_index {
+       (
+               my $mode,
+               my $state,
+               my $settings
+       ) = @_;
+       
+       if ($mode eq viewer) {
+               my %frame_data     = read_data_file(join_path(PATH_SEPARATOR(), DATA_PATH(), 0));
+               my %next_frame_data= read_data_file(join_path(PATH_SEPARATOR(), DATA_PATH(), 1));
+               my %default        = read_data_file(DATA_DEFAULT_PATH());
+               
+               %frame_data     = merge_settings(\%default,      \%frame_data);
+               %next_frame_data= merge_settings(\%default, \%next_frame_data);
+               
+               print_viewer_page(
+                       WWW_INDEX_PATH(),
+                       {
+                               'frame' => 0,
+                               'access' => 1,
+                               'password_ok' => 0,
+                               'timer_unlocked' => 3, # not relevant
+                               'timer' => 0, # not relevant
+                               'static' => 1,
+                               'show_command' => 1
+                       },
+                       $state,
+                       $settings,
+                       \%frame_data,
+                       \%next_frame_data
+               );
+       }
+       else {
+               # TO DO !
+       }
+}
+
+
 1
index fa9bd98f7a56305793acbccbb0a39c835cb69b6a..59fa4b7a789b21298e453ee2c81a5d3d62497d7b 100644 (file)
@@ -140,6 +140,8 @@ PERL_SET_PATH = \$ENV{'PATH'} = @_PERL_STR($path);
 
 PERL_EXPORT_VERSION    = @_PERL_OUR_STR( VERSION, $_version)
 
+PERL_PATH_SEPARATOR = = @_PERL_CONSTANT_STR( PATH_SEPARATOR, $_PATH_SEPARATOR)
+
 PERL_CGI_PATH           = @_PERL_CONSTANT_STR( CGI_PATH       , $_cgi_path       )
 PERL_CGI_2WORDS_PATH    = @_PERL_CONSTANT_STR( CGI_2WORDS_PATH, $_cgi_2words_path)
 PERL_CGI_ATTACH_PATH    = @_PERL_CONSTANT_STR( CGI_ATTACH_PATH, $_cgi_attach_path)
index f0ad73bb2fedb11fea301ef970704ba19923fd46..3c59102ff9acff9edbca8767dfb71c52597a285c 100644 (file)
@@ -33,27 +33,15 @@ use botm_common (
        'join_path'
 );
 use bsta_lib (
-       'STATE',
+       'STATE', 'TEXT_MODE'
        'fail_method', 'fail_content_type',
-       'entityencode',
-       'printdatafileht',
-       'urlencode',
-       'bb2ht', 'bb2bb', 'linehtml',
-       
-       'readdatafile', 'writedatafile'
+       'get_remote_addr', 'get_frame', 'get_password',
+       'merge_settings',
+       'print_viewer_page', 'write_index'
 );
 use File::Copy;
 
-###PERL_CGI_PATH:           CGI_PATH           = /bsta/
-###PERL_CGI_ATTACH_PATH:    CGI_ATTACH_PATH    = /bsta/a
-###PERL_CGI_BBCODE_PATH:    CGI_BBCODE_PATH    = /bsta/b
-###PERL_CGI_CSS_PATH:       CGI_CSS_PATH       = /bsta/bsta.css
-###PERL_CGI_FRAME_PATH:     CGI_FRAME_PATH     = /bsta/f
-###PERL_CGI_GOTO_PATH:      CGI_GOTO_PATH      = /bsta/g
-###PERL_CGI_INFO_PATH:      CGI_INFO_PATH      = /bsta/i
-###PERL_CGI_LOGO_PATH:      CGI_LOGO_PATH      = /bsta/botmlogo.png
-###PERL_CGI_TIMER_PATH:     CGI_TIMER_PATH     = /bsta/timer.js
-###PERL_CGI_VIEWER_PATH:    CGI_VIEWER_PATH    = /bsta/v
+###PERL_PATH_SEPARATOR:     PATH_SEPARATOR     = /
 
 ###PERL_DATA_PATH:          DATA_PATH          = /botm/data/bsta/
 ###PERL_DATA_DEFAULT_PATH:  DATA_DEFAULT_PATH  = /botm/data/bsta/default
@@ -64,63 +52,57 @@ use File::Copy;
 ###PERL_DATA_STORY_PATH:    DATA_STORY_PATH    = /botm/data/bsta/story
 
 ###PERL_WWW_PATH:           WWW_PATH           = /botm/www/1190/bsta/
-###PERL_WWW_INDEX_PATH:     WWW_INDEX_PATH     = /botm/www/1190/bsta/index.htm
-
-###PERL_WEBSITE:            WEBSITE            = 1190.bicyclesonthemoon.info
-###PERL_WEBSITE_NAME:       WEBSITE_NAME       = Bicycles on the Moon
-###PERL_FAVICON_PATH:       FAVICON_PATH       = /img/favicon.png
 
 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 %newstate;
-my %gotolist;
-
-my $time = time();
-srand ($time-$$);
+my %new_state;
+my %goto_list;
 
 my $method;
 my $frame;
-my $framedata_path;
+my $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 $state_file;
+my $show_command;
 my $ongtime;
-my $textmode;
+my $text_mode;
 
 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 fail_method($ENV{'REQUEST_METHOD'}, 'GET, POST, HEAD');
 }
 
-%http = read_header_env (\%ENV);
+%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> );
+               foreach my $ind (keys %cgi_post) {
+                       $cgi{$ind} = $cgi_post{$ind};
                }
        }
        # multipart not supported
@@ -128,220 +110,177 @@ if ($method eq 'POST') {
                exit fail_content_type($http{'content-type'}, $method);
        }
 }
+$IP = get_remote_addr();
+$frame = get_frame(\%cgi);
+$password = get_password(\%cgi);
 
-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';
-}
-
-if ($cgi{'f'} =~ /^(.+)$/) {
-       $frame = int($1);
-}
-elsif ($ENV{'PATH_INFO'} =~ /^\/(.+)$/) {
-       $frame = int($1);
-}
-else {
-       $frame = 0;
-}
-$framedata_path = join_path('/', DATA_PATH, $frame);
+%settings  = read_data_file(DATA_SETTINGS_PATH());
+%default   = read_data_file(DATA_DEFAULT_PATH());
 
-if ($cgi{'p'} =~ /^(.+)$/) {
-       $password = $1;
-}
-else {
-       $password = '';
+if ($frame >= 0) {
+       $frame_data_path = join_path(PATH_SEPARATOR(), DATA_PATH(), $frame);
+       %frame_data= read_data_file($frame_data_path);
 }
-# print "Content-type: text/plain\n\n";
 
-%settings = read_data_file(DATA_SETTINGS_PATH);
-%default  = read_data_file(DATA_DEFAULT_PATH);
-%framedata= read_data_file($framedata_path);
-if ($password eq $settings{'password'}) {
-       $passwordOK = 1;
-}
-else{
-       $passwordOK = 0;
-}
+$password_ok = ($password eq $settings{'password'}) {
 
-if (open ($statefile, "+<:encoding(UTF-8)", DATA_STATE_PATH)) {
-       if (flock($statefile, 2)) {
+# state & activation logic
+if (open ($state_file, "+<:encoding(UTF-8)", DATA_STATE_PATH())) {
+       if (flock($state_file, 2)) {
                
-               %state = read_data_file($statefile);
+               %state = read_data_file($state_file);
                
                if ($frame < 0) {
                        $frame = int($state{'last'}) + $frame +1;
-                       $framedata_path = join_path('/', DATA_PATH, $frame);
-                       %framedata = read_data_file($framedata_path);
+                       $frame_data_path = join_path(PATH_SEPARATOR(), DATA_PATH(), $frame);
+                       %frame_data = read_data_file($frame_data_path);
                }
                
                if (
-                       int($state{'state'}) == STATE->{'waiting'} &&
-                       $frame == int($state{'last'}) &&
-                       $method ne 'HEAD' &&
-                       !$passwordOK
+                       (int($state{'state'}) == STATE->{'waiting'}) &&
+                       ($frame == int($state{'last'})) &&
+                       ($method ne 'HEAD') &&
+                       (!$password_ok)
                ) {
-                       my %newstate = %state;
-                       if ($state{'ip1'} ne $IP) {
+                       # register IP for progress
+                       my %new_state = %state;
+                       unless (
+                               ($state{'ip1'} eq $IP) ||
+                               ($state{'ip1'} eq $IP) ||
+                               ($state{'ip1'} eq $IP)
+                       )
+                       {
                                if ($state{'ip1'} eq '') {
-                                       $newstate{'ip1'} = $IP;
-                                       write_data_file($statefile, '', '', \%newstate);
+                                       $new_state{'ip1'} = $IP;
                                }
-                               elsif ($state{'ip2'} ne $IP) {
-                                       if ($state{'ip2'} eq '') {
-                                               $newstate{'ip2'} = $IP;
-                                               write_data_file($statefile, '', '', \%newstate);
-                                       }
-                                       else {
-                                               $newstate{'state'} = STATE->{'ready'};
-                                               $newstate{'ip3'} = $IP;
-                                               write_data_file($statefile, '', '', \%newstate);
-                                       }
+                               elsif ($state{'ip2'} eq '') {
+                                       $new_state{'ip2'} = $IP;
                                }
+                               elsif ($state{'ip3'} eq '') {
+                                       $new_state{'ip3'} = $IP;
+                               }
+                               write_data_file($state_file, '', '', \%new_state);
                        }
                }
-               elsif (int($state{'state'}) == STATE->{'inactive'} && $frame == 1) {
+               elsif (
+                       (int($state{'state'}) == STATE->{'inactive'}) &&
+                       ($frame == 1)
+               ) {
+                       # ready to activate?
+                       # NOTE: at this point frame 0 is already ONGed.
                        my %story;
-                       my $framefilename;
-                       my $inpath;
-                       my $outpath;
+                       my $frame_file;
+                       my $in_path;
+                       my $out_path;
                        
-                       %story = read_data_file(DATA_STORY_PATH);
-                       %gotolist = read_data_file(DATA_LIST_PATH);
-                       if (int($story{'state'}) == 0x11 && int($story{'pass'}) == 1) {
-                               #ACTIVATE!
+                       %story     = read_data_file(DATA_STORY_PATH());
+                       %goto_list = read_data_file(DATA_LIST_PATH);
+                       
+                       if (
+                               (int($story{'state'}) == 0x11) && # buttons 0 + 4
+                               (int($story{'pass'}) == 1)
+                       ) {
+                               # conditions met; ACTIVATE!
+                               
+                               # update ONG time of frame 1
+                               $frame_data{'ongtime'} = $time;
+                               write_data_file($frame_data_path, '', '', \%frame_data);
                                
-                               $framedata{'ongtime'} = $time;
-                               write_data_file($framedata_path, '', '', \%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 ;
+                               # 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'} = int($settings{'firstongtime'});
                                
-                               unless (defined($framedata{'ext'})){
-                                       $framedata{'ext'} = $default{'ext'};
-                               }
+                               # prepare to ONG frame 1
                                
-                               $framefilename = sprintf($settings{'frame'}, $frame, $framedata{'ext'});
-                               $inpath  = join_path('/', DATA_PATH, $framefilename);
-                               $outpath = join_path('/', WWW_PATH,  $framefilename);
+                               # determine frame file & paths
+                               unless (defined($frame_data{'ext'})){
+                                       $frame_data{'ext'} = $default{'ext'};
+                               }
+                               $frame_file = sprintf($settings{'frame'}, $frame, $frame_data{'ext'});
+                               $in_path  = join_path(PATH_SEPARATOR(), DATA_PATH(), $frame_file);
+                               $out_path = join_path(PATH_SEPARATOR(), WWW_PATH(),  $frame_file);
                                
-                               $gotolist{'title-1'}   = $framedata{'title'};
-                               $gotolist{'ongtime-1'} = $framedata{'ongtime'};
+                               # update the GOTO list with frame 1
+                               $goto_list{'title-1'}   = $frame_data{'title'};
+                               $goto_list{'ongtime-1'} = $frame_data{'ongtime'};
                                
                                if (copy ($inpath, $outpath)) {
-                                       writeindex(WWW_INDEX_PATH);
-                                       write_data_file($statefile,     '', '', \%state);
-                                       write_data_file(DATA_LIST_PATH, '', '', \%gotolist);
+                                       write_index('viewer', \%state, \%settings);
+                                       write_data_file($state_file,     '','', \%state);
+                                       write_data_file(DATA_LIST_PATH(),'','', \%goto_list);
                                }
                                else {
+                                       # 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 ($state_file);
 }
 else {
-       $state{'state'} = 0;
+       $state{'state'} = STATE->{'inactive'};
 }
 
-%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};
-       }
-}
+$next_frame_data_path = join_path(PATH_SEPARATOR(), DATA_PATH(), $frame+1);
+%next_frame_data = read_data_file($next_frame_data_path);
+
+# apply defaults
+%frame_data      = merge_settings(\%default,      \%frame_data);
+%next_frame_data = merge_settings(\%default, \%next_frame_data);
 
-$hours=int($state{'nextong'})-$time;
-$ongtime=int($state{'ongtime'});
+$timer   = int($state{'nextong'}) - $time;
+$ongtime = int($state{'ongtime'});
 if($ongtime == 0) {
-       $ongtime=int($settings{'ongtime'})
+       $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));
+$show_command = ($timer < ($ongtime*3600/3));
+if ($state{'state'} >= STATE->{'ready'}) {
+       $timer_unlocked = 3;
 }
-elsif(($hours>=-15) && (int($state{'state'}) == 2)){
-       $seconds='NG';
-       $minutes='00';
-       $hours='00';
+elsif ($state{'ip3'} ne '') {
+       $timer_unlocked = 3;
 }
-else{
-       $seconds='EE';
-       $minutes='EE';
-       $hours='EE';
+elsif ($state{'ip2'} ne '') {
+       $timer_unlocked = 2;
 }
-
-
-if ($passwordOK || (int($state{'state'}) >= 1 && $frame <= int($state{'last'}) && $frame >= 0)) {
-       $access=1;
+elsif ($state{'ip1'} ne '') {
+       $timer_unlocked = 1;
 }
 else {
-       $access=0;
-       %framedata = readdatafile(DATA_NOACCESS_PATH);
-       foreach my $ind (keys %default) {
-               unless(defined($framedata{$ind})){
-                       $framedata{$ind}=$default{$ind};
-               }
-       }
+       $timer_unlocked = 0;
 }
 
-$textmode = int($cgi{'b'});
-if($textmode > 2) {
-       $textmode = 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);
 }
 
-# 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";
-# }
+$text_mode = int($cgi{'b'});
+if($text_mode > 2) {
+       $text_mode = TEXT_MODE->{'normal'};
+}
 
 print "Content-type: text/html\n";
 if(!$access) {
@@ -352,311 +291,19 @@ 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="'.CGI_CSS_PATH.'">'."\n";
-print '<link rel="index" href="'.CGI_GOTO_PATH.($passwordOK?('?p='.urlencode($password)):'').'">'."\n";
-print '<link rel="start" href="'.CGI_VIEWER_PATH.'/0'.($passwordOK?('?p='.urlencode($password)):'').'">'."\n";
-if($frame>0 && $access) {
-       print '<link rel="prev" href="'.CGI_VIEWER_PATH.'/'.($frame - 1).($passwordOK?('?p='.urlencode($password)):'').'">'."\n";
-}
-if ($passwordOK || $frame<int($state{'last'})) {
-       print '<link rel="next" href="'.CGI_VIEWER_PATH.'/'.($frame + 1).($passwordOK?('?p='.urlencode($password)):'').'">'."\n";
-       print '<link rel="prefetch" href="'.CGI_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="'.CGI_TIMER_PATH.'"></script> -->'."\n";
-}
-print '</head><body>'."\n";
-print '<a href="/"><img id="botmlogo" src="'.CGI_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'});
-       }
-       else {
-               print CGI_FRAME_PATH.'/'.$frame.($passwordOK?('?p='.urlencode($password)):'');
-       }
-       print '" id="frame" alt="'.$frame.'" title="'.entityencode($framedata{'title'}).'">'."\n";
-}
-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.CGI_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";
-}
-elsif($framedata{'content'} ne ''){
-       print '<div id="undertext">'."\n";
-       print bb2htf($framedata{'content'})."\n";
-       print '</div>'."\n";
-}
-
-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>';
-}
-print '&gt;';
-if (!$access){
-       print '<a href="'.CGI_VIEWER_PATH.'/-1">'.entityencode($framedata{'command'}).'</a><br>'."\n";
-}
-else {
-       if ($passwordOK || $frame<int($state{'last'}) || (int($state{'state'}) >= 2 && $showcommand)) {
-               if ($passwordOK || $frame<int($state{'last'})) {
-                       print '<a href="'.CGI_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";
-}
-print '</div>'."\n";
-
-print '<div id="underlinks">'."\n";
-print '<a href="'.CGI_PATH.'">Once again</a>';
-if($frame>0 && $access) {
-       print ' | <a href="'.CGI_VIEWER_PATH.'/'.($frame-1).($passwordOK?('?p='.urlencode($password)):'').'">Before</a>';
-}
-if($frame != int($state{'last'})) {
-       print ' | <a href="'.CGI_VIEWER_PATH.'/'.int($state{'last'}).($passwordOK?('?p='.urlencode($password)):'').'">Now</a>';
-}
-print ' | <a href="'.CGI_GOTO_PATH.($passwordOK?('?p='.urlencode($password)):'').'">GOTO</a>'."\n";
-print '<span style="float: right;">'."\n";
-
-if ($textmode!=0) {
-       print '<a href="'.CGI_VIEWER_PATH.'/'.$frame.($passwordOK?('?p='.urlencode($password)):'').'">Without</a> | ';
-}
-
-print '<a href="'.(($textmode==2)?(CGI_INFO_PATH.'/'.$frame.($passwordOK?('?p='.urlencode($password)):'')):(CGI_VIEWER_PATH.'/'.$frame.'?b=2'.($passwordOK?('&amp;p='.urlencode($password)):''))).'">Info</a>';
-print ' | <a href="'.(($textmode==1)?(CGI_BBCODE_PATH.'/'.$frame.($passwordOK?('?p='.urlencode($password)):'')):(CGI_VIEWER_PATH.'/'.$frame.'?b=1'.($passwordOK?('&amp;p='.urlencode($password)):''))).'">BB</a>';
-print "\n</span>\n";
-
-print '</div>'."\n";
-
-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(DATA_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="'.CGI_CSS_PATH.'">'."\n";
-       print $indexfile '<link rel="index" href="'.CGI_GOTO_PATH.'">'."\n";
-       print $indexfile '<link rel="start" href="'.CGI_VIEWER_PATH.'/0">'."\n";
-       print $indexfile '<link rel="next" href="'.CGI_VIEWER_PATH.'/1">'."\n";
-       print $indexfile '<link rel="prefetch" href="'.CGI_VIEWER_PATH.'/1">'."\n";
-       print $indexfile '</head><body>'."\n";
-       print $indexfile '<a href="/"><img id="botmlogo" src="'.CGI_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="'.CGI_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="'.CGI_VIEWER_PATH.'/-1">Now</a> | <a href="'.CGI_GOTO_PATH.'">GOTO</a>';
-       print $indexfile '<span style="float: right;"><a href="'.CGI_INFO_PATH.'/0?b=2">Info</a> | <a href="'.CGI_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;
-}
-
-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 = CGI_ATTACH_PATH.'/'.int($1).($passwordOK?('?p='.urlencode($password)):'');
-               }
-               elsif ($tagvalue =~ /^vw&([0-9]+)$/) {
-                       $tagvalue = CGI_VIEWER_PATH.'/'.int($1).($passwordOK?('?p='.urlencode($password)):'');
-               }
-               elsif ($tagvalue =~ /^fr&([0-9]+)$/) {
-                       $tagvalue = CGI_FRAME_PATH.'/'.int($1).($passwordOK?('?p='.urlencode($password)):'');
-               }
-               else {
-                       $tagvalue = '';
-               }
-               
-               $bb = $pretext.$tagvalue.$posttext;
-       }
-       
-       return bb2ht ($bb);
-}
-
-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.CGI_ATTACH_PATH.'/'.int($1);
-               }
-               elsif ($tagvalue =~ /^vw&([0-9]+)$/) {
-                       $tagvalue = 'http://'.WEBSITE.CGI_VIEWER_PATH.'/'.int($1);
-               }
-               elsif ($tagvalue =~ /^fr&([0-9]+)$/) {
-                       $tagvalue = 'http://'.WEBSITE.CGI_FRAME_PATH.'/'.int($1);
-               }
-               else {
-                       $tagvalue = '';
-               }
-               
-               $bb = $pretext.$tagvalue.$posttext;
-       }
-       
-       return linehtml(bb2bb ($bb));
-}
+print_viewer_page (
+       \*STDOUT,
+       {
+               'frame'         => $frame,
+               'access'        => $access,
+               'password_ok'   => $password_ok,
+               'timer_unlocked'=> $timer_unlocked,
+               'timer'         => $timer,
+               'static'        => 0,
+               'show_command'  => $show_command,
+       },
+       \%state,
+       \%settings,
+       \%frame_data,
+       \%next_frame_data
+);