]> bicyclesonthemoon.info Git - ott/bsta/blob - viewer.1.pl
Improve variable name & hash handling, & url_merging in viewer output generator
[ott/bsta] / viewer.1.pl
1 ###RUN_PERL: #!/usr/bin/perl
2
3 # /bsta/v
4 # viewer is generated from viewer.1.pl.
5 #
6 # The viewer interface
7 #
8 # Copyright (C) 2016-2017, 2019-2020, 2023  Balthasar SzczepaƄski
9 #
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.
14 #
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.
19 #
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/>.
22
23 use strict;
24 use utf8;
25 # use Encode::Locale ('decode_argv');
26 use Encode ('encode', 'decode');
27
28 ###PERL_LIB: use lib /botm/lib/bsta
29 use botm_common (
30         'read_header_env',
31         'read_data_file', 'write_data_file',
32         'url_query_decode',
33         'join_path'
34 );
35 use bsta_lib (
36         'STATE',
37         'fail_method', 'fail_content_type',
38         'entityencode',
39         'printdatafileht',
40         'urlencode',
41         'bb2ht', 'bb2bb', 'linehtml',
42         
43         'readdatafile', 'writedatafile'
44 );
45 use File::Copy;
46
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
57
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
65
66 ###PERL_WWW_PATH:           WWW_PATH           = /botm/www/1190/bsta/
67 ###PERL_WWW_INDEX_PATH:     WWW_INDEX_PATH     = /botm/www/1190/bsta/index.htm
68
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
72
73 binmode STDIN,  ':encoding(UTF-8)';
74 binmode STDOUT, ':encoding(UTF-8)';
75 binmode STDERR, ':encoding(UTF-8)';
76 # decode_argv();
77
78 my %http;
79 my %cgi;
80 my %framedata;
81 my %nextframedata;
82 my %default;
83 my %settings;
84 my %state;
85 my %newstate;
86 my %gotolist;
87
88 my $time = time();
89 srand ($time-$$);
90
91 my $method;
92 my $frame;
93 my $framedata_path;
94 my $password;
95 my $passwordOK;
96 my $IP;
97 my $access;
98 my $seconds;
99 my $minutes;
100 my $hours;
101 my $statefile;
102 my $showcommand;
103 my $ongtime;
104 my $textmode;
105
106 delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
107 ###PERL_SET_PATH: $ENV{'PATH'} = /usr/local/bin:/usr/bin:/bin;
108
109 if ($ENV{'REQUEST_METHOD'} =~ /^(HEAD|GET|POST)$/) {
110         $method=$1;
111 }
112 else{
113         exit fail_method($ENV{'REQUEST_METHOD'}, 'GET, POST, HEAD');
114 }
115
116 %http = read_header_env (\%ENV);
117 %cgi = url_query_decode($ENV{'QUERY_STRING'});
118
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};
124                 }
125         }
126         # multipart not supported
127         else{
128                 exit fail_content_type($http{'content-type'}, $method);
129         }
130 }
131
132 if ($ENV{'HTTP_X_FORWARDED_FOR'} =~ /^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$/) {
133         $IP=$1;
134 }
135 elsif ($ENV{'REMOTE_ADDR'} =~ /^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$/) {
136         $IP=$1;
137 }
138 else {
139         $IP='0.0.0.0';
140 }
141
142 if ($cgi{'f'} =~ /^(.+)$/) {
143         $frame = int($1);
144 }
145 elsif ($ENV{'PATH_INFO'} =~ /^\/(.+)$/) {
146         $frame = int($1);
147 }
148 else {
149         $frame = 0;
150 }
151 $framedata_path = join_path('/', DATA_PATH, $frame);
152
153 if ($cgi{'p'} =~ /^(.+)$/) {
154         $password = $1;
155 }
156 else {
157         $password = '';
158 }
159 # print "Content-type: text/plain\n\n";
160
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'}) {
165         $passwordOK = 1;
166 }
167 else{
168         $passwordOK = 0;
169 }
170
171 if (open ($statefile, "+<:encoding(UTF-8)", DATA_STATE_PATH)) {
172         if (flock($statefile, 2)) {
173                 
174                 %state = read_data_file($statefile);
175                 
176                 if ($frame < 0) {
177                         $frame = int($state{'last'}) + $frame +1;
178                         $framedata_path = join_path('/', DATA_PATH, $frame);
179                         %framedata = read_data_file($framedata_path);
180                 }
181                 
182                 if (
183                         int($state{'state'}) == STATE->{'waiting'} &&
184                         $frame == int($state{'last'}) &&
185                         $method ne 'HEAD' &&
186                         !$passwordOK
187                 ) {
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);
193                                 }
194                                 elsif ($state{'ip2'} ne $IP) {
195                                         if ($state{'ip2'} eq '') {
196                                                 $newstate{'ip2'} = $IP;
197                                                 write_data_file($statefile, '', '', \%newstate);
198                                         }
199                                         else {
200                                                 $newstate{'state'} = STATE->{'ready'};
201                                                 $newstate{'ip3'} = $IP;
202                                                 write_data_file($statefile, '', '', \%newstate);
203                                         }
204                                 }
205                         }
206                 }
207                 elsif (int($state{'state'}) == STATE->{'inactive'} && $frame == 1) {
208                         my %story;
209                         my $framefilename;
210                         my $inpath;
211                         my $outpath;
212                         
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) {
216                                 #ACTIVATE!
217                                 
218                                 $framedata{'ongtime'} = $time;
219                                 write_data_file($framedata_path, '', '', \%framedata);
220                                 $state{'state'} = 1;
221                                 $state{'last'} = 1;
222                                 $state {'ip1'} = '0.0.0.0';
223                                 $state {'ip2'} = '0.0.0.0';
224                                 $state {'ip3'} = '';
225                                 $state {'nextong'} = (int($time / 3600) + int($settings{'firstongtime'})) * 3600 ;
226                                 $state{'ongtime'} = int($settings{'firstongtime'});
227                                 
228                                 unless (defined($framedata{'ext'})){
229                                         $framedata{'ext'} = $default{'ext'};
230                                 }
231                                 
232                                 $framefilename = sprintf($settings{'frame'}, $frame, $framedata{'ext'});
233                                 $inpath  = join_path('/', DATA_PATH, $framefilename);
234                                 $outpath = join_path('/', WWW_PATH,  $framefilename);
235                                 
236                                 $gotolist{'title-1'}   = $framedata{'title'};
237                                 $gotolist{'ongtime-1'} = $framedata{'ongtime'};
238                                 
239                                 if (copy ($inpath, $outpath)) {
240                                         writeindex(WWW_INDEX_PATH);
241                                         write_data_file($statefile,     '', '', \%state);
242                                         write_data_file(DATA_LIST_PATH, '', '', \%gotolist);
243                                 }
244                                 else {
245                                         $state{'state'} = STATE->{'inactive'};
246                                 }
247                         }
248                 }
249         }
250         else {
251                 $state{'state'} = 0;
252         }
253         close ($statefile);
254 }
255 else {
256         $state{'state'} = 0;
257 }
258
259 %nextframedata=readdatafile(DATA_PATH.($frame+1));
260 foreach my $ind (keys %default) {
261         unless(defined($framedata{$ind})){
262                 $framedata{$ind}=$default{$ind};
263         }
264         unless(defined($nextframedata{$ind})){
265                 $nextframedata{$ind}=$default{$ind};
266         }
267 }
268
269 $hours=int($state{'nextong'})-$time;
270 $ongtime=int($state{'ongtime'});
271 if($ongtime == 0) {
272         $ongtime=int($settings{'ongtime'})
273 }
274
275 $showcommand = ($hours < ($ongtime*3600/3));
276 if($hours>0){
277         $seconds = sprintf('%02d',$hours % 60);
278         $hours = int($hours/60);
279         $minutes = sprintf('%02d',$hours % 60);
280         $hours = sprintf('%02d',int($hours/60));
281 }
282 elsif(($hours>=-15) && (int($state{'state'}) == 2)){
283         $seconds='NG';
284         $minutes='00';
285         $hours='00';
286 }
287 else{
288         $seconds='EE';
289         $minutes='EE';
290         $hours='EE';
291 }
292
293
294 if ($passwordOK || (int($state{'state'}) >= 1 && $frame <= int($state{'last'}) && $frame >= 0)) {
295         $access=1;
296 }
297 else {
298         $access=0;
299         %framedata = readdatafile(DATA_NOACCESS_PATH);
300         foreach my $ind (keys %default) {
301                 unless(defined($framedata{$ind})){
302                         $framedata{$ind}=$default{$ind};
303                 }
304         }
305 }
306
307 $textmode = int($cgi{'b'});
308 if($textmode > 2) {
309         $textmode = 0;
310 }
311
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";
320 # }
321 # print "\n>>>HTTP<<<\n\n";
322 # foreach my $ind (keys %http) {
323         # print $ind.': '.$http{$ind}."\n";
324 # }
325 # print "\n>>>CGI<<<\n\n";
326 # foreach my $ind (keys %cgi) {
327         # print $ind.'='.$cgi{$ind}."\n";
328 # }
329 # print "\n>>>FRAMEDATA<<<\n\n";
330 # foreach my $ind (keys %framedata) {
331         # print $ind.': '.$framedata{$ind}."\n";
332 # }
333 # print "\n>>>NEXTFRAMEDATA<<<\n\n";
334 # foreach my $ind (keys %nextframedata) {
335         # print $ind.': '.$nextframedata{$ind}."\n";
336 # }
337 # print "\n>>>SETTINGS<<<\n\n";
338 # foreach my $ind (keys %settings) {
339         # print $ind.': '.$settings{$ind}."\n";
340 # }
341 # print "\n>>>STATE<<<\n\n";
342 # foreach my $ind (keys %state) {
343         # print $ind.': '.$state{$ind}."\n";
344 # }
345
346 print "Content-type: text/html\n";
347 if(!$access) {
348         print "Status: 403 Forbidden\n";
349 }
350 print "\n";
351 if($method eq 'HEAD') {
352         exit;
353 }
354
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";
358 print '<title>';
359 unless($framedata{'title'} eq '' || $framedata{'title'} eq $settings{'story'}) {
360         print entityencode($framedata{'title'}).' &bull; ';
361 }
362 print entityencode($settings{'story'}).' &bull; '.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";
369 }
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";
373 }
374 if($frame==int($state{'last'}) && int($state{'state'}) >= 1 && int($state{'state'}) < 3) {
375         print '<!-- <script src="'.CGI_TIMER_PATH.'"></script> -->'."\n";
376 }
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";
380
381 print '<div id="inst" class="ins">'."\n";
382
383 print '<div id="title">'."\n";
384 print '<H1 id="titletext">'.entityencode($framedata{'title'}).'</H1>'."\n";
385 print '</div>'."\n";
386
387 print '</div><div id="framespace">'."\n";
388 if(!$access) {
389         print '<img src="'.CGI_PATH.$framedata{'frame'}.'" id ="frame" alt="'.$frame.'" title="'.entityencode($framedata{'title'}).'">'."\n";
390 }
391 else {
392         print'<img src="';
393         if($frame<=int($state{'last'}) && int($state{'state'}) >= 1) {
394                 print CGI_PATH.sprintf($settings{'frame'},$frame,$framedata{'ext'});
395         }
396         else {
397                 print CGI_FRAME_PATH.'/'.$frame.($passwordOK?('?p='.urlencode($password)):'');
398         }
399         print '" id="frame" alt="'.$frame.'" title="'.entityencode($framedata{'title'}).'">'."\n";
400 }
401 print '</div><div id="insb" class="ins">'."\n";
402
403 if($textmode==2){
404         print '<div id="chat">'."\n";
405         
406         if ($passwordOK || $frame<int($state{'last'}) || (int($state{'state'}) >= 2 && $showcommand)) {
407                 $framedata{'command'}=$nextframedata{'title'};
408         }
409         if ($access) {
410                 $framedata{'frame'}=sprintf($settings{'frame'},$frame,$framedata{'ext'});
411         }
412         
413         printdatafileht(%framedata);
414         print '</div>'."\n";
415 }
416 elsif($textmode==1){
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";
421 }
422 elsif($framedata{'content'} ne ''){
423         print '<div id="undertext">'."\n";
424         print bb2htf($framedata{'content'})."\n";
425         print '</div>'."\n";
426 }
427
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 '') {
432                 print 'br';
433         }
434         else {
435                 print 'ni';
436         }
437         print '">'.$hours.'</span>:<span id="ongm" class="';
438         if(int($state{'state'}) >= 2 || $state{'ip2'} ne '') {
439                 print 'br';
440         }
441         else {
442                 print 'ni';
443         }
444         print '">'.$minutes.'</span>:<span id="ongs" class="';
445         if(int($state{'state'}) >= 2 || $state{'ip3'} ne '') {
446                 print 'br';
447         }
448         else {
449                 print 'ni';
450         }
451         print '">'.$seconds.'</span>]<br>';
452 }
453 print '&gt;';
454 if (!$access){
455         print '<a href="'.CGI_VIEWER_PATH.'/-1">'.entityencode($framedata{'command'}).'</a><br>'."\n";
456 }
457 else {
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)):'').'">';
461                 }
462                 if($nextframedata{'title'} ne '') {
463                         print entityencode($nextframedata{'title'});
464                 }
465                 elsif ($passwordOK || $frame<int($state{'last'})) {
466                         print'<span class="inp">_</span>';
467                 }
468                 if ($passwordOK || $frame<int($state{'last'})) {
469                         print '</a>';
470                 }
471                 else {
472                         print'<span class="inp">_</span>';
473                 }
474         }
475         else {
476                 print'<span class="inp">_</span>';
477         }
478         print '<br>'."\n";
479 }
480 print '</div>'."\n";
481
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>';
486 }
487 if($frame != int($state{'last'})) {
488         print ' | <a href="'.CGI_VIEWER_PATH.'/'.int($state{'last'}).($passwordOK?('?p='.urlencode($password)):'').'">Now</a>';
489 }
490 print ' | <a href="'.CGI_GOTO_PATH.($passwordOK?('?p='.urlencode($password)):'').'">GOTO</a>'."\n";
491 print '<span style="float: right;">'."\n";
492
493 if ($textmode!=0) {
494         print '<a href="'.CGI_VIEWER_PATH.'/'.$frame.($passwordOK?('?p='.urlencode($password)):'').'">Without</a> | ';
495 }
496
497 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>';
498 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>';
499 print "\n</span>\n";
500
501 print '</div>'."\n";
502
503 print '</div>'."\n";
504
505 print '</div>'."\n";
506 print '<a href="/" class="cz">'.WEBSITE.'</a>'."\n";
507
508 print '</body></html>'."\n";
509
510 sub writeindex {
511         (my $indexpath) = @_;
512         my $indexfile;
513         my %framedata;
514         my %nextframedata;
515         my %default;
516         
517         if(ref($indexpath)) {
518                 $indexfile=$indexpath;
519                 unless (seek($indexfile, 0, 0)) {
520                         return 0;
521                 }
522         }
523         else {
524                 unless (open ($indexfile, ">", $indexpath)) {
525                         return 0;
526                 }
527         }
528         
529         %framedata = readdatafile(DATA_PATH.0);
530         %nextframedata = readdatafile(DATA_PATH.1);
531         %default=readdatafile(DATA_DEFAULT_PATH);
532         
533         foreach my $ind (keys %default) {
534                 unless(defined($framedata{$ind})){
535                         $framedata{$ind}=$default{$ind};
536                 }
537                 unless(defined($nextframedata{$ind})){
538                         $nextframedata{$ind}=$default{$ind};
539                 }
540         }
541         
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'}).' &bull; '.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";
555         
556         print $indexfile '<div id="inst" class="ins">'."\n";
557         
558         print $indexfile '<div id="title">'."\n";
559         print $indexfile '<H1 id="titletext">'.entityencode($settings{'story'}).'</H1>'."\n";
560         print $indexfile '</div>'."\n";
561         
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";
564         
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";
569         
570         print $indexfile '<div id="command">'."\n";
571         print $indexfile '&gt;<a href="'.CGI_VIEWER_PATH.'/1">'.entityencode($nextframedata{'title'}).'</a>'."\n";
572         print $indexfile '</div>'."\n";
573         
574         print $indexfile '<div id="underlinks">'."\n";
575
576         
577         # <span style="float: right;"><a href="/bsta/v/0?b=2">Info</a> | <a href="/bsta/v/0?b=1">BB</a></span>
578         
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";
582         
583         print $indexfile '</div>'."\n";
584         
585         print $indexfile '</div>'."\n";
586         print $indexfile '<a href="/" class="cz">'.WEBSITE.'</a>'."\n";
587         
588         print $indexfile '</body></html>'."\n";
589         
590         unless (ref($indexpath)) {
591                 close ($indexfile);
592         }
593         else {
594                 truncate ($indexfile , tell($indexfile));
595         }
596         
597         return 1;
598 }
599
600 sub bb2htf {
601         (my $bb) = @_;
602         my $tag;
603         my $tagvalue;
604         my $pretext;
605         my $posttext;
606         
607         while($bb =~ m/(###([^#;]*);)/g) {
608                 $tag = $1;
609                 $tagvalue = $2;
610                 $pretext = substr($bb,0,pos ($bb)-length($tag));
611                 $posttext = substr ($bb,pos ($bb));
612                 
613                 if ($tagvalue =~ /^att&([0-9]+)$/) {
614                         $tagvalue = CGI_ATTACH_PATH.'/'.int($1).($passwordOK?('?p='.urlencode($password)):'');
615                 }
616                 elsif ($tagvalue =~ /^vw&([0-9]+)$/) {
617                         $tagvalue = CGI_VIEWER_PATH.'/'.int($1).($passwordOK?('?p='.urlencode($password)):'');
618                 }
619                 elsif ($tagvalue =~ /^fr&([0-9]+)$/) {
620                         $tagvalue = CGI_FRAME_PATH.'/'.int($1).($passwordOK?('?p='.urlencode($password)):'');
621                 }
622                 else {
623                         $tagvalue = '';
624                 }
625                 
626                 $bb = $pretext.$tagvalue.$posttext;
627         }
628         
629         return bb2ht ($bb);
630 }
631
632 sub bb2bbf {
633         (my $bb) = @_;
634         my $tag;
635         my $tagvalue;
636         my $pretext;
637         my $posttext;
638         
639         while($bb =~ m/(###([^#;]*);)/g) {
640                 $tag = $1;
641                 $tagvalue = $2;
642                 $pretext = substr($bb,0,pos ($bb)-length($tag));
643                 $posttext = substr ($bb,pos ($bb));
644                 
645                 if ($tagvalue =~ /^att&([0-9]+)$/) {
646                         $tagvalue = 'http://'.WEBSITE.CGI_ATTACH_PATH.'/'.int($1);
647                 }
648                 elsif ($tagvalue =~ /^vw&([0-9]+)$/) {
649                         $tagvalue = 'http://'.WEBSITE.CGI_VIEWER_PATH.'/'.int($1);
650                 }
651                 elsif ($tagvalue =~ /^fr&([0-9]+)$/) {
652                         $tagvalue = 'http://'.WEBSITE.CGI_FRAME_PATH.'/'.int($1);
653                 }
654                 else {
655                         $tagvalue = '';
656                 }
657                 
658                 $bb = $pretext.$tagvalue.$posttext;
659         }
660         
661         return linehtml(bb2bb ($bb));
662 }