1 # bsta_lib.pm is generated from bsta_lib.1.pm
5 # Copyright (C) 2016, 2017, 2019, 2020, 2022, 2023 Balthasar SzczepaĆski
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as
9 # published by the Free Software Foundation, either version 3 of the
10 # License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
27 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
29 ###PERL_EXPORT_VERSION: our $VERSION = 'x.x.x';
30 our @ISA = qw(Exporter);
34 'failpage', 'fail_method', 'fail_content_type',
35 'get_remote_addr', 'get_frame', 'get_password',
40 'readdatafile', 'writedatafile', 'printdatafile', # TO REMOVE
41 'entityencode', # TO REMOVE
42 'printdatafileht', # TO REMOVE ???
43 'gethttpheader', 'getcgi', # TO REMOVE
44 'urldecode', # TO REMOVE
45 'urlencode', # TO REMOVE
46 'linehtml', # TO REMOVE
47 'bb2ht', 'bb2bb' # TO REMOVE
50 ###PERL_LIB: use lib /botm/lib/bsta
52 'url_query_decode', 'url_query_encode',
53 'url_decode', 'url_encode',
54 'html_entity_encode_dec',
57 'read_data_file', 'write_data_file'
60 ###PERL_CGI_PATH: CGI_PATH = /bsta/
61 ###PERL_CGI_BBCODE_PATH: CGI_BBCODE_PATH = /bsta/b
62 ###PERL_CGI_CSS_PATH: CGI_CSS_PATH = /bsta/bsta.css
63 ###PERL_CGI_FRAME_PATH: CGI_FRAME_PATH = /bsta/f
64 ###PERL_CGI_GOTO_PATH: CGI_GOTO_PATH = /bsta/g
65 ###PERL_CGI_INFO_PATH: CGI_INFO_PATH = /bsta/i
66 ###PERL_CGI_LOGO_PATH: CGI_LOGO_PATH = /bsta/botmlogo.png
67 ###PERL_CGI_TIMER_PATH: CGI_TIMER_PATH = /bsta/timer.js
68 ###PERL_CGI_VIEWER_PATH: CGI_VIEWER_PATH = /bsta/v
70 ###PERL_SCHEME: SCHEME = http
71 ###PERL_WEBSITE: WEBSITE = 1190.bicyclesonthemoon.info
72 ###PERL_FAVICON_PATH: FAVICON_PATH = /img/favicon.png
75 use constant STATE => {
81 use constant TEXT_MODE => {
87 use constant tags_bbcode => {
94 'ni' => '[color=#0057AF]',
96 'br' => '[color=#BB6622]',
98 'po' => '[color=#FF8800]',
109 '/list' => '[/list]',
113 '/?' => '[/unknown!]',
115 use constant tags_html => {
118 'fq' => '<div class="fq">',
120 'tq' => '<div class="tq">',
122 'ni' => '<span class="ni">',
124 'br' => '<span class="br">',
126 'po' => '<span class="po">',
128 'url' => '<a href="#">',#think: how to add selfincluding?
129 'url=' => '<a href="',
135 'list=' => '<ol style="list-style-type: ',
136 'list=1' => 'decimal',
137 'list=A' => 'upper-alpha',
138 'list=a' => 'lower-alpha',
139 'list=I' => 'upper-roman',
140 'list=i' => 'lower-roman',
147 '/?' => '[/unknown!]',
152 # Function to return an error page
153 # arguments: 1 - header fields, 2 - page title, 3 - error message, 4 method
155 (my $header, my $title, my $message, my $method) = @_;
159 if($method eq 'HEAD') {
163 print "Content-type: text/html; charset=UTF-8\n\n";
165 print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "">'."\n";
166 print '<html lang="en"><head>'."\n";
167 print '<meta http-equiv="Content-type" content="text/html; charset=UTF-8">'."\n";
169 print '<title>'.html_entity_encode_dec($title, 1).'</title>'."\n";
171 print '</head><body>'."\n";
173 print '<h1>'.html_entity_encode_dec($title, 1).'</h1>'."\n";
175 if ($message ne '') {
176 print '<p>'.html_entity_encode_dec($message, 1).'</p>'."\n";
178 print '</body></html>'."\n";
182 (my $method, my $allowed) = @_;
184 my $header = "Status: 405 Method Not Allowed\n";
185 if ($allowed ne '') {
186 $header .= "Allow: $allowed\n";
190 "405 Method Not Allowed",
191 "The interface does not support the $method method.",
196 sub fail_content_type
198 (my $content_type, my $method) = @_;
201 "Status: 415 Unsupported Media Type\n",
202 "415 Unsupported Media Type",
203 "Unsupported Content-type: $content_type.",
208 # function to obtain address of remote agent
209 sub get_remote_addr {
210 if ($ENV{'HTTP_X_FORWARDED_FOR'} =~ /^.+$/) {
213 elsif ($ENV{'REMOTE_ADDR'} =~ /^.+$/) {
221 # function to obtain frame number
225 if ($cgi{'f'} =~ /^.+$/) {
228 elsif ($ENV{'PATH_INFO'} =~ /^\/(.+)$/) {
236 # function to obtain password
240 if ($cgi{'p'} =~ /^.+$/) {
251 foreach my $settings (@_) {
252 foreach my $ind (keys %$settings) {
253 $final_settings{$ind} = $settings->{$ind};
256 return %final_settings;
260 # function to encode entities, decimal,
262 (my $t, my $all) = @_;
263 return html_entity_encode_dec($t, 1, $all);
267 # function to get values of http header fields. Returns a hash. names of header
268 # fields are lowercase
272 return read_header_env($env);
276 # The function to get CGI parameters from string.
277 # Format is: name=url_encoded_value&name=url_encoded_value& ... &name=url_encoded_value
279 return url_query_decode($_[0]);
283 # Function for decoding URL-encoded text
285 return url_decode($_[0]);
289 # Function to read data from datafiles.
290 # Very similar to http header file reading. (function readheaderfile() in proxy
295 # 1. After field name and colon there must be exactly one whitespace (space or
296 # tab). Any other leading or trailing whitespace (but not the newline character
297 # at the end of the line) is treated as part of the field value.
299 # 2. Instead of colon an equal sign can be used. The number of whitespaces after
300 # it is then zero and not one.
302 # 3. When header field is split into multiple lines the next lines must start
303 # with exactly one whitespace (tab or space) Any other leading or trailing
304 # whitespace (but not the newline character at the end of the line) is treated
305 # as part of the field value. the lines will be joined with a newline between
308 # 4. When the same field name appears it replaces the previous one.
310 # 5. Line separator is LF and not CR LF. The CR character is treated as part of
313 # 6. After the end of header (double newline) all next lines are treated as the
314 # value of the "content" field.
316 # Returns a hash containing the values.
317 # Names are case sensitive and are converted to lowercase
319 # Argument can be a path or a file handle. In case of a file handle it will just
320 # read the file. In case of path it opens the file before reading and closes
321 # after. On failure (file not open) returns empty hash.
326 return read_data_file($datapath);
330 # the function to write data to datafiles (see readdatafile() description)
332 # First argument can be a path or a file handle. In case of a file handle it
333 # will just write the file. In case of path it opens the file before writing and
336 # On failure (file not open) returns 0.
337 # On success returns 1.
340 (my $headerpath, my %header) = @_;
342 return write_data_file($headerpath, '', 0, \%header);
346 # the function to print data to stdout (see readdatafile() description)
348 # On success returns 1.
353 return write_data_file(\*STDOUT, '', 0, \%header);
357 # the function to print data to stdout as html (see readdatafile() description)
359 # On success returns 1.
361 sub printdatafileht {
364 print_html_data(\*STDOUT, \%header);
370 (my $t, my $all) = @_;
371 return url_encode($t, '', $all);
375 # different & simpler implementation than in post library
379 #analyse bbcode text to build tag tree #TODO make [/*] optional!
381 (my $bb, my $printdebug) = @_;
394 $bbtree{"_.name" } = "ht";
395 $bbtree{"_.value" } = '';
396 $bbtree{"_.type" } = "tag";
397 $bbtree{"_.count" } = 0;
398 $bbtree{"_.closed"} = 0;
399 $debug .= debug($printdebug,
401 "<!--GENERATING BBCODE TREE:\n".
402 '[_]automatic tag: [ht]'."\n"
406 my $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
408 if($bb =~ m/\[(\/?)([a-z]+|\*)(=([^\[\]]*))?\]/g) {
416 if ($pre_text ne '') {
417 $debug .= debug($printdebug, "[$new_ind]text: $pre_text\n");
418 $bbtree{$new_ind.'.type' } = 'text';
419 $bbtree{$new_ind.'.value'} = $pre_text;
420 $bbtree{ $ind.'.count'}+= 1;
421 $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
424 if($tag_name =~ /^(fq|tq|br|ni|po|url|i|list|\*)$/) {
425 if ($tag_end ne '') {
427 ($tag_name ne $bbtree{$ind.'.name'}) ||
430 $debug .= debug($printdebug, "[$new_ind]text: $tag\n");
431 $bbtree{$new_ind.'.type' } = 'text';
432 $bbtree{$new_ind.'.value'} = $tag;
433 $bbtree{ $ind.'.count'}+= 1;
434 # $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
437 $debug .= debug($printdebug, "[$new_$ind]tag: $tag\n");
438 $bbtree{$new_ind.'.type' } = 'tag';
439 $bbtree{$new_ind.'.name' } = '/'.$tag_name;
440 $bbtree{$new_ind.'.value' } = $tag_value;
441 $bbtree{ $ind.'.count' }+= 1;
442 $bbtree{ $ind.'.closed'} = 1;
444 $ind =~ s/\.[0-9]+$//;
445 # $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
450 $debug .= debug($printdebug, "[$new_ind]tag: $tag\n");
451 $bbtree{$new_ind.'.type' } = 'tag';
452 $bbtree{$new_ind.'.name' } = $tag_name;
453 $bbtree{$new_ind.'.value' } = $tag_value;
454 $bbtree{$new_ind.'.count' } = 0;
455 $bbtree{$new_ind.'.closed'} = 0;
456 $bbtree{ $ind.'.count' }+= 1;
459 # $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
463 $debug .= debug($printdebug, "[$new_ind]text: $tag\n");
464 $bbtree{$new_ind.'.type' } = 'text';
465 $bbtree{$new_ind.'.value'} = $tag;
466 $bbtree{ $ind.'.count'}+= 1;
467 # $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
471 $debug .= debug($printdebug, "[$new_ind]text: $bb\n");
472 $bbtree{$new_ind.'.type' } = 'text';
473 $bbtree{$new_ind.'.value'} = $bb;
474 $bbtree{ $ind.'.count'}+= 1;
475 # $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
479 my $final_ind = '_.'.$bbtree{"_.count"}
480 $debug .= debug($printdebug, "[$final_ind]automatic tag: [/ht]\n -->\n");
481 $bbtree{$final_ind.'.type' } = "tag";
482 $bbtree{$final_ind.'.name' } = '/ht';
483 $bbtree{ '_.count' }+= 1;
484 $bbtree{ '_.closed'} = 1;
486 return ($debug, %bbtree);
489 #convert tag tree to final text
491 (my $printdebug, my $debug, my $lang, my $bbtree) = @_;
496 my $tags = ($lang eq 'html') ? tags_html : tags_bbcode;
497 my $escape = ($lang eq 'html');
499 # $debug .= debug($printdebug, "\n****\n");
500 # foreach my $iiii (keys %tags) {
501 # $debug .= debug($printdebug, $iiii.'='.$tags->{$iiii}."\n");
503 # $debug .= debug($printdebug, "****\n");
508 $debug .= debug($printdebug, "\n<!--PROCESSING BBCODE TREE:\n");
510 while ($level >= 0) {
512 $debug .= debug($printdebug, "[$level:$ind:".int($bbtree->{$ind.'.count'})."]");
514 if ($bbtree->{$ind.'.type'} eq 'text') {
515 my $text = $bbtree->{$ind.'.value'}
516 $debug .= debug($printdebug, "text: ".$text);
517 $out .= $escape ? html_encode_line($text) : $text;
522 elsif ($bbtree->{$ind.'.type'} eq 'tag') {
523 my $name = $bbtree->{$ind.'.name'};
525 if ($name =~ /^\//) {
526 $debug .= debug($printdebug, "tag: [$name]");
528 $indd =~ s/\.([0-9]+)$//;
529 if (exists($tags->{$name.'='}) && ($bbtree->{$indd.'.value'} ne '')) {
530 $out .= $tags->{$name.'='};
532 elsif (exists($tags->{$name})) {
533 $out .= $tags->{$name};
536 $out .= $tags->{'/?'};
537 $debug .= debug($printdebug, "[unknown!]");
540 $ind =~ s/\.([0-9]+)$//;
542 $debug .= debug($printdebug, "[<]");
553 my $value = $bbtree->{$ind.'.value'};
554 if($bbtree->{$ind.'.closed'} ne '') {
555 $debug .= debug($printdebug, "tag: [$name]");
557 if (exists($tags->{$name.'='}) && ($value ne '')) {
558 if (exists($tags->{$name.'='.$value})) {
561 $tags->{$name.'='.$value'} .
567 ($escape ? html_entity_encode_dec($value, 1) : $value) .
571 elsif (exists($tags->{$name})) {
572 $out .= $tags->{$name};
575 $out .= $out.$tags->{'?'};
576 $debug .= debug($printdebug, "[unknown!]");
580 $debug .= debug($printdebug, "unclosed tag: [$name]");
581 my $text = $name . (($value ne '') ? ('='.$value) : '');
582 $out .= '['.($escape ? html_encode_line($text) : $text).']';
584 if ($bbtree->{$ind.'.count'} > 0) {
587 $debug .= debug($printdebug, "[>]");
596 $debug .= debug($printdebug, "unknown thing: ".$bbtree->{$ind.'.type'});
597 #should not occur with a correct bbtree
598 #unless unimplemented
599 $ind =~ s/\.([0-9]+)$//;
601 $debug .= debug($printdebug, "[<ui]");
610 if ($goto_next ne '') {
612 $ind =~ s/\.([0-9]+)$//;
614 if ($i < $bbtree->{$ind.'.count'}){
621 # should not occur with a correct bbtree
622 $debug .= debug($printdebug, "[<$goto_next]");
625 } while ($level >= 0);}
628 $debug .= debug($printdebug, "[>$level:$ind]\n");
631 $debug .= debug($printdebug, "-->\n");
632 return ($debug, $out);
637 (my $bb, my $printdebug) = @_;
642 ($debug, %bbtree) = bbtree($bb, $printdebug);
643 ($debug, $ht) = convtree ($printdebug, $debug, 'html', %bbtree);
650 (my $bb, my $printdebug) = @_;
655 ($debug, %bbtree) = bbtree($bb, $printdebug);
656 ($debug, $ht) = convtree ($printdebug, $debug, 'bb', %bbtree);
663 return bb_to_html(@_);
668 return bb_to_bbcode(@_);
677 while ($bb =~ m/###([^#;]*);/g) {
682 if ($value =~ /^att&([0-9]+)$/) {
689 elsif ($value =~ /^vw&([0-9]+)$/) {
696 elsif ($value =~ /^fr&([0-9]+)$/) {
706 $bb = $before . $value . $after;
712 sub html_encode_line {
713 (my $text, my $non_ascii, my $all) = @_;
717 $text =~ s/\r\n/\n/gs;
720 while ($text ne '') {
721 $ind = index($text, "\n");
723 $html .= html_entity_encode_dec(substr($text, 0, $ind), $non_ascii, $all)."<br>\n";
724 $text = substr($text, $ind+1);
728 $html .= html_entity_encode_dec($text, 1);
737 return html_encode_line($_[0], 1);
741 (my $print, my $text) = @_;
751 sub print_html_start {
753 print $fh '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "">'."\n";
754 print $fh '<html lang="en">'."\n";
759 print $fh '</html>'."\n";
762 sub print_html_head_start {
763 print $fh '<head>'."\n";
764 print $fh '<meta http-equiv="Content-type" content="text/html; charset=UTF-8">'."\n";
765 print $fh '<link rel="icon" type="image/png" href="'.html_entity_encode_dec(FAVICON_PATH(),1).'">'."\n";
766 print $fh '<link rel="stylesheet" href="'.html_entity_encode_dec(CGI_CSS_PATH(),1).'">'."\n";
769 sub print_html_head_end {
771 print $fh '</head>'."\n";
774 sub print_html_body_start {
776 print $fh '<body>'."\n";
777 print $fh '<a href="/"><img id="botmlogo" src="'.html_entity_encode_dec(CGI_LOGO_PATH(),1).'" alt="'.html_entity_encode_dec(WEBSITE(),1).'"></a>'."\n";
778 print $fh '<div id="all">'."\n";
781 sub print_html_body_end {
783 print $fh '</div>'."\n";
784 print $fh '<a href="/" class="cz">'.html_entity_encode_dec(WEBSITE(),1).'</a>'."\n";
785 print $fh '</body></html>'."\n";
788 sub print_html_data {
789 (my $fh, my $data) = @_;
791 foreach my $key (keys %$data) {
792 unless ($key eq 'content') {
793 my $val = $data->{'ind'};
794 $val =~ s/(\r)?\n/\n /gs; # does the space make sense in HTML anyway?
795 print $fh html_encode_line("$key: $val\n", 1);
798 print $fh html_encode_line("\n".$data->{'content'});
801 sub print_viewer_page {
812 my $frame = int($context->{'frame'});
813 # my $prev_frame = $frame - 1;
814 my $next_frame = $frame + 1;
816 my $title = $frame_data->{'title'};
817 my $command = $next_frame_data->{'title'};
819 my $access = $context->{'access'};
820 my $password_ok = $context->{'password_ok'};
821 my $static = $context->{'static'};
823 my $text_mode = int($context->{'text_mode'});
824 my $timer_unlocked = int($context->{'timer_unlocked'});
825 my $timer = int($context->{'timer'};
827 my $last_frame = int($state->{'last'});
828 my $ong_state = int($state->{'state'});
830 my $timer_color_h = (($timer_unlocked >= 1) || ($ong_state >= STATE->{'ready'})) ? 'br' : 'ni';
831 my $timer_color_m = (($timer_unlocked >= 2) || ($ong_state >= STATE->{'ready'})) ? 'br' : 'ni';
832 my $timer_color_s = (($timer_unlocked >= 3) || ($ong_state >= STATE->{'ready'})) ? 'br' : 'ni';
838 $timer_s = sprintf('%02d', $timer % 60);
839 $timer_h = int($timer / 60);
840 $timer_m = sprintf('%02d', $timer_h % 60);
841 $timer_h = sprintf('%02d', $timer_h / 60);
843 elsif (($timer >= -15) && ($ong_state >= STATE->{'ready'})) {
854 my $prev_available = (($frame > 0) && $access');
855 my $next_available = ($password_ok' || ($next_frame <= $last_frame));
856 my $prefetch_next = (
858 ($next_frame < $tast_frame) || (
859 ($next_frame <= $last_frame) &&
860 ($ong_state >= STATE->{'ready'})
864 ($frame == $last_frame)) && (
865 ($ong_state == STATE->{'waiting'}) ||
866 ($ong_state == STATE->{'ready'})
871 ($frame' < $last_frame) || (
872 ($ong_state) >= STATE->{'ready'}) &&
873 $context->{'show_command'}
876 my $show_command_link = ($next_available || (!$access));
877 my $show_command_cursor = (($frame == $last_frame) || ($command eq ''));
878 my $frame_indirect = !(
880 ($frame <= $last_frame) &&
881 ($ong_state > STATE->{'inactive'})
884 my $nextframe_indirect = !($next_frame < $last_frame);
888 my $base_url = CGI_PATH;
889 my $goto_url = CGI_GOTO_PATH;
890 my $info_url = CGI_INFO_PATH;
891 my $bbcode_url = CGI_BBCODE_PATH;
892 my $timer_url = CGI_TIMER_PATH;
893 my $viewer_full_url = merge_url(
894 {'scheme' => SCHEME(), 'host' => WEBSITE()},
895 {'path' => CGI_VIEWER_PATH()},
898 my $viewer_url = merge_url(
899 {'path' => CGI_VIEWER_PATH()},
902 my $viewer_0_url = merge_url(
903 {'path' => CGI_VIEWER_PATH()},
906 my $viewer_prev_url = merge_url(
907 {'path' => CGI_VIEWER_PATH()},
910 my $viewer_next_url = merge_url(
911 {'path' => CGI_VIEWER_PATH()},
912 {'path' => $next_frame}
914 my $viewer_last_url = merge_url(
915 {'path' => CGI_VIEWER_PATH()},
916 {'path' => ($static ? -1 : $last_frame)}
918 if ($text_mode != TEXT_MODE->{'bb'}) {
919 $bbcode_url = merge_url(
922 'b' = TEXT_MODE->{'bb'}
926 if ($text_mode != TEXT_MODE->{'info'}) {
927 $info_url = merge_url(
930 'b' = TEXT_MODE->{'info'}
937 my $frame_normal_url;
939 if ($frame_data->{'frame'} ne '') {
940 $frame_file = $frame_data->{'frame'};
943 $frame_file = sprintf(
944 $settings->{'frame'},
945 $frame, $frame_data->{'ext'}
948 $frame_normal_url = merge_url(
949 {'path' => CGI_PATH()},
950 {'path' => $frame_file}
952 $frame_url = $frame_indirect ?
954 {'path' => CGI_FRAME_PATH()},
958 $frame_full_url = merge_url(
959 {'scheme' => SCHEME(), 'host' => WEBSITE()},
960 {'path' => $frame_normal_url}
962 if ($nextframe_indirect) {
963 $frame_next_url = merge_url(
964 {'path' => CGI_FRAME_PATH()},
965 {'path' => $nextframe}
968 elsif ($next_frame_data->{'frame'} ne '') {
969 $frame_next_url = merge_url(
970 {'path' => CGI_PATH()},
971 {'path' => $next_frame_data->{'frame'}}
975 $frame_next_url = merge_url(CGI_PATH(), sprintf(
976 $settings->{'frame'}, $next_frame, $next_frame_data->{'ext'}
981 $password_query = url_query_encode({'p', $settings->{'password'}});
982 $goto_url = merge_url($goto_url , {'query' => $password_query});
983 $info_url = merge_url($info_url , {'query' => $password_query});
984 $bbcode_url = merge_url($bbcode_url , {'query' => $password_query});
985 $viewer_url = merge_url($viewer_url , {'query' => $password_query});
986 $viewer_0_url = merge_url($viewer_0_url , {'query' => $password_query});
987 $viewer_prev_url = merge_url($viewer_prev_url, {'query' => $password_query});
988 $viewer_next_url = merge_url($viewer_next_url, {'query' => $password_query});
989 $viewer_last_url = merge_url($viewer_last_url, {'query' => $password_query});
990 if ($frame_indirect) {
991 $frame_url = merge_url($frame_url , {'query' => $password_query});
993 if ($nextframe_indirect) {
994 $frame_url = merge_url($frame_next_url, {'query' => $password_query});
997 my $_base_url = html_entity_encode_dec($base_url , 1);
998 my $_goto_url = html_entity_encode_dec($goto_url , 1);
999 my $_info_url = html_entity_encode_dec($info_url , 1);
1000 my $_bbcode_url = html_entity_encode_dec($bbcode_url , 1);
1001 my $_timer_url = html_entity_encode_dec($timer_url , 1);
1002 my $_viewer_full_url = html_entity_encode_dec($viewer_full_url, 1);
1003 my $_viewer_url = html_entity_encode_dec($viewer_url , 1);
1004 my $_viewer_0_url = html_entity_encode_dec($viewer_0_url , 1);
1005 my $_viewer_prev_url = html_entity_encode_dec($viewer_prev_url, 1);
1006 my $_viewer_next_url = html_entity_encode_dec($viewer_next_url, 1);
1007 my $_viewer_last_url = html_entity_encode_dec($viewer_last_url, 1);
1008 my $_frame_url = html_entity_encode_dec($frame_url , 1);
1009 my $_frame_next_url = html_entity_encode_dec($frame_next_url , 1);
1010 my $_frame_full_url = html_entity_encode_dec($frame_full_url , 1);
1012 my $_title = html_entity_encode_dec($title, 1);
1013 my $_command = html_entity_encode_dec($command, 1);
1015 if ($text_mode == TEXT_MODE->{'info'}) {
1016 if ($show_command) {
1017 $frame_data->{'command'} = $command;
1019 if ($context->{'access'}) {
1020 $frame_data->{'frame'} = $frame_file;
1026 unless (seek($fh, 0, 0)) {
1027 #don't actually fail here
1031 unless (open ($fh, ">:encoding(UTF-8)", encode('locale_fs', $file))) {
1036 print_html_start($fh);
1037 print_html_head_start($fh);
1039 print $fh '<link rel="index" href="'.$_goto_url.'">'."\n";
1040 print $fh '<link rel="start" href="'.$_viewer_0_url.'">'."\n";
1041 if ($prev_available) {
1042 print $fh '<link rel="prev" href="'.$_viewer_prev_url.'">'."\n";
1044 if ($next_available) {
1045 print $fh '<link rel="next" href="'.$_viewer_next_url.'">'."\n";
1046 if ($prefetch_next) {
1047 print $fh '<link rel="prefetch" href="'.$_viewer_next_url.'">'."\n";
1048 print $fh '<link rel="prefetch" href="'.$_frame_next_url.'">'."\n";
1052 print $fh '<!-- <script src="'.$_timer_url.'"></script> -->'."\n";
1055 print_html_head_end($fh);
1056 print_html_body_start($fh);
1058 print $fh '<div id="inst" class="ins">'."\n";
1060 print $fh '<div id="title">'."\n";
1061 print $fh '<h1 id="titletext">'.$_title.'</h1>'."\n";
1062 print $fh '</div>'."\n";
1064 print $fh '</div><div id="framespace">'."\n";
1066 print $fh '<img src="'.$_frame_url.'" id="frame" alt="'.$frame.'" title="'.$_title.'">'."\n";
1068 print $fh '</div><div id="insb" class="ins">'."\n";
1070 if ($text_mode == TEXT_MODE->{'info'}) {
1071 print $fh '<div id="chat">'."\n";
1073 print_html_data($fh, $frame_data);
1075 print $fh '</div>'."\n";
1077 elsif ($text_mode == TEXT_MODE->{'bb'}) {
1078 print $fh '<div id="chat">'."\n";
1080 print $fh '[quote][center][size=200]'.$_title.'[/size]<br>'."\n";
1081 print $fh '[url='.$_viewer_full_url.'][img]'.$_frame_full_url.'[/img][/url][/center]<br>'."\n";
1082 print $fh html_encode_line(bb_to_bbcode(eval_bb($frame_data->{'content'})));
1083 print $fh '[/quote]</div>'."\n";
1085 print $fh '</div>'."\n";
1087 elsif ($frame_data->{'content'} ne '') {
1088 print $fh '<div id="undertext">'."\n";
1089 print $fh bb_to_html(eval_bb($frame_data->{'content'}))."\n";
1090 print $fh '</div>'."\n";
1093 print $fh '<div id="command">'."\n";
1096 print $fh '[<span id="ongh" class="'.$timer_color_h.'">'.$timer_h.'</span>';
1097 print $fh ':<span id="ongm" class="'.$timer_color_m.'">'.$timer_m.'</span>';
1098 print $fh ':<span id="ongs" class="'.$timer_color_s.'">'.$timer_s.'</span>]<br>'."\n";
1101 if ($show_command_link) {
1102 print $fh '<a href="'.($access : $_viewer_next_url : $_viewer_last_url).'">';
1104 if ($show_command) {
1105 print $fh $_command;
1107 if ($show_command_cursor) {
1108 print $fh '<span class="inp">_</span>';
1110 if ($show_command_link) {
1113 print $fh "<br>\n</div>\n";
1115 print $fh '<div id="underlinks">'."\n";
1117 unless (($frame == 0) && $Static) {
1118 print $fh '<a href="'.$_base_url.'">Once again</a> | ';
1120 if ($prev_available) {
1121 print $fh '<a href="'.$_viewer_prev_url.'">Before</a> | ';
1123 unless ($frame == $last_frame) {
1124 print $fh '<a href="'.$_viewer_last_url.'">Now</a> | ';
1126 print $fh '<a href="'.$_goto_url.'">GOTO</a>'."\n";
1127 print $fh '<span style="float: right;">'."\n";
1128 if ($text_mode != TEXT_MODE->{'normal'}) {
1129 print $fh '<a href="'.$_viewer_url.'">Without</a> | ';
1131 print $fh '<a href="'.$_info_path.'">Info</a> | ';
1132 print $fh '<a href="'.$_bbcode_path.'">BB</a>';
1133 print $fh "\n</span>\n";
1135 print $fh "</div>\n</div>\n";
1137 print_html_body_end($fh);
1138 print_html_end($fh);
1141 unless (ref($file)) {
1145 truncate ($fh , tell($fh));
1159 if ($mode eq viewer) {
1160 my %frame_data = read_data_file(join_path(PATH_SEPARATOR(), DATA_PATH(), 0));
1161 my %next_frame_data= read_data_file(join_path(PATH_SEPARATOR(), DATA_PATH(), 1));
1162 my %default = read_data_file(DATA_DEFAULT_PATH());
1164 %frame_data = merge_settings(\%default, \%frame_data);
1165 %next_frame_data= merge_settings(\%default, \%next_frame_data);
1173 'timer_unlocked' => 3, # not relevant
1174 'timer' => 0, # not relevant