]> bicyclesonthemoon.info Git - ott/bsta/blob - viewer.1.pl
Initial state as of 31.08.2023
[ott/bsta] / viewer.1.pl
1 ###PERL;
2 #
3 # /bsta/v
4 # viewer is generated from viewer.1.pl.
5 # 09.01.2023
6 #
7 # The viewer interface
8 #
9 #    Copyright (C) 2016-2017, 2019-2020, 2023  Balthasar SzczepaƄski
10 #
11 #    This program is free software: you can redistribute it and/or modify
12 #    it under the terms of the GNU Affero General Public License as
13 #    published by the Free Software Foundation, either version 3 of the
14 #    License, or (at your option) any later version.
15 #
16 #    This program is distributed in the hope that it will be useful,
17 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
18 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 #    GNU Affero General Public License for more details.
20 #
21 #    You should have received a copy of the GNU Affero General Public License
22 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23
24 use strict;
25 #use warnings;
26 ###LIB;
27 use bsta_lib qw(failpage gethttpheader getcgi entityencode readdatafile writedatafile printdatafileht urlencode bb2ht bb2bb linehtml);
28 use File::Copy;
29
30 ###DATA_PATH;
31 ###DEFAULT_PATH;
32 ###SETTINGS_PATH;
33 ###STATE_PATH;
34 ###LOGO_PATH;
35 ###FAVICON_PATH;
36 ###WEBSITE;
37 ###WEBSITE_NAME;
38 ###CSS_PATH;
39 ###CGI_PATH;
40 ###FRAME_PATH;
41 ###VIEWER_PATH;
42 ###NOACCESS_PATH;
43 ###STORY_PATH;
44 ###WWW_PATH;
45 ###INDEX_PATH;
46 ###ATTACH_PATH;
47 ###INFO_PATH;
48 ###BBCODE_PATH;
49 ###GOTO_PATH;
50 ###LIST_PATH;
51 ###TIMER_PATH;
52
53 my %http;
54 my %cgi;
55 my %framedata;
56 my %nextframedata;
57 my %default;
58 my %settings;
59 my %state;
60 my %newstate;
61 my %gotolist;
62
63 my $time = time();
64 srand ($time-$$);
65
66 my $method;
67 my $frame;
68 my $password;
69 my $passwordOK;
70 my $IP;
71 my $access;
72 my $seconds;
73 my $minutes;
74 my $hours;
75 my $statefile;
76 my $showcommand;
77 my $ongtime;
78 my $textmode;
79
80 delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
81 ###PATH;
82
83 if ($ENV{'REQUEST_METHOD'} =~ /^(HEAD|GET|POST)$/) {
84         $method=$1;
85 }
86 else{
87         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);
88 }
89
90 %http = gethttpheader (\%ENV);
91 %cgi = getcgi($ENV{'QUERY_STRING'});
92
93 if ($method eq 'POST') {
94         if ($http{'content-type'} eq 'application/x-www-form-urlencoded') {
95                 my %cgipost=getcgi( <STDIN> );
96                 foreach my $ind (keys %cgipost) {
97                         $cgi{$ind}=$cgipost{$ind};
98                 }
99         }
100         # multipart not supported
101         else{
102                 exit failpage("Status: 415 Unsupported Media Type\n","415 Unsupported Media Type","Unsupported Content-type: $http{'content-type'}.");
103         }
104 }
105
106 if ($ENV{'HTTP_X_FORWARDED_FOR'} =~ /^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$/) {
107         $IP=$1;
108 }
109 elsif ($ENV{'REMOTE_ADDR'} =~ /^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$/) {
110         $IP=$1;
111 }
112 else {
113         $IP='0.0.0.0';
114 }
115
116 if ($cgi{'f'} =~ /^(.+)$/) {
117         $frame=int($1);
118 }
119 elsif ($ENV{'PATH_INFO'} =~ /^\/(.+)$/) {
120         $frame=int($1);
121 }
122 else {
123         $frame = 0;
124 }
125
126 if ($cgi{'p'} =~ /^(.+)$/) {
127         $password=$1;
128 }
129 else {
130         $password='';
131 }
132 # print "Content-type: text/plain\n\n";
133
134 %settings=readdatafile(SETTINGS_PATH);
135 %default=readdatafile(DEFAULT_PATH);
136 %framedata=readdatafile(DATA_PATH.$frame);
137 if($password eq $settings{'password'}){
138         $passwordOK = 1;
139 }
140 else{
141         $passwordOK = 0;
142 }
143
144 if (open ($statefile,"+<",STATE_PATH)){
145         if (flock($statefile,2)) {
146                 
147                 %state=readdatafile($statefile);
148                 
149                 if($frame<0) {
150                         $frame = int($state{'last'}) + $frame +1;
151                         %framedata=readdatafile(DATA_PATH.$frame);
152                 }
153                 
154                 if(int($state{'state'})==1 && $frame == int($state{'last'}) && $method ne 'HEAD' && !$passwordOK){
155                         my %newstate=%state;
156                         if($state{'ip1'} ne $IP) {
157                                 if ($state{'ip1'} eq '') {
158                                         $newstate{'ip1'} = $IP;
159                                         writedatafile($statefile,%newstate);
160                                 }
161                                 elsif($state{'ip2'} ne $IP) {
162                                         if ($state{'ip2'} eq '') {
163                                                 $newstate{'ip2'} = $IP;
164                                                 writedatafile($statefile,%newstate);
165                                         }
166                                         else {
167                                                 $newstate{'state'}=2;
168                                                 $newstate{'ip3'} = $IP;
169                                                 writedatafile($statefile,%newstate);
170                                         }
171                                 }
172                         }
173                 }
174                 elsif(int($state{'state'})==0 && $frame == 1) {
175                         my %story;
176                         my $inpath;
177                         my $outpath;
178                         
179                         %story = readdatafile(STORY_PATH);
180                         %gotolist=readdatafile(LIST_PATH);
181                         if(int($story{'state'}) == 17 && int($story{'pass'}) == 1) {
182                                 #ACTIVATE!
183                                 
184                                 $framedata{'ongtime'} = $time;
185                                 writedatafile(DATA_PATH.$frame,%framedata);
186                                 $state{'state'} = 1;
187                                 $state{'last'} = 1;
188                                 $state {'ip1'} = '0.0.0.0';
189                                 $state {'ip2'} = '0.0.0.0';
190                                 $state {'ip3'} = '';
191                                 $state {'nextong'} = (int($time / 3600) + int($settings{'firstongtime'})) * 3600 ;
192                                 $state{'ongtime'} = int($settings{'firstongtime'});
193                                 
194                                 unless(defined($framedata{'ext'})){
195                                         $framedata{'ext'}=$default{'ext'};
196                                 }
197                                 
198                                 $inpath = DATA_PATH.sprintf($settings{'frame'},$frame,$framedata{'ext'});
199                                 $outpath = WWW_PATH.sprintf($settings{'frame'},$frame,$framedata{'ext'});
200                                 
201                                 $gotolist{'title-1'}=$framedata{'title'};
202                                 $gotolist{'ongtime-1'}=$framedata{'ongtime'};
203                                 
204                                 if(copy ($inpath, $outpath)) {
205                                         writeindex(INDEX_PATH);
206                                         writedatafile($statefile,%state);
207                                         writedatafile(LIST_PATH,%gotolist);
208                                 }
209                                 else {
210                                         $state{'state'} = 0;
211                                 }
212                         }
213                 }
214         }
215         else {
216                 $state{'state'} = 0;
217         }
218         close ($statefile);
219 }
220 else {
221         $state{'state'} = 0;
222 }
223
224 %nextframedata=readdatafile(DATA_PATH.($frame+1));
225 foreach my $ind (keys %default) {
226         unless(defined($framedata{$ind})){
227                 $framedata{$ind}=$default{$ind};
228         }
229         unless(defined($nextframedata{$ind})){
230                 $nextframedata{$ind}=$default{$ind};
231         }
232 }
233
234 $hours=int($state{'nextong'})-$time;
235 $ongtime=int($state{'ongtime'});
236 if($ongtime == 0) {
237         $ongtime=int($settings{'ongtime'})
238 }
239
240 $showcommand = ($hours < ($ongtime*3600/3));
241 if($hours>0){
242         $seconds = sprintf('%02d',$hours % 60);
243         $hours = int($hours/60);
244         $minutes = sprintf('%02d',$hours % 60);
245         $hours = sprintf('%02d',int($hours/60));
246 }
247 elsif(($hours>=-15) && (int($state{'state'}) == 2)){
248         $seconds='NG';
249         $minutes='00';
250         $hours='00';
251 }
252 else{
253         $seconds='EE';
254         $minutes='EE';
255         $hours='EE';
256 }
257
258
259 if ($passwordOK || (int($state{'state'}) >= 1 && $frame <= int($state{'last'}) && $frame >= 0)) {
260         $access=1;
261 }
262 else {
263         $access=0;
264         %framedata = readdatafile(NOACCESS_PATH);
265         foreach my $ind (keys %default) {
266                 unless(defined($framedata{$ind})){
267                         $framedata{$ind}=$default{$ind};
268                 }
269         }
270 }
271
272 $textmode = int($cgi{'b'});
273 if($textmode > 2) {
274         $textmode = 0;
275 }
276
277 # print "Content-type: text/plain\n\n";
278 # print 'frame='.$frame."\n";
279 # print 'password='.$password."\n";
280 # print 'passwordOK='.$passwordOK."\n";
281 # print 'access='.$access."\n";
282 # print "\n>>>ENV<<<\n\n";
283 # foreach my $ind (keys %ENV) {
284         # print $ind.'='.$ENV{$ind}."\n";
285 # }
286 # print "\n>>>HTTP<<<\n\n";
287 # foreach my $ind (keys %http) {
288         # print $ind.': '.$http{$ind}."\n";
289 # }
290 # print "\n>>>CGI<<<\n\n";
291 # foreach my $ind (keys %cgi) {
292         # print $ind.'='.$cgi{$ind}."\n";
293 # }
294 # print "\n>>>FRAMEDATA<<<\n\n";
295 # foreach my $ind (keys %framedata) {
296         # print $ind.': '.$framedata{$ind}."\n";
297 # }
298 # print "\n>>>NEXTFRAMEDATA<<<\n\n";
299 # foreach my $ind (keys %nextframedata) {
300         # print $ind.': '.$nextframedata{$ind}."\n";
301 # }
302 # print "\n>>>SETTINGS<<<\n\n";
303 # foreach my $ind (keys %settings) {
304         # print $ind.': '.$settings{$ind}."\n";
305 # }
306 # print "\n>>>STATE<<<\n\n";
307 # foreach my $ind (keys %state) {
308         # print $ind.': '.$state{$ind}."\n";
309 # }
310
311 print "Content-type: text/html\n";
312 if(!$access) {
313         print "Status: 403 Forbidden\n";
314 }
315 print "\n";
316 if($method eq 'HEAD') {
317         exit;
318 }
319
320 print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "">'."\n";
321 print '<html lang="en"><head>'."\n";
322 print '<meta http-equiv="Content-type" content="text/html; charset=UTF-8">'."\n";
323 print '<title>';
324 unless($framedata{'title'} eq '' || $framedata{'title'} eq $settings{'story'}) {
325         print entityencode($framedata{'title'}).' &bull; ';
326 }
327 print entityencode($settings{'story'}).' &bull; '.WEBSITE_NAME.'</title>'."\n";
328 print '<link rel="icon" type="image/png" href="'.FAVICON_PATH.'">'."\n";
329 print '<link rel="stylesheet" href="'.CSS_PATH.'">'."\n";
330 print '<link rel="index" href="'.GOTO_PATH.($passwordOK?('?p='.urlencode($password)):'').'">'."\n";
331 print '<link rel="start" href="'.VIEWER_PATH.'/0'.($passwordOK?('?p='.urlencode($password)):'').'">'."\n";
332 if($frame>0 && $access) {
333         print '<link rel="prev" href="'.VIEWER_PATH.'/'.($frame - 1).($passwordOK?('?p='.urlencode($password)):'').'">'."\n";
334 }
335 if ($passwordOK || $frame<int($state{'last'})) {
336         print '<link rel="next" href="'.VIEWER_PATH.'/'.($frame + 1).($passwordOK?('?p='.urlencode($password)):'').'">'."\n";
337         print '<link rel="prefetch" href="'.VIEWER_PATH.'/'.($frame + 1).($passwordOK?('?p='.urlencode($password)):'').'">'."\n";
338 }
339 if($frame==int($state{'last'}) && int($state{'state'}) >= 1 && int($state{'state'}) < 3) {
340         print '<!-- <script src="'.TIMER_PATH.'"></script> -->'."\n";
341 }
342 print '</head><body>'."\n";
343 print '<a href="/"><img id="botmlogo" src="'.LOGO_PATH.'" alt="'.WEBSITE.'"></a>'."\n";
344 print '<div id="all">'."\n";
345
346 print '<div id="inst" class="ins">'."\n";
347
348 print '<div id="title">'."\n";
349 print '<H1 id="titletext">'.entityencode($framedata{'title'}).'</H1>'."\n";
350 print '</div>'."\n";
351
352 print '</div><div id="framespace">'."\n";
353 if(!$access) {
354         print '<img src="'.CGI_PATH.$framedata{'frame'}.'" id ="frame" alt="'.$frame.'" title="'.entityencode($framedata{'title'}).'">'."\n";
355 }
356 else {
357         print'<img src="';
358         if($frame<=int($state{'last'}) && int($state{'state'}) >= 1) {
359                 print CGI_PATH.sprintf($settings{'frame'},$frame,$framedata{'ext'});
360         }
361         else {
362                 print FRAME_PATH.'/'.$frame.($passwordOK?('?p='.urlencode($password)):'');
363         }
364         print '" id="frame" alt="'.$frame.'" title="'.entityencode($framedata{'title'}).'">'."\n";
365 }
366 print '</div><div id="insb" class="ins">'."\n";
367
368 if($textmode==2){
369         print '<div id="chat">'."\n";
370         
371         if ($passwordOK || $frame<int($state{'last'}) || (int($state{'state'}) >= 2 && $showcommand)) {
372                 $framedata{'command'}=$nextframedata{'title'};
373         }
374         if ($access) {
375                 $framedata{'frame'}=sprintf($settings{'frame'},$frame,$framedata{'ext'});
376         }
377         
378         printdatafileht(%framedata);
379         print '</div>'."\n";
380 }
381 elsif($textmode==1){
382         print '<div id="chat">'."\n";
383         print '[quote][center][size=200]'.entityencode($framedata{'title'}).'[/size]<br>'."\n";
384         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";
385         print bb2bbf($framedata{'content'}).'[/quote]</div>'."\n";
386 }
387 elsif($framedata{'content'} ne ''){
388         print '<div id="undertext">'."\n";
389         print bb2htf($framedata{'content'})."\n";
390         print '</div>'."\n";
391 }
392
393 print '<div id="command">'."\n";
394 if($frame==int($state{'last'}) && int($state{'state'}) >= 1 && int($state{'state'}) < 3) {
395         print '[<span id="ongh" class="';
396         if(int($state{'state'}) >= 2 || $state{'ip1'} ne '') {
397                 print 'br';
398         }
399         else {
400                 print 'ni';
401         }
402         print '">'.$hours.'</span>:<span id="ongm" class="';
403         if(int($state{'state'}) >= 2 || $state{'ip2'} ne '') {
404                 print 'br';
405         }
406         else {
407                 print 'ni';
408         }
409         print '">'.$minutes.'</span>:<span id="ongs" class="';
410         if(int($state{'state'}) >= 2 || $state{'ip3'} ne '') {
411                 print 'br';
412         }
413         else {
414                 print 'ni';
415         }
416         print '">'.$seconds.'</span>]<br>';
417 }
418 print '&gt;';
419 if (!$access){
420         print '<a href="'.VIEWER_PATH.'/-1">'.entityencode($framedata{'command'}).'</a><br>'."\n";
421 }
422 else {
423         if ($passwordOK || $frame<int($state{'last'}) || (int($state{'state'}) >= 2 && $showcommand)) {
424                 if ($passwordOK || $frame<int($state{'last'})) {
425                         print '<a href="'.VIEWER_PATH.'/'.($frame + 1).($passwordOK?('?p='.urlencode($password)):'').'">';
426                 }
427                 if($nextframedata{'title'} ne '') {
428                         print entityencode($nextframedata{'title'});
429                 }
430                 elsif ($passwordOK || $frame<int($state{'last'})) {
431                         print'<span class="inp">_</span>';
432                 }
433                 if ($passwordOK || $frame<int($state{'last'})) {
434                         print '</a>';
435                 }
436                 else {
437                         print'<span class="inp">_</span>';
438                 }
439         }
440         else {
441                 print'<span class="inp">_</span>';
442         }
443         print '<br>'."\n";
444 }
445 print '</div>'."\n";
446
447 print '<div id="underlinks">'."\n";
448 print '<a href="'.CGI_PATH.'">Once again</a>';
449 if($frame>0 && $access) {
450         print ' | <a href="'.VIEWER_PATH.'/'.($frame-1).($passwordOK?('?p='.urlencode($password)):'').'">Before</a>';
451 }
452 if($frame != int($state{'last'})) {
453         print ' | <a href="'.VIEWER_PATH.'/'.int($state{'last'}).($passwordOK?('?p='.urlencode($password)):'').'">Now</a>';
454 }
455 print ' | <a href="'.GOTO_PATH.($passwordOK?('?p='.urlencode($password)):'').'">GOTO</a>'."\n";
456 print '<span style="float: right;">'."\n";
457
458 if ($textmode!=0) {
459         print '<a href="'.VIEWER_PATH.'/'.$frame.($passwordOK?('?p='.urlencode($password)):'').'">Without</a> | ';
460 }
461
462 print '<a href="'.(($textmode==2)?(INFO_PATH.'/'.$frame.($passwordOK?('?p='.urlencode($password)):'')):(VIEWER_PATH.'/'.$frame.'?b=2'.($passwordOK?('&amp;p='.urlencode($password)):''))).'">Info</a>';
463 print ' | <a href="'.(($textmode==1)?(BBCODE_PATH.'/'.$frame.($passwordOK?('?p='.urlencode($password)):'')):(VIEWER_PATH.'/'.$frame.'?b=1'.($passwordOK?('&amp;p='.urlencode($password)):''))).'">BB</a>';
464 print "\n</span>\n";
465
466 print '</div>'."\n";
467
468 print '</div>'."\n";
469
470 print '</div>'."\n";
471 print '<a href="/" class="cz">'.WEBSITE.'</a>'."\n";
472
473 print '</body></html>'."\n";
474
475 sub writeindex {
476         (my $indexpath) = @_;
477         my $indexfile;
478         my %framedata;
479         my %nextframedata;
480         my %default;
481         
482         if(ref($indexpath)) {
483                 $indexfile=$indexpath;
484                 unless (seek($indexfile, 0, 0)) {
485                         return 0;
486                 }
487         }
488         else {
489                 unless (open ($indexfile, ">", $indexpath)) {
490                         return 0;
491                 }
492         }
493         
494         %framedata = readdatafile(DATA_PATH.0);
495         %nextframedata = readdatafile(DATA_PATH.1);
496         %default=readdatafile(DEFAULT_PATH);
497         
498         foreach my $ind (keys %default) {
499                 unless(defined($framedata{$ind})){
500                         $framedata{$ind}=$default{$ind};
501                 }
502                 unless(defined($nextframedata{$ind})){
503                         $nextframedata{$ind}=$default{$ind};
504                 }
505         }
506         
507         print $indexfile '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "">'."\n";
508         print $indexfile '<html lang="en"><head>'."\n";
509         print $indexfile '<meta http-equiv="Content-type" content="text/html; charset=UTF-8">'."\n";
510         print $indexfile '<title>'.entityencode($settings{'story'}).' &bull; '.WEBSITE_NAME.'</title>'."\n";
511         print $indexfile '<link rel="icon" type="image/png" href="'.FAVICON_PATH.'">'."\n";
512         print $indexfile '<link rel="stylesheet" href="'.CSS_PATH.'">'."\n";
513         print $indexfile '<link rel="index" href="'.GOTO_PATH.'">'."\n";
514         print $indexfile '<link rel="start" href="'.VIEWER_PATH.'/0">'."\n";
515         print $indexfile '<link rel="next" href="'.VIEWER_PATH.'/1">'."\n";
516         print $indexfile '<link rel="prefetch" href="'.VIEWER_PATH.'/1">'."\n";
517         print $indexfile '</head><body>'."\n";
518         print $indexfile '<a href="/"><img id="botmlogo" src="'.LOGO_PATH.'" alt="'.WEBSITE.'"></a>'."\n";
519         print $indexfile '<div id="all">'."\n";
520         
521         print $indexfile '<div id="inst" class="ins">'."\n";
522         
523         print $indexfile '<div id="title">'."\n";
524         print $indexfile '<H1 id="titletext">'.entityencode($settings{'story'}).'</H1>'."\n";
525         print $indexfile '</div>'."\n";
526         
527         print $indexfile '</div><div id="framespace">'."\n";
528         print $indexfile '<img src="'.CGI_PATH.sprintf($settings{'frame'},0,$framedata{'ext'}).'" title="'.entityencode($framedata{'title'}).'" alt="0" id="frame">'."\n";
529         
530         print $indexfile '</div><div id="insb" class="ins">'."\n";
531         print $indexfile '<div id="undertext">'."\n";
532         print $indexfile bb2htf($framedata{'content'})."\n";
533         print $indexfile '</div>'."\n";
534         
535         print $indexfile '<div id="command">'."\n";
536         print $indexfile '&gt;<a href="'.VIEWER_PATH.'/1">'.entityencode($nextframedata{'title'}).'</a>'."\n";
537         print $indexfile '</div>'."\n";
538         
539         print $indexfile '<div id="underlinks">'."\n";
540
541         
542         # <span style="float: right;"><a href="/bsta/v/0?b=2">Info</a> | <a href="/bsta/v/0?b=1">BB</a></span>
543         
544         print $indexfile '<a href="'.VIEWER_PATH.'/-1">Now</a> | <a href="'.GOTO_PATH.'">GOTO</a>';
545         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";
546         print $indexfile '</div>'."\n";
547         
548         print $indexfile '</div>'."\n";
549         
550         print $indexfile '</div>'."\n";
551         print $indexfile '<a href="/" class="cz">'.WEBSITE.'</a>'."\n";
552         
553         print $indexfile '</body></html>'."\n";
554         
555         unless (ref($indexpath)) {
556                 close ($indexfile);
557         }
558         else {
559                 truncate ($indexfile , tell($indexfile));
560         }
561         
562         return 1;
563 }
564
565 sub bb2htf {
566         (my $bb) = @_;
567         my $tag;
568         my $tagvalue;
569         my $pretext;
570         my $posttext;
571         
572         while($bb =~ m/(###([^#;]*);)/g) {
573                 $tag = $1;
574                 $tagvalue = $2;
575                 $pretext = substr($bb,0,pos ($bb)-length($tag));
576                 $posttext = substr ($bb,pos ($bb));
577                 
578                 if ($tagvalue =~ /^att&([0-9]+)$/) {
579                         $tagvalue = ATTACH_PATH.'/'.int($1).($passwordOK?('?p='.urlencode($password)):'');
580                 }
581                 elsif ($tagvalue =~ /^vw&([0-9]+)$/) {
582                         $tagvalue = VIEWER_PATH.'/'.int($1).($passwordOK?('?p='.urlencode($password)):'');
583                 }
584                 elsif ($tagvalue =~ /^fr&([0-9]+)$/) {
585                         $tagvalue = FRAME_PATH.'/'.int($1).($passwordOK?('?p='.urlencode($password)):'');
586                 }
587                 else {
588                         $tagvalue = '';
589                 }
590                 
591                 $bb = $pretext.$tagvalue.$posttext;
592         }
593         
594         return bb2ht ($bb);
595 }
596
597 sub bb2bbf {
598         (my $bb) = @_;
599         my $tag;
600         my $tagvalue;
601         my $pretext;
602         my $posttext;
603         
604         while($bb =~ m/(###([^#;]*);)/g) {
605                 $tag = $1;
606                 $tagvalue = $2;
607                 $pretext = substr($bb,0,pos ($bb)-length($tag));
608                 $posttext = substr ($bb,pos ($bb));
609                 
610                 if ($tagvalue =~ /^att&([0-9]+)$/) {
611                         $tagvalue = 'http://'.WEBSITE.ATTACH_PATH.'/'.int($1);
612                 }
613                 elsif ($tagvalue =~ /^vw&([0-9]+)$/) {
614                         $tagvalue = 'http://'.WEBSITE.VIEWER_PATH.'/'.int($1);
615                 }
616                 elsif ($tagvalue =~ /^fr&([0-9]+)$/) {
617                         $tagvalue = 'http://'.WEBSITE.FRAME_PATH.'/'.int($1);
618                 }
619                 else {
620                         $tagvalue = '';
621                 }
622                 
623                 $bb = $pretext.$tagvalue.$posttext;
624         }
625         
626         return linehtml(bb2bb ($bb));
627 }