1 ###RUN_PERL: #!/usr/bin/perl
4 # viewer is generated from viewer.1.pl.
8 # Copyright (C) 2016-2017, 2019-2020, 2023 Balthasar SzczepaĆski
10 # This program is free software: you can redistribute it and/or modify
11 # it under the terms of the GNU Affero General Public License as
12 # published by the Free Software Foundation, either version 3 of the
13 # License, or (at your option) any later version.
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU Affero General Public License for more details.
20 # You should have received a copy of the GNU Affero General Public License
21 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # use Encode::Locale ('decode_argv');
26 use Encode ('encode', 'decode');
28 ###PERL_LIB: use lib /botm/lib/bsta
31 'read_data_file', 'write_data_file',
37 'fail_method', 'fail_content_type',
41 'bb2ht', 'bb2bb', 'linehtml',
43 'readdatafile', 'writedatafile'
47 ###PERL_CGI_PATH: CGI_PATH = /bsta/
48 ###PERL_CGI_ATTACH_PATH: CGI_ATTACH_PATH = /bsta/a
49 ###PERL_CGI_BBCODE_PATH: CGI_BBCODE_PATH = /bsta/b
50 ###PERL_CGI_CSS_PATH: CGI_CSS_PATH = /bsta/bsta.css
51 ###PERL_CGI_FRAME_PATH: CGI_FRAME_PATH = /bsta/f
52 ###PERL_CGI_GOTO_PATH: CGI_GOTO_PATH = /bsta/g
53 ###PERL_CGI_INFO_PATH: CGI_INFO_PATH = /bsta/i
54 ###PERL_CGI_LOGO_PATH: CGI_LOGO_PATH = /bsta/botmlogo.png
55 ###PERL_CGI_TIMER_PATH: CGI_TIMER_PATH = /bsta/timer.js
56 ###PERL_CGI_VIEWER_PATH: CGI_VIEWER_PATH = /bsta/v
58 ###PERL_DATA_PATH: DATA_PATH = /botm/data/bsta/
59 ###PERL_DATA_DEFAULT_PATH: DATA_DEFAULT_PATH = /botm/data/bsta/default
60 ###PERL_DATA_LIST_PATH: DATA_LIST_PATH = /botm/data/bsta/list
61 ###PERL_DATA_NOACCESS_PATH: DATA_NOACCESS_PATH = /botm/data/bsta/noaccess
62 ###PERL_DATA_SETTINGS_PATH: DATA_SETTINGS_PATH = /botm/data/bsta/settings
63 ###PERL_DATA_STATE_PATH: DATA_STATE_PATH = /botm/data/bsta/state
64 ###PERL_DATA_STORY_PATH: DATA_STORY_PATH = /botm/data/bsta/story
66 ###PERL_WWW_PATH: WWW_PATH = /botm/www/1190/bsta/
67 ###PERL_WWW_INDEX_PATH: WWW_INDEX_PATH = /botm/www/1190/bsta/index.htm
69 ###PERL_WEBSITE: WEBSITE = 1190.bicyclesonthemoon.info
70 ###PERL_WEBSITE_NAME: WEBSITE_NAME = Bicycles on the Moon
71 ###PERL_FAVICON_PATH: FAVICON_PATH = /img/favicon.png
73 binmode STDIN, ':encoding(UTF-8)';
74 binmode STDOUT, ':encoding(UTF-8)';
75 binmode STDERR, ':encoding(UTF-8)';
106 delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
107 ###PERL_SET_PATH: $ENV{'PATH'} = /usr/local/bin:/usr/bin:/bin;
109 if ($ENV{'REQUEST_METHOD'} =~ /^(HEAD|GET|POST)$/) {
113 exit fail_method($ENV{'REQUEST_METHOD'}, 'GET, POST, HEAD');
116 %http = read_header_env (\%ENV);
117 %cgi = url_query_decode($ENV{'QUERY_STRING'});
119 if ($method eq 'POST') {
120 if ($http{'content-type'} eq 'application/x-www-form-urlencoded') {
121 my %cgipost=getcgi( <STDIN> );
122 foreach my $ind (keys %cgipost) {
123 $cgi{$ind}=$cgipost{$ind};
126 # multipart not supported
128 exit fail_content_type($http{'content-type'}, $method);
132 if ($ENV{'HTTP_X_FORWARDED_FOR'} =~ /^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$/) {
135 elsif ($ENV{'REMOTE_ADDR'} =~ /^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$/) {
142 if ($cgi{'f'} =~ /^(.+)$/) {
145 elsif ($ENV{'PATH_INFO'} =~ /^\/(.+)$/) {
151 $framedata_path = join_path('/', DATA_PATH, $frame);
153 if ($cgi{'p'} =~ /^(.+)$/) {
159 # print "Content-type: text/plain\n\n";
161 %settings = read_data_file(DATA_SETTINGS_PATH);
162 %default = read_data_file(DATA_DEFAULT_PATH);
163 %framedata= read_data_file($framedata_path);
164 if ($password eq $settings{'password'}) {
171 if (open ($statefile, "+<:encoding(UTF-8)", DATA_STATE_PATH)) {
172 if (flock($statefile, 2)) {
174 %state = read_data_file($statefile);
177 $frame = int($state{'last'}) + $frame +1;
178 $framedata_path = join_path('/', DATA_PATH, $frame);
179 %framedata = read_data_file($framedata_path);
183 int($state{'state'}) == STATE->{'waiting'} &&
184 $frame == int($state{'last'}) &&
188 my %newstate = %state;
189 if ($state{'ip1'} ne $IP) {
190 if ($state{'ip1'} eq '') {
191 $newstate{'ip1'} = $IP;
192 write_data_file($statefile, '', '', \%newstate);
194 elsif ($state{'ip2'} ne $IP) {
195 if ($state{'ip2'} eq '') {
196 $newstate{'ip2'} = $IP;
197 write_data_file($statefile, '', '', \%newstate);
200 $newstate{'state'} = STATE->{'ready'};
201 $newstate{'ip3'} = $IP;
202 write_data_file($statefile, '', '', \%newstate);
207 elsif (int($state{'state'}) == STATE->{'inactive'} && $frame == 1) {
213 %story = read_data_file(DATA_STORY_PATH);
214 %gotolist = read_data_file(DATA_LIST_PATH);
215 if (int($story{'state'}) == 0x11 && int($story{'pass'}) == 1) {
218 $framedata{'ongtime'} = $time;
219 write_data_file($framedata_path, '', '', \%framedata);
222 $state {'ip1'} = '0.0.0.0';
223 $state {'ip2'} = '0.0.0.0';
225 $state {'nextong'} = (int($time / 3600) + int($settings{'firstongtime'})) * 3600 ;
226 $state{'ongtime'} = int($settings{'firstongtime'});
228 unless (defined($framedata{'ext'})){
229 $framedata{'ext'} = $default{'ext'};
232 $framefilename = sprintf($settings{'frame'}, $frame, $framedata{'ext'});
233 $inpath = join_path('/', DATA_PATH, $framefilename);
234 $outpath = join_path('/', WWW_PATH, $framefilename);
236 $gotolist{'title-1'} = $framedata{'title'};
237 $gotolist{'ongtime-1'} = $framedata{'ongtime'};
239 if (copy ($inpath, $outpath)) {
240 writeindex(WWW_INDEX_PATH);
241 write_data_file($statefile, '', '', \%state);
242 write_data_file(DATA_LIST_PATH, '', '', \%gotolist);
245 $state{'state'} = STATE->{'inactive'};
259 %nextframedata=readdatafile(DATA_PATH.($frame+1));
260 foreach my $ind (keys %default) {
261 unless(defined($framedata{$ind})){
262 $framedata{$ind}=$default{$ind};
264 unless(defined($nextframedata{$ind})){
265 $nextframedata{$ind}=$default{$ind};
269 $hours=int($state{'nextong'})-$time;
270 $ongtime=int($state{'ongtime'});
272 $ongtime=int($settings{'ongtime'})
275 $showcommand = ($hours < ($ongtime*3600/3));
277 $seconds = sprintf('%02d',$hours % 60);
278 $hours = int($hours/60);
279 $minutes = sprintf('%02d',$hours % 60);
280 $hours = sprintf('%02d',int($hours/60));
282 elsif(($hours>=-15) && (int($state{'state'}) == 2)){
294 if ($passwordOK || (int($state{'state'}) >= 1 && $frame <= int($state{'last'}) && $frame >= 0)) {
299 %framedata = readdatafile(DATA_NOACCESS_PATH);
300 foreach my $ind (keys %default) {
301 unless(defined($framedata{$ind})){
302 $framedata{$ind}=$default{$ind};
307 $textmode = int($cgi{'b'});
312 # print "Content-type: text/plain\n\n";
313 # print 'frame='.$frame."\n";
314 # print 'password='.$password."\n";
315 # print 'passwordOK='.$passwordOK."\n";
316 # print 'access='.$access."\n";
317 # print "\n>>>ENV<<<\n\n";
318 # foreach my $ind (keys %ENV) {
319 # print $ind.'='.$ENV{$ind}."\n";
321 # print "\n>>>HTTP<<<\n\n";
322 # foreach my $ind (keys %http) {
323 # print $ind.': '.$http{$ind}."\n";
325 # print "\n>>>CGI<<<\n\n";
326 # foreach my $ind (keys %cgi) {
327 # print $ind.'='.$cgi{$ind}."\n";
329 # print "\n>>>FRAMEDATA<<<\n\n";
330 # foreach my $ind (keys %framedata) {
331 # print $ind.': '.$framedata{$ind}."\n";
333 # print "\n>>>NEXTFRAMEDATA<<<\n\n";
334 # foreach my $ind (keys %nextframedata) {
335 # print $ind.': '.$nextframedata{$ind}."\n";
337 # print "\n>>>SETTINGS<<<\n\n";
338 # foreach my $ind (keys %settings) {
339 # print $ind.': '.$settings{$ind}."\n";
341 # print "\n>>>STATE<<<\n\n";
342 # foreach my $ind (keys %state) {
343 # print $ind.': '.$state{$ind}."\n";
346 print "Content-type: text/html\n";
348 print "Status: 403 Forbidden\n";
351 if($method eq 'HEAD') {
355 print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "">'."\n";
356 print '<html lang="en"><head>'."\n";
357 print '<meta http-equiv="Content-type" content="text/html; charset=UTF-8">'."\n";
359 unless($framedata{'title'} eq '' || $framedata{'title'} eq $settings{'story'}) {
360 print entityencode($framedata{'title'}).' • ';
362 print entityencode($settings{'story'}).' • '.WEBSITE_NAME.'</title>'."\n";
363 print '<link rel="icon" type="image/png" href="'.FAVICON_PATH.'">'."\n";
364 print '<link rel="stylesheet" href="'.CGI_CSS_PATH.'">'."\n";
365 print '<link rel="index" href="'.CGI_GOTO_PATH.($passwordOK?('?p='.urlencode($password)):'').'">'."\n";
366 print '<link rel="start" href="'.CGI_VIEWER_PATH.'/0'.($passwordOK?('?p='.urlencode($password)):'').'">'."\n";
367 if($frame>0 && $access) {
368 print '<link rel="prev" href="'.CGI_VIEWER_PATH.'/'.($frame - 1).($passwordOK?('?p='.urlencode($password)):'').'">'."\n";
370 if ($passwordOK || $frame<int($state{'last'})) {
371 print '<link rel="next" href="'.CGI_VIEWER_PATH.'/'.($frame + 1).($passwordOK?('?p='.urlencode($password)):'').'">'."\n";
372 print '<link rel="prefetch" href="'.CGI_VIEWER_PATH.'/'.($frame + 1).($passwordOK?('?p='.urlencode($password)):'').'">'."\n";
374 if($frame==int($state{'last'}) && int($state{'state'}) >= 1 && int($state{'state'}) < 3) {
375 print '<!-- <script src="'.CGI_TIMER_PATH.'"></script> -->'."\n";
377 print '</head><body>'."\n";
378 print '<a href="/"><img id="botmlogo" src="'.CGI_LOGO_PATH.'" alt="'.WEBSITE.'"></a>'."\n";
379 print '<div id="all">'."\n";
381 print '<div id="inst" class="ins">'."\n";
383 print '<div id="title">'."\n";
384 print '<H1 id="titletext">'.entityencode($framedata{'title'}).'</H1>'."\n";
387 print '</div><div id="framespace">'."\n";
389 print '<img src="'.CGI_PATH.$framedata{'frame'}.'" id ="frame" alt="'.$frame.'" title="'.entityencode($framedata{'title'}).'">'."\n";
393 if($frame<=int($state{'last'}) && int($state{'state'}) >= 1) {
394 print CGI_PATH.sprintf($settings{'frame'},$frame,$framedata{'ext'});
397 print CGI_FRAME_PATH.'/'.$frame.($passwordOK?('?p='.urlencode($password)):'');
399 print '" id="frame" alt="'.$frame.'" title="'.entityencode($framedata{'title'}).'">'."\n";
401 print '</div><div id="insb" class="ins">'."\n";
404 print '<div id="chat">'."\n";
406 if ($passwordOK || $frame<int($state{'last'}) || (int($state{'state'}) >= 2 && $showcommand)) {
407 $framedata{'command'}=$nextframedata{'title'};
410 $framedata{'frame'}=sprintf($settings{'frame'},$frame,$framedata{'ext'});
413 printdatafileht(%framedata);
417 print '<div id="chat">'."\n";
418 print '[quote][center][size=200]'.entityencode($framedata{'title'}).'[/size]<br>'."\n";
419 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";
420 print bb2bbf($framedata{'content'}).'[/quote]</div>'."\n";
422 elsif($framedata{'content'} ne ''){
423 print '<div id="undertext">'."\n";
424 print bb2htf($framedata{'content'})."\n";
428 print '<div id="command">'."\n";
429 if($frame==int($state{'last'}) && int($state{'state'}) >= 1 && int($state{'state'}) < 3) {
430 print '[<span id="ongh" class="';
431 if(int($state{'state'}) >= 2 || $state{'ip1'} ne '') {
437 print '">'.$hours.'</span>:<span id="ongm" class="';
438 if(int($state{'state'}) >= 2 || $state{'ip2'} ne '') {
444 print '">'.$minutes.'</span>:<span id="ongs" class="';
445 if(int($state{'state'}) >= 2 || $state{'ip3'} ne '') {
451 print '">'.$seconds.'</span>]<br>';
455 print '<a href="'.CGI_VIEWER_PATH.'/-1">'.entityencode($framedata{'command'}).'</a><br>'."\n";
458 if ($passwordOK || $frame<int($state{'last'}) || (int($state{'state'}) >= 2 && $showcommand)) {
459 if ($passwordOK || $frame<int($state{'last'})) {
460 print '<a href="'.CGI_VIEWER_PATH.'/'.($frame + 1).($passwordOK?('?p='.urlencode($password)):'').'">';
462 if($nextframedata{'title'} ne '') {
463 print entityencode($nextframedata{'title'});
465 elsif ($passwordOK || $frame<int($state{'last'})) {
466 print'<span class="inp">_</span>';
468 if ($passwordOK || $frame<int($state{'last'})) {
472 print'<span class="inp">_</span>';
476 print'<span class="inp">_</span>';
482 print '<div id="underlinks">'."\n";
483 print '<a href="'.CGI_PATH.'">Once again</a>';
484 if($frame>0 && $access) {
485 print ' | <a href="'.CGI_VIEWER_PATH.'/'.($frame-1).($passwordOK?('?p='.urlencode($password)):'').'">Before</a>';
487 if($frame != int($state{'last'})) {
488 print ' | <a href="'.CGI_VIEWER_PATH.'/'.int($state{'last'}).($passwordOK?('?p='.urlencode($password)):'').'">Now</a>';
490 print ' | <a href="'.CGI_GOTO_PATH.($passwordOK?('?p='.urlencode($password)):'').'">GOTO</a>'."\n";
491 print '<span style="float: right;">'."\n";
494 print '<a href="'.CGI_VIEWER_PATH.'/'.$frame.($passwordOK?('?p='.urlencode($password)):'').'">Without</a> | ';
497 print '<a href="'.(($textmode==2)?(CGI_INFO_PATH.'/'.$frame.($passwordOK?('?p='.urlencode($password)):'')):(CGI_VIEWER_PATH.'/'.$frame.'?b=2'.($passwordOK?('&p='.urlencode($password)):''))).'">Info</a>';
498 print ' | <a href="'.(($textmode==1)?(CGI_BBCODE_PATH.'/'.$frame.($passwordOK?('?p='.urlencode($password)):'')):(CGI_VIEWER_PATH.'/'.$frame.'?b=1'.($passwordOK?('&p='.urlencode($password)):''))).'">BB</a>';
506 print '<a href="/" class="cz">'.WEBSITE.'</a>'."\n";
508 print '</body></html>'."\n";
511 (my $indexpath) = @_;
517 if(ref($indexpath)) {
518 $indexfile=$indexpath;
519 unless (seek($indexfile, 0, 0)) {
524 unless (open ($indexfile, ">", $indexpath)) {
529 %framedata = readdatafile(DATA_PATH.0);
530 %nextframedata = readdatafile(DATA_PATH.1);
531 %default=readdatafile(DATA_DEFAULT_PATH);
533 foreach my $ind (keys %default) {
534 unless(defined($framedata{$ind})){
535 $framedata{$ind}=$default{$ind};
537 unless(defined($nextframedata{$ind})){
538 $nextframedata{$ind}=$default{$ind};
542 print $indexfile '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "">'."\n";
543 print $indexfile '<html lang="en"><head>'."\n";
544 print $indexfile '<meta http-equiv="Content-type" content="text/html; charset=UTF-8">'."\n";
545 print $indexfile '<title>'.entityencode($settings{'story'}).' • '.WEBSITE_NAME.'</title>'."\n";
546 print $indexfile '<link rel="icon" type="image/png" href="'.FAVICON_PATH.'">'."\n";
547 print $indexfile '<link rel="stylesheet" href="'.CGI_CSS_PATH.'">'."\n";
548 print $indexfile '<link rel="index" href="'.CGI_GOTO_PATH.'">'."\n";
549 print $indexfile '<link rel="start" href="'.CGI_VIEWER_PATH.'/0">'."\n";
550 print $indexfile '<link rel="next" href="'.CGI_VIEWER_PATH.'/1">'."\n";
551 print $indexfile '<link rel="prefetch" href="'.CGI_VIEWER_PATH.'/1">'."\n";
552 print $indexfile '</head><body>'."\n";
553 print $indexfile '<a href="/"><img id="botmlogo" src="'.CGI_LOGO_PATH.'" alt="'.WEBSITE.'"></a>'."\n";
554 print $indexfile '<div id="all">'."\n";
556 print $indexfile '<div id="inst" class="ins">'."\n";
558 print $indexfile '<div id="title">'."\n";
559 print $indexfile '<H1 id="titletext">'.entityencode($settings{'story'}).'</H1>'."\n";
560 print $indexfile '</div>'."\n";
562 print $indexfile '</div><div id="framespace">'."\n";
563 print $indexfile '<img src="'.CGI_PATH.sprintf($settings{'frame'},0,$framedata{'ext'}).'" title="'.entityencode($framedata{'title'}).'" alt="0" id="frame">'."\n";
565 print $indexfile '</div><div id="insb" class="ins">'."\n";
566 print $indexfile '<div id="undertext">'."\n";
567 print $indexfile bb2htf($framedata{'content'})."\n";
568 print $indexfile '</div>'."\n";
570 print $indexfile '<div id="command">'."\n";
571 print $indexfile '><a href="'.CGI_VIEWER_PATH.'/1">'.entityencode($nextframedata{'title'}).'</a>'."\n";
572 print $indexfile '</div>'."\n";
574 print $indexfile '<div id="underlinks">'."\n";
577 # <span style="float: right;"><a href="/bsta/v/0?b=2">Info</a> | <a href="/bsta/v/0?b=1">BB</a></span>
579 print $indexfile '<a href="'.CGI_VIEWER_PATH.'/-1">Now</a> | <a href="'.CGI_GOTO_PATH.'">GOTO</a>';
580 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";
581 print $indexfile '</div>'."\n";
583 print $indexfile '</div>'."\n";
585 print $indexfile '</div>'."\n";
586 print $indexfile '<a href="/" class="cz">'.WEBSITE.'</a>'."\n";
588 print $indexfile '</body></html>'."\n";
590 unless (ref($indexpath)) {
594 truncate ($indexfile , tell($indexfile));
607 while($bb =~ m/(###([^#;]*);)/g) {
610 $pretext = substr($bb,0,pos ($bb)-length($tag));
611 $posttext = substr ($bb,pos ($bb));
613 if ($tagvalue =~ /^att&([0-9]+)$/) {
614 $tagvalue = CGI_ATTACH_PATH.'/'.int($1).($passwordOK?('?p='.urlencode($password)):'');
616 elsif ($tagvalue =~ /^vw&([0-9]+)$/) {
617 $tagvalue = CGI_VIEWER_PATH.'/'.int($1).($passwordOK?('?p='.urlencode($password)):'');
619 elsif ($tagvalue =~ /^fr&([0-9]+)$/) {
620 $tagvalue = CGI_FRAME_PATH.'/'.int($1).($passwordOK?('?p='.urlencode($password)):'');
626 $bb = $pretext.$tagvalue.$posttext;
639 while($bb =~ m/(###([^#;]*);)/g) {
642 $pretext = substr($bb,0,pos ($bb)-length($tag));
643 $posttext = substr ($bb,pos ($bb));
645 if ($tagvalue =~ /^att&([0-9]+)$/) {
646 $tagvalue = 'http://'.WEBSITE.CGI_ATTACH_PATH.'/'.int($1);
648 elsif ($tagvalue =~ /^vw&([0-9]+)$/) {
649 $tagvalue = 'http://'.WEBSITE.CGI_VIEWER_PATH.'/'.int($1);
651 elsif ($tagvalue =~ /^fr&([0-9]+)$/) {
652 $tagvalue = 'http://'.WEBSITE.CGI_FRAME_PATH.'/'.int($1);
658 $bb = $pretext.$tagvalue.$posttext;
661 return linehtml(bb2bb ($bb));