1 # bsta_lib.pm is generated from bsta_lib.1.pm
5 # Copyright (C) 2016, 2017, 2019, 2020, 2022, 2023, 2024 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/>.
22 # TODO: BB & INFO indent
30 use Encode ('encode', 'decode');
32 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
34 ###PERL_EXPORT_VERSION: our $VERSION = 'x.x.x';
35 our @ISA = qw(Exporter);
38 'STATE', 'TEXT_MODE', 'INTF_STATE', 'CHAT_STATE', 'CHAT_ACTION',
39 'get_remote_addr', 'get_id', 'get_frame', 'get_password',
41 'print_html_start', 'print_html_end',
42 'print_html_head_start', 'print_html_head_end',
43 'print_html_body_start', 'print_html_body_end',
44 'print_viewer_page', 'print_goto',
45 'write_index', 'write_static_viewer_page', 'write_static_goto',
46 'get_frame_file', 'get_page_file',
47 'read_frame_data', 'write_frame_data', 'read_default', 'read_noaccess',
48 'read_state', 'write_state',
49 'read_words_list', 'write_words_list', 'read_words', 'write_words',
50 'read_story', 'write_story',
51 'read_goto', 'write_goto',
52 'read_chat', 'write_chat',
53 'read_settings', 'read_attachment', 'read_coincidence',
55 'eval_bb', 'bb_to_bbcode', 'bb_to_html'
58 ###PERL_LIB: use lib /botm/lib/bsta
61 'url_query_decode', 'url_query_encode',
62 'url_decode', 'url_encode',
63 'html_entity_encode_dec',
66 'read_data_file', 'write_data_file',
68 'copy_encoded', 'open_encoded', '_x_encoded',
69 'http_header_line', 'http_status',
70 'http_header_status', 'http_header_allow', 'http_header_location'
73 ###PERL_PATH_SEPARATOR: PATH_SEPARATOR = /
75 ###PERL_CGI_PATH: CGI_PATH = /bsta/
76 ###PERL_CGI_ATTACH_PATH: CGI_ATTACH_PATH = /bsta/a
77 ###PERL_CGI_2WORDS_PATH: CGI_2WORDS_PATH = /bsta/2words
78 ###PERL_CGI_BBCODE_PATH: CGI_BBCODE_PATH = /bsta/b
79 ###PERL_DATA_CHAT_PATH: DATA_CHAT_PATH = /botm/data/bsta/chat
80 ###PERL_CGI_COIN_PATH: CGI_COIN_PATH = /bsta/coin
81 ###PERL_CGI_CSS_PATH: CGI_CSS_PATH = /bsta/bsta.css
82 ###PERL_CGI_FRAME_PATH: CGI_FRAME_PATH = /bsta/f
83 ###PERL_CGI_GOTO_PATH: CGI_GOTO_PATH = /bsta/g
84 ###PERL_CGI_INFO_PATH: CGI_INFO_PATH = /bsta/i
85 ###PERL_CGI_LIST_PATH: CGI_LIST_PATH = /bsta/goto.htm
86 ###PERL_CGI_LOGO_PATH: CGI_LOGO_PATH = /bsta/botmlogo.png
87 ###PERL_CGI_TIMER_PATH: CGI_TIMER_PATH = /bsta/timer.js
88 ###PERL_CGI_VIEWER_PATH: CGI_VIEWER_PATH = /bsta/v
89 ###PERL_CGI_WORDS_PATH: CGI_WORDS_PATH = /bsta/w
91 ###PERL_DATA_PATH: DATA_PATH = /botm/data/bsta/
92 ###PERL_DATA_ATTACH_PATH: DATA_ATTACH_PATH = /botm/data/bsta/a
93 ###PERL_DATA_COIN_PATH: DATA_COIN_PATH = /botm/data/bsta/coincidence
94 ###PERL_DATA_DEFAULT_PATH: DATA_DEFAULT_PATH = /botm/data/bsta/default
95 ###PERL_DATA_LIST_PATH: DATA_LIST_PATH = /botm/data/bsta/list
96 ###PERL_DATA_NOACCESS_PATH: DATA_NOACCESS_PATH = /botm/data/bsta/noaccess
97 ###PERL_DATA_SETTINGS_PATH: DATA_SETTINGS_PATH = /botm/data/bsta/settings
98 ###PERL_DATA_STATE_PATH: DATA_STATE_PATH = /botm/data/bsta/state
99 ###PERL_DATA_STORY_PATH: DATA_STORY_PATH = /botm/data/bsta/story
100 ###PERL_DATA_WORDS_PATH: DATA_WORDS_PATH = /botm/data/bsta/words/
102 ###PERL_WWW_PATH: WWW_PATH = /botm/www/
103 ###PERL_WWW_GOTO_PATH: WWW_GOTO_PATH = /botm/www/1190/bsta/goto.htm
104 ###PERL_WWW_INDEX_PATH: WWW_INDEX_PATH = /botm/www/1190/bsta/index.htm
106 ###PERL_SCHEME: SCHEME = http
107 ###PERL_WEBSITE: WEBSITE = 1190.bicyclesonthemoon.info
108 ###PERL_WEBSITE_NAME: WEBSITE_NAME = Bicycles on the Moon
109 ###PERL_FAVICON_PATH: FAVICON_PATH = /img/favicon.png
111 ###PERL_COIN_DATE: COIN_DATE = 13-Nov-2016 22:15
112 ###PERL_INTF_DATE: INTF_DATE = 28-Sep-2016 20:34
114 ###PERL_STORY_CREDITS: STORY_CREDITS = "BSTA" by Balthasar Szczepański
115 ###PERL_INTF_CREDITS: INTF_CREDITS = Online interface © Balthasar Szczepański; AGPL 3 license
116 ###PERL_SOURCE_URL: SOURCE_URL = http://bicyclesonthemoon.info/git-projects/?p=ott/bsta
118 ###PERL_COMMENT_PAGE_LENGTH:COMMENT_PAGE_LENGTH= 16
120 use constant STATE => {
126 use constant INTF_STATE => {
139 use constant TEXT_MODE => {
145 use constant CHAT_STATE => {
150 use constant CHAT_ACTION => {
158 use constant tags_bbcode => {
165 'quote' => '[quote]',
166 'quote=' => '[quote="',
168 '/quote' => '[/quote]',
169 'ni' => '[color=#0057AF]',
171 'br' => '[color=#BB6622]',
173 'po' => '[color=#FF8800]',
184 '/list' => '[/list]',
188 '/?' => '[/unknown!]',
190 use constant tags_html => {
193 'fq' => '<div class="fq">',
195 'tq' => '<div class="tq">',
197 'quote' => '<div class="opomba"><div class="opomba_text">',
198 'quote=' => '<div class="opomba"><div class="opomba_info"><b>',
199 'quote/='=> '</b> wrote:</div><div class="opomba_text">',
200 '/quote' => '</div></div>',
201 'ni' => '<span class="ni">',
203 'br' => '<span class="br">',
205 'po' => '<span class="po">',
207 'url' => '<a href="#">',#think: how to add selfincluding?
208 'url=' => '<a href="',
214 'list=' => '<ol style="list-style-type: ',
215 'list=1' => 'decimal',
216 'list=A' => 'upper-alpha',
217 'list=a' => 'lower-alpha',
218 'list=I' => 'upper-roman',
219 'list=i' => 'lower-roman',
226 '/?' => '[/unknown!]',
231 # function to obtain address of remote agent
232 sub get_remote_addr {
233 if ($ENV{'HTTP_X_FORWARDED_FOR'} =~ /^.+$/) {
236 elsif ($ENV{'REMOTE_ADDR'} =~ /^.+$/) {
244 # functions to get ID/number etc.
246 (my $cgi, my $default, my $cgi_name) = @_;
247 if ($default eq '') {
250 if ($cgi_name eq '') {
254 if ($cgi->{$cgi_name} =~ /^.+$/) {
257 elsif ($ENV{'PATH_INFO'} =~ /^\/(.+)$/) {
261 return int($default);
265 # function to obtain frame number
267 (my $cgi, my $default) = @_;
268 return get_id($cgi, $default, 'f');
271 # function to obtain password
275 if ($cgi->{'p'} =~ /^.+$/) {
287 foreach my $settings (@_) {
288 foreach my $ind (keys %$settings) {
289 $final_settings{$ind} = $settings->{$ind};
292 return %final_settings;
297 # different & simpler implementation than in post library
301 #analyse bbcode text to build tag tree
302 #TODO make [/*] optional!
304 (my $bb, my $printdebug) = @_;
317 $bbtree{"_.name" } = "ht";
318 $bbtree{"_.value" } = '';
319 $bbtree{"_.type" } = "tag";
320 $bbtree{"_.count" } = 0;
321 $bbtree{"_.closed"} = 0;
322 $debug .= debug($printdebug,
324 "<!--GENERATING BBCODE TREE:\n".
325 '[_]automatic tag: [ht]'."\n"
329 my $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
331 if($bb =~ m/\[(\/?)([A-Za-z]+|\*)(=([^\[\]]*))?\]/g) {
338 if ($tag_value =~ /^"(.*)"$/) {
342 if ($pre_text ne '') {
343 $debug .= debug($printdebug, "[$new_ind]text: $pre_text\n");
344 $bbtree{$new_ind.'.type' } = 'text';
345 $bbtree{$new_ind.'.value'} = $pre_text;
346 $bbtree{ $ind.'.count'}+= 1;
347 $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
350 if($tag_name =~ /^(fq|tq|quote|br|ni|po|url|i|list|\*)$/) {
351 if ($tag_end ne '') {
353 ($tag_name ne $bbtree{$ind.'.name'}) ||
356 $debug .= debug($printdebug, "[$new_ind]text: $tag\n");
357 $bbtree{$new_ind.'.type' } = 'text';
358 $bbtree{$new_ind.'.value'} = $tag;
359 $bbtree{ $ind.'.count'}+= 1;
360 # $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
363 $debug .= debug($printdebug, "[$new_ind]tag: $tag\n");
364 $bbtree{$new_ind.'.type' } = 'tag';
365 $bbtree{$new_ind.'.name' } = '/'.$tag_name;
366 $bbtree{$new_ind.'.value' } = $tag_value;
367 $bbtree{ $ind.'.count' }+= 1;
368 $bbtree{ $ind.'.closed'} = 1;
370 $ind =~ s/\.[0-9]+$//;
371 # $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
376 $debug .= debug($printdebug, "[$new_ind]tag: $tag\n");
377 $bbtree{$new_ind.'.type' } = 'tag';
378 $bbtree{$new_ind.'.name' } = $tag_name;
379 $bbtree{$new_ind.'.value' } = $tag_value;
380 $bbtree{$new_ind.'.count' } = 0;
381 $bbtree{$new_ind.'.closed'} = 0;
382 $bbtree{ $ind.'.count' }+= 1;
385 # $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
389 $debug .= debug($printdebug, "[$new_ind]text: $tag\n");
390 $bbtree{$new_ind.'.type' } = 'text';
391 $bbtree{$new_ind.'.value'} = $tag;
392 $bbtree{ $ind.'.count'}+= 1;
393 # $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
397 $debug .= debug($printdebug, "[$new_ind]text: $bb\n");
398 $bbtree{$new_ind.'.type' } = 'text';
399 $bbtree{$new_ind.'.value'} = $bb;
400 $bbtree{ $ind.'.count'}+= 1;
401 # $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
405 my $final_ind = '_.'.$bbtree{"_.count"};
406 $debug .= debug($printdebug, "[$final_ind]automatic tag: [/ht]\n -->\n");
407 $bbtree{$final_ind.'.type' } = "tag";
408 $bbtree{$final_ind.'.name' } = '/ht';
409 $bbtree{ '_.count' }+= 1;
410 $bbtree{ '_.closed'} = 1;
412 return ($debug, %bbtree);
415 #convert tag tree to final text
417 (my $printdebug, my $debug, my $lang, my $bbtree) = @_;
422 my $tags = ($lang eq 'html') ? tags_html : tags_bbcode;
423 my $escape = ($lang eq 'html');
425 # $debug .= debug($printdebug, "\n****\n");
426 # foreach my $iiii (keys %tags) {
427 # $debug .= debug($printdebug, $iiii.'='.$tags->{$iiii}."\n");
429 # $debug .= debug($printdebug, "****\n");
434 $debug .= debug($printdebug, "\n<!--PROCESSING BBCODE TREE:\n");
436 while ($level >= 0) {
438 $debug .= debug($printdebug, "[$level:$ind:".int($bbtree->{$ind.'.count'})."]");
440 if ($bbtree->{$ind.'.type'} eq 'text') {
441 my $text = $bbtree->{$ind.'.value'};
442 $debug .= debug($printdebug, "text: ".$text);
443 $out .= $escape ? html_encode_line($text) : $text;
448 elsif ($bbtree->{$ind.'.type'} eq 'tag') {
449 my $name = $bbtree->{$ind.'.name'};
451 if ($name =~ /^\//) {
452 $debug .= debug($printdebug, "tag: [$name]");
454 $indd =~ s/\.([0-9]+)$//;
455 if (exists($tags->{$name.'='}) && ($bbtree->{$indd.'.value'} ne '')) {
456 $out .= $tags->{$name.'='};
458 elsif (exists($tags->{$name})) {
459 $out .= $tags->{$name};
462 $out .= $tags->{'/?'};
463 $debug .= debug($printdebug, "[unknown!]");
466 $ind =~ s/\.([0-9]+)$//;
468 $debug .= debug($printdebug, "[<]");
479 my $value = $bbtree->{$ind.'.value'};
480 if($bbtree->{$ind.'.closed'} ne '') {
481 $debug .= debug($printdebug, "tag: [$name]");
483 if (exists($tags->{$name.'='}) && ($value ne '')) {
484 if (exists($tags->{$name.'='.$value})) {
487 $tags->{$name.'='.$value} .
493 ($escape ? html_entity_encode_dec($value, 1) : $value) .
497 elsif (exists($tags->{$name})) {
498 $out .= $tags->{$name};
501 $out .= $out.$tags->{'?'};
502 $debug .= debug($printdebug, "[unknown!]");
506 $debug .= debug($printdebug, "unclosed tag: [$name]");
507 my $text = $name . (($value ne '') ? ('='.$value) : '');
508 $out .= '['.($escape ? html_encode_line($text) : $text).']';
510 if ($bbtree->{$ind.'.count'} > 0) {
513 $debug .= debug($printdebug, "[>]");
522 $debug .= debug($printdebug, "unknown thing: ".$bbtree->{$ind.'.type'});
523 #should not occur with a correct bbtree
524 #unless unimplemented
525 $ind =~ s/\.([0-9]+)$//;
527 $debug .= debug($printdebug, "[<ui]");
536 if ($goto_next ne '') {
538 $ind =~ s/\.([0-9]+)$//;
540 if (($i < $bbtree->{$ind.'.count'}) and ($1 ne '')){
547 # should not occur with a correct bbtree
548 $debug .= debug($printdebug, "[<$goto_next]");
551 } while ($level >= 0);}
554 $debug .= debug($printdebug, "[>$level:$ind]\n");
557 $debug .= debug($printdebug, "-->\n");
558 return ($debug, $out);
563 (my $bb, my $printdebug) = @_;
568 ($debug, %bbtree) = bbtree($bb, $printdebug);
569 ($debug, $ht) = convtree ($printdebug, $debug, 'html', \%bbtree);
576 (my $bb, my $printdebug) = @_;
581 ($debug, %bbtree) = bbtree($bb, $printdebug);
582 ($debug, $ht) = convtree ($printdebug, $debug, 'bb', \%bbtree);
588 (my $bb, my $full_url, my $password) = @_;
593 my $base_url = $full_url ?
594 {'scheme' => SCHEME(), 'host' => WEBSITE()} :
597 while ($bb =~ m/###([^#;]*);/g) {
602 if ($value =~ /^att&([0-9]+)$/) {
605 {'path' => CGI_ATTACH_PATH()},
609 elsif ($value =~ /^vw&([0-9]+)$/) {
612 {'path' => CGI_VIEWER_PATH()},
616 elsif ($value =~ /^fr&([0-9]+)$/) {
619 {'path' => CGI_FRAME_PATH()},
626 if (($value ne '') && ($password ne '')) {
629 {'query' => {'p' => $password}}
632 $bb = $before . $value . $after;
638 sub html_encode_line {
639 (my $text, my $non_ascii, my $all) = @_;
643 $text =~ s/\r\n/\n/gs;
646 while ($text ne '') {
647 $ind = index($text, "\n");
649 $html .= html_entity_encode_dec(substr($text, 0, $ind), $non_ascii, $all)."<br>\n";
650 $text = substr($text, $ind+1);
654 $html .= html_entity_encode_dec($text, 1);
662 (my $print, my $text) = @_;
672 sub print_html_start {
674 print $fh '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "">'."\n";
675 print $fh '<html lang="en">'."\n";
680 print $fh '</html>'."\n";
683 sub print_html_head_start {
685 print $fh ' <head>'."\n";
686 print $fh ' <meta http-equiv="Content-type" content="text/html; charset=UTF-8">'."\n";
687 print $fh ' <link rel="icon" type="image/png" href="'.html_entity_encode_dec(FAVICON_PATH(),1).'">'."\n";
688 print $fh ' <link rel="stylesheet" href="'.html_entity_encode_dec(CGI_CSS_PATH(),1).'">'."\n";
691 sub print_html_head_end {
693 print $fh ' </head>'."\n";
696 sub print_html_body_start {
698 print $fh ' <body>'."\n";
699 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";
700 print $fh ' <div id="all">'."\n";
703 sub print_html_body_end {
704 (my $fh, my $hide_credits) = @_;
705 print $fh ' </div>'."\n";
706 unless ($hide_credits) {
707 print $fh ' <p>'."\n";
708 print $fh ' '.html_entity_encode_dec(STORY_CREDITS(),1).'<br>'."\n";
709 print $fh ' '.html_entity_encode_dec(INTF_CREDITS(),1).'<br>'."\n";
710 print $fh ' <a href="'.html_entity_encode_dec(SOURCE_URL(),1).'" class="cz">source code</a>'."\n";
711 print $fh ' </p>'."\n";
713 print $fh ' <a href="/" class="cz">'.html_entity_encode_dec(WEBSITE(),1).'</a>'."\n";
714 print $fh ' </body>'."\n";
717 sub print_html_data {
718 (my $fh, my $data) = @_;
720 foreach my $key (keys %$data) {
721 unless ($key eq 'content') {
722 my $val = $data->{$key};
723 $val =~ s/(\r)?\n/\n /gs; # does the space make sense in HTML anyway?
724 print $fh html_encode_line("$key: $val\n", 1);
727 print $fh html_encode_line("\n".$data->{'content'});
744 $last_frame = int($state->{'last'});
745 $ong_state = int($state->{'state'});
746 $password_query = url_query_encode({'p', $settings->{'password'}});
748 my $_title = html_entity_encode_dec($settings->{'story'}, 1);
749 my $_website_name = html_entity_encode_dec(WEBSITE_NAME() , 1);
750 my $_base_url = html_entity_encode_dec(CGI_PATH() , 1);
754 unless (seek($fh, 0, 0)) {
755 #don't actually fail here
759 unless (open_encoded($fh, ">:encoding(UTF-8)", $file)) {
764 print_html_start($fh);
765 print_html_head_start($fh);
767 print $fh ' <title>GOTO • '.$_title.' • '.$_website_name.'</title>'."\n";
769 print_html_head_end($fh);
770 print_html_body_start($fh);
772 print $fh ' <div id="inst" class="ins">'."\n";
774 print $fh ' <div id="title">'."\n";
775 print $fh ' <h1 id="titletext">'.$_title.'</h1>'."\n";
776 print $fh ' </div>'."\n";
778 print $fh ' </div>'."\n";
779 print $fh ' <div id="insb" class="ins">'."\n";
781 print $fh ' <div id="chat">'."\n";
783 for (my $frame = 0; ; $frame += 1) {
786 ($frame <= $last_frame) &&
787 ($ong_state >= STATE->{'waiting'})
800 $ongtime = $goto_list->{'ongtime-'.$frame};
801 $title = $goto_list->{'title-' .$frame};
802 if (($ongtime eq '') && ($title eq '')) {
803 my %frame_data = read_frame_data($frame);
804 $ongtime = $frame_data{'ongtime'};
805 $title = $frame_data{'title'};
806 unless (keys %frame_data) {
811 if ($ongtime ne '') {
812 @time_tab = gmtime($ongtime);
813 $time_text = sprintf(
814 '%02d.%02d.%02d %02d:%02d',
823 $time_text = (($frame <= $last_frame) && ($ong_state >= STATE->{'waiting'})) ?
824 'EE.EE.EE EE:EE' : '--.--.-- --:--';
829 $timer_color = (($frame > $last_frame) || ($ong_state < STATE->{'waiting'})) ?
831 (($frame == $last_frame) && ($ong_state < STATE->{'ready'})) ?
834 $frame_text = sprintf('%03d',$frame);
835 $viewer_url = merge_url(
836 {'path' => CGI_VIEWER_PATH()},
838 ); # TODO: consider static here?
840 $viewer_url = merge_url($viewer_url, {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
843 my $_viewer_url = html_entity_encode_dec($viewer_url, 1);
844 my $_title = html_entity_encode_dec($title , 1);
846 print $fh ' <span class="'.$timer_color.'">'.$frame_text.'</span> '.$time_text.' <a href="'.$_viewer_url.'">'.$_title.'</a><br>'."\n";
848 print $fh ' </div>'."\n";
850 print $fh ' <div id="underlinks">'."\n";
851 print $fh ' <a href="'.$_base_url.'">BSTA</a>'."\n";
852 print $fh ' </div>'."\n";
854 print $fh ' </div>'."\n";
856 print_html_body_end($fh, $ong_state == STATE->{'inactive'});
859 unless (ref($file)) {
863 truncate ($fh , tell($fh));
869 sub print_viewer_page {
882 my $launch = $context->{'launch'};
883 my $access = $context->{'access'};
884 my $password_ok = $context->{'password_ok'};
885 my $static = $context->{'static'};
887 my $goto = int($context->{'goto'});
888 my $frame = int($context->{'frame'});
889 my $text_mode = int($context->{'text_mode'});
890 my $timer_unlocked = int($context->{'timer_unlocked'});
891 my $timer = int($context->{'timer'});
892 # my $words_page = int($context->{'words_page'});
894 my $prev_frame = $frame - 1;
895 my $next_frame = $frame + 1;
897 my $story = $settings->{'story'};
898 my $title = $frame_data->{'title'};
899 my $command = ($frame_data->{'command'} ne '') ?
900 $frame_data->{'command'} :
901 $next_frame_data->{'title'};
903 my $last_frame = int($state->{'last'});
904 my $ong_state = int($state->{'state'});
906 my $width = int($frame_data->{'width'});
907 my $height = int($frame_data->{'height'});
908 my $frame_type = $frame_data->{'frametype'};
910 my $timer_color_h = (($timer_unlocked >= 1) || ($ong_state >= STATE->{'ready'})) ? 'br' : 'ni';
911 my $timer_color_m = (($timer_unlocked >= 2) || ($ong_state >= STATE->{'ready'})) ? 'br' : 'ni';
912 my $timer_color_s = (($timer_unlocked >= 3) || ($ong_state >= STATE->{'ready'})) ? 'br' : 'ni';
919 (($timer >= 0) && ($frame == 0))
921 $timer_s = sprintf('%02d', $timer % 60);
922 $timer_h = int($timer / 60);
923 $timer_m = sprintf('%02d', $timer_h % 60);
924 $timer_h = sprintf('%02d', $timer_h / 60);
926 elsif (($timer >= -15) && ($ong_state >= STATE->{'ready'})) {
937 my $words_posts = int($words_data->{'posts'});
938 my $words_link_text = 'Words'.(($words_posts > 0) ? "[$words_posts]" : '');
940 my $prev_available = (($frame > 0) && $access);
941 my $next_available = ($launch || $password_ok || ($next_frame <= $last_frame));
942 my $prefetch_prev = (
944 ($prev_frame < $last_frame) || ( # avoid unseen trigger!
945 ($prev_frame <= $last_frame) &&
946 ($ong_state >= STATE->{'ready'})
949 my $prefetch_next = (
951 ($next_frame < $last_frame) || ( # avoid unseen trigger!
952 ($next_frame <= $last_frame) &&
953 ($ong_state >= STATE->{'ready'})
960 ($frame == $last_frame) && (
961 ($ong_state == STATE->{'waiting'}) ||
962 ($ong_state == STATE->{'ready'})
970 ($frame < $last_frame) || (
971 ($ong_state >= STATE->{'ready'}) &&
972 $context->{'show_command'}
975 my $show_command_link = ($next_available || (!$access));
976 my $show_command_cursor = ((!$next_available) || ($command eq ''));
977 my $show_words = ($password_ok || ($access && !$launch));
979 my $frame_indirect = !(
981 ($frame <= $last_frame) &&
982 ($ong_state > STATE->{'inactive'})
985 my $prevframe_indirect = !($prev_frame <= $last_frame);
986 my $nextframe_indirect = !($next_frame <= $last_frame);
990 my $base_url = CGI_PATH();
991 my $timer_url = CGI_TIMER_PATH();
992 my $viewer_full_url = merge_url(
993 {'scheme' => SCHEME(), 'host' => WEBSITE()},
994 {'path' => CGI_VIEWER_PATH()},
997 my $viewer_url = merge_url(
998 {'path' => CGI_VIEWER_PATH()},
1001 my $viewer_0_url = merge_url(
1002 {'path' => CGI_VIEWER_PATH()},
1005 my $viewer_prev_url = merge_url(
1006 {'path' => CGI_VIEWER_PATH()},
1007 {'path' => $prev_frame}
1009 my $viewer_next_url = merge_url(
1010 {'path' => CGI_VIEWER_PATH()},
1011 {'path' => $next_frame}
1013 my $viewer_last_url = merge_url(
1014 {'path' => CGI_VIEWER_PATH()},
1015 {'path' => ($static ? -1 : $last_frame)}
1017 my $goto_url = ($goto) ?
1020 {'path' => $viewer_url},
1026 'fragment' => 'goto'
1030 unless ($password_ok) {
1032 $viewer_0_url = $base_url;
1033 if ($prev_frame == 0) {
1034 $viewer_prev_url = $viewer_0_url;
1037 $page_file = get_page_file($prev_frame, $prev_frame_data, $settings);
1038 if (_x_encoded('-f',
1039 join_path(PATH_SEPARATOR(), WWW_PATH() , $page_file)
1041 $viewer_prev_url = merge_url(
1042 {'path' => $base_url},
1043 {'path' => $page_file}
1047 if ($next_frame < $last_frame) {
1048 $page_file = get_page_file($next_frame, $next_frame_data, $settings);
1049 if (_x_encoded('-f',
1050 join_path(PATH_SEPARATOR(), WWW_PATH() , $page_file)
1052 $viewer_next_url = merge_url(
1053 {'path' => $base_url},
1054 {'path' => $page_file}
1060 (_x_encoded('-f',WWW_GOTO_PATH()))
1062 $goto_url = CGI_LIST_PATH();
1065 my $bbcode_url = ($text_mode == TEXT_MODE->{'bb'}) ?
1067 {'path' => CGI_BBCODE_PATH()},
1074 'b' => TEXT_MODE->{'bb'}
1079 my $info_url = ($text_mode == TEXT_MODE->{'info'}) ?
1081 {'path' => CGI_INFO_PATH()},
1088 'b' => TEXT_MODE->{'info'}
1093 my $words_url = merge_url (
1097 'b' => TEXT_MODE->{'words'}
1106 my $frame_normal_url;
1108 $frame_file = get_frame_file($frame, $frame_data, $settings);
1109 $frame_normal_url = merge_url(
1110 {'path' => CGI_PATH()},
1111 {'path' => $frame_file}
1113 $frame_url = $frame_indirect ?
1115 {'path' => CGI_FRAME_PATH()},
1119 $frame_full_url = merge_url(
1120 {'scheme' => SCHEME(), 'host' => WEBSITE()},
1121 {'path' => $frame_normal_url}
1123 if ($prevframe_indirect) {
1124 $frame_prev_url = merge_url(
1125 {'path' => CGI_FRAME_PATH()},
1126 {'path' => $prev_frame}
1130 $frame_prev_url = merge_url(
1131 {'path' => CGI_PATH()},
1132 {'path' => get_frame_file($prev_frame, $prev_frame_data, $settings)}
1135 if ($nextframe_indirect) {
1136 $frame_next_url = merge_url(
1137 {'path' => CGI_FRAME_PATH()},
1138 {'path' => $next_frame}
1142 $frame_next_url = merge_url(
1143 {'path' => CGI_PATH()},
1144 {'path' => get_frame_file($next_frame, $next_frame_data, $settings)}
1149 $password_query = url_query_encode({'p', $settings->{'password'}});
1150 $goto_url = merge_url($goto_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1151 $info_url = merge_url($info_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1152 $words_url = merge_url($words_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1153 $bbcode_url = merge_url($bbcode_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1154 $viewer_url = merge_url($viewer_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1155 $viewer_0_url = merge_url($viewer_0_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1156 $viewer_prev_url = merge_url($viewer_prev_url, {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1157 $viewer_next_url = merge_url($viewer_next_url, {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1158 $viewer_last_url = merge_url($viewer_last_url, {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1159 if ($frame_indirect) {
1160 $frame_url = merge_url($frame_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1162 if ($prevframe_indirect) {
1163 $frame_prev_url= merge_url($frame_prev_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1165 if ($nextframe_indirect) {
1166 $frame_next_url= merge_url($frame_next_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1169 my $_password = $password_ok ? html_entity_encode_dec($settings->{'password'}, 1) : '';
1170 my $_action_url = html_entity_encode_dec(CGI_VIEWER_PATH(), 1);
1171 my $_base_url = html_entity_encode_dec($base_url , 1);
1172 my $_goto_url = html_entity_encode_dec($goto_url , 1);
1173 my $_info_url = html_entity_encode_dec($info_url , 1);
1174 my $_words_url = html_entity_encode_dec($words_url , 1);
1175 my $_bbcode_url = html_entity_encode_dec($bbcode_url , 1);
1176 my $_timer_url = html_entity_encode_dec($timer_url , 1);
1177 my $_viewer_full_url = html_entity_encode_dec($viewer_full_url , 1);
1178 my $_viewer_url = html_entity_encode_dec($viewer_url , 1);
1179 my $_viewer_0_url = html_entity_encode_dec($viewer_0_url , 1);
1180 my $_viewer_prev_url = html_entity_encode_dec($viewer_prev_url , 1);
1181 my $_viewer_next_url = html_entity_encode_dec($viewer_next_url , 1);
1182 my $_viewer_last_url = html_entity_encode_dec($viewer_last_url , 1);
1183 my $_frame_url = html_entity_encode_dec($frame_url , 1);
1184 my $_frame_prev_url = html_entity_encode_dec($frame_prev_url , 1);
1185 my $_frame_next_url = html_entity_encode_dec($frame_next_url , 1);
1186 my $_frame_full_url = html_entity_encode_dec($frame_full_url , 1);
1188 my $_story = html_entity_encode_dec($story , 1);
1189 my $_title = html_entity_encode_dec($title , 1);
1190 my $_command = html_entity_encode_dec($command , 1);
1191 my $_frame_type = html_entity_encode_dec($frame_type, 1);
1193 my $_website_name = html_entity_encode_dec(WEBSITE_NAME(), 1);
1195 if ($text_mode == TEXT_MODE->{'info'}) {
1196 if ($show_command) {
1197 $frame_data->{'command'} = $command;
1200 $frame_data->{'frame'} = $frame_file;
1202 if ($frame_data->{'page'} eq '') {
1203 unless (($access) && ($frame < $last_frame)) {
1204 $frame_data->{'page'} = '';
1207 $frame_data->{'page'} = get_page_file($frame, $frame_data, $settings);
1212 # everything determined, now start generating
1216 unless (seek($fh, 0, 0)) {
1217 #don't actually fail here
1221 unless (open_encoded($fh, ">:encoding(UTF-8)", $file)) {
1226 print_html_start($fh);
1227 print_html_head_start($fh);
1229 print $fh ' <title>'.$_title;
1230 if ($story ne $title) {
1231 print $fh ' • '.$_story;
1233 print $fh ' • '.$_website_name.'</title>'."\n";
1234 print $fh ' <link rel="index" href="'.$_goto_url.'">'."\n";
1235 print $fh ' <link rel="start" href="'.$_viewer_0_url.'">'."\n";
1236 if ($prev_available) {
1237 print $fh ' <link rel="prev" href="'.$_viewer_prev_url.'">'."\n";
1238 if ($prefetch_prev) {
1239 print $fh ' <link rel="prefetch" href="'.$_viewer_prev_url.'">'."\n";
1240 print $fh ' <link rel="prefetch" href="'.$_frame_prev_url.'">'."\n";
1243 if ($next_available) {
1244 print $fh ' <link rel="next" href="'.$_viewer_next_url.'">'."\n";
1245 if ($prefetch_next) {
1246 print $fh ' <link rel="prefetch" href="'.$_viewer_next_url.'">'."\n";
1247 print $fh ' <link rel="prefetch" href="'.$_frame_next_url.'">'."\n";
1251 print $fh ' <script src="'.$_timer_url.'"></script>'."\n";
1254 print_html_head_end($fh);
1255 print_html_body_start($fh);
1257 print $fh ' <div id="inst" class="ins">'."\n";
1259 print $fh ' <div id="title">'."\n";
1260 print $fh ' <h1 id="titletext">'.$_title.'</h1>'."\n";
1261 print $fh ' </div>'."\n";
1263 print $fh ' </div>'."\n";
1264 print $fh ' <div id="framespace">'."\n";
1266 print $fh ' <img src="'.$_frame_url.'" id="frame" class="'.$_frame_type.'" alt="'.$frame.'" title="'.$_title.'" width="'.$width.'" height="'.$height.'">'."\n";
1268 print $fh ' </div>'."\n";
1269 print $fh ' <div id="insb" class="ins">'."\n";
1271 if ($text_mode == TEXT_MODE->{'info'}) {
1272 print $fh ' <div id="chat">'."\n";
1274 print_html_data($fh, $frame_data);
1276 print $fh ' </div>'."\n";
1278 elsif ($text_mode == TEXT_MODE->{'bb'}) {
1279 print $fh ' <div id="chat">'."\n";
1281 print $fh '[quote][center][size=200]'.$_title.'[/size]<br>'."\n";
1282 print $fh '[url='.$_viewer_full_url.'][img]'.$_frame_full_url.'[/img][/url][/center]<br>'."\n";
1283 print $fh html_encode_line(
1286 $frame_data->{'content'},
1291 print $fh '[/quote]'."\n";
1293 print $fh ' </div>'."\n";
1295 elsif ($frame_data->{'content'} ne '') {
1296 print $fh ' <div id="undertext">'."\n";
1297 print $fh bb_to_html(
1299 $frame_data->{'content'},
1301 $password_ok ? $settings->{'password'} : ''
1304 print $fh ' </div>'."\n";
1307 print $fh ' <div id="command">'."\n";
1310 print $fh ' <span id="timer">';
1311 print $fh '[<span id="ongh" class="hv '.$timer_color_h.'">'.$timer_h.'</span>';
1312 print $fh ':<span id="ongm" class="hv '.$timer_color_m.'">'.$timer_m.'</span>';
1313 print $fh ':<span id="ongs" class="hv '.$timer_color_s.'">'.$timer_s.'</span>]';
1314 print $fh '</span><br>'."\n";
1317 if ($show_command_link) {
1318 print $fh '<a href="'.($access ? $_viewer_next_url : $_viewer_last_url).'">';
1320 if ($show_command) {
1321 print $fh $_command;
1323 if ($show_command_cursor) {
1324 print $fh '<span class="inp">_</span>';
1326 if ($show_command_link) {
1330 print $fh " </div>\n";
1332 print $fh ' <div id="underlinks">'."\n ";
1334 unless (($frame == 0) && $static) {
1335 print $fh '<a href="'.$_base_url.'">Once again</a> | ';
1337 if ($prev_available) {
1338 print $fh '<a href="'.$_viewer_prev_url.'">Before</a> | ';
1340 unless ($frame == $last_frame) {
1341 print $fh '<a href="'.$_viewer_last_url.'">Now</a> | ';
1343 print $fh '<a href="'.$_goto_url.'">GOTO</a>'."\n";
1344 print $fh ' <span style="float: right;">'."\n ";
1346 ($text_mode == TEXT_MODE->{'normal'})
1350 print $fh '<a href="'.$_words_url.'">'.$words_link_text.'</a> | ';
1354 print $fh '<a href="'.$_viewer_url.'">Without</a> | ';
1356 print $fh '<a href="'.$_info_url.'">Info</a> | ';
1357 print $fh '<a href="'.$_bbcode_url.'">BB</a>';
1358 print $fh "\n </span>\n";
1360 print $fh " </div>\n";
1363 print $fh ' <div class="underlinks" id="goto">'."\n";
1364 print $fh ' <form class="goto" method="get" action="'.$_action_url.'">'."\n";
1365 print $fh ' GO TO:'."\n";
1366 print $fh ' <input class="intx" type="number" size="4" name="f"'.(
1368 ('value="'.$frame.'"') :
1371 print $fh ' <input class="inbt" type="submit" value="GO">'."\n";
1373 print $fh ' <input type="hidden" name="p" value="'.$_password.'">'."\n";
1375 print $fh ' <input type="hidden" name="g" value="2">'."\n";
1376 print $fh ' </form>'."\n";
1377 print $fh " </div>\n";
1380 print $fh " </div>\n";
1382 if (($text_mode == TEXT_MODE->{'words'}) && $show_words) {
1383 print_comments($fh, $context, $settings, $words_data);
1386 print_html_body_end($fh, $ong_state == STATE->{'inactive'});
1387 print_html_end($fh);
1390 unless (ref($file)) {
1394 truncate ($fh , tell($fh));
1400 sub print_comments {
1401 (my $fh, my $context, my $settings, my $words_data) = @_;
1403 my $password_ok = $context->{'password_ok'};
1404 my $frame = int($context->{'frame'});
1405 my $page = int($context->{'words_page'});
1406 my $post_count = int($words_data->{'posts'});
1407 my $id_start = $page * COMMENT_PAGE_LENGTH();
1408 my $id_stop = $id_start + COMMENT_PAGE_LENGTH();
1409 my $older = ($page > 0) ? ($page-1) : '';
1412 if ($id_stop >= $post_count) {
1413 $id_stop = $post_count;
1420 my $words_url = merge_url(
1421 {'path' => CGI_VIEWER_PATH()},
1424 'query' => {'b' => TEXT_MODE->{'words'}},
1427 my $older_url = merge_url(
1430 'query' => {'i' => $page-1},
1431 'fragment' => 'insw',
1435 my $newer_url = merge_url(
1438 'query' => {'i' => $page+1},
1439 'fragment' => 'insw',
1445 $password_query = url_query_encode({'p', $settings->{'password'}});
1446 $older_url = merge_url($older_url, {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1447 $newer_url = merge_url($older_url, {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1450 my $_password = $password_ok ? html_entity_encode_dec($settings->{'password'}, 1) : '';
1451 my $_post_url = html_entity_encode_dec(CGI_WORDS_PATH(), 1);
1452 my $_older_url = html_entity_encode_dec($older_url, 1);
1453 my $_newer_url = html_entity_encode_dec($newer_url, 1);
1455 if (($older ne '') || ($newer ne '')) {
1456 $links .= ' <div class="underlinks">'."\n";
1459 $links .= '<a href="'.$_older_url.'">Older</a>'
1461 if (($older ne '') && ($newer ne '')) {
1465 $links .= '<a href="'.$_newer_url.'">Newer</a>';
1468 $links .= ' </div>'."\n";
1471 print $fh ' <div class="space"></div>'."\n";
1472 print $fh ' <div id="insw" class="ins">'."\n";
1474 print $fh ' <div class="title" id="wordstitle">'."\n";
1475 print $fh ' <h1 class="titletext" id="wordstitletext">Words</h1>'."\n";
1476 print $fh ' </div>'."\n";
1482 print $fh ' <div class="undertext" id="words">'."\n";
1484 if ($post_count > 0) {
1485 for (my $i=$id_start; $i<$id_stop; ++$i) {
1486 my $ID = $words_data->{'content'}->[$i];
1487 my %post_data = read_words($ID);
1489 my $post_time = int($post_data{'posttime'});
1490 my $edit_time = int($post_data{'edittime'});
1495 if ($post_time != 0) {
1496 my @time_tab = gmtime($post_time);
1497 $post_time_text = sprintf(
1498 '%04d.%02d.%02d %02d:%02d:%02d UTC',
1507 if (($edit_time !=0) && ($edit_time != $post_time)) {
1508 my @time_tab = gmtime($edit_time);
1509 $edit_time_text = sprintf(
1510 '%04d.%02d.%02d %02d:%02d UTC',
1518 my $quote_url = merge_url(
1519 {'path' => CGI_WORDS_PATH()},
1527 my $edit_url = merge_url(
1528 {'path' => CGI_WORDS_PATH()},
1533 'key' => $post_data{'key'},
1534 'username' => $post_data{'name'},
1538 my $remove_url = merge_url(
1539 {'path' => CGI_WORDS_PATH()},
1544 'key' => $post_data{'key'},
1545 'username' => $post_data{'name'},
1550 $quote_url = merge_url($quote_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1551 $edit_url = merge_url($edit_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1552 $remove_url = merge_url($remove_url, {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1555 my $_ID = html_entity_encode_dec($ID, 1);
1556 my $_name = html_entity_encode_dec($post_data{'name'}, 1);
1557 my $_quote_url = html_entity_encode_dec($quote_url, 1);
1558 my $_edit_url = html_entity_encode_dec($edit_url, 1);
1559 my $_remove_url = html_entity_encode_dec($remove_url, 1);
1561 print $fh ' <div id="'.$_ID.'"class="opomba">'."\n";
1562 print $fh ' <div class="opomba_info">'."\n";
1563 print $fh ' <a href="#'.$_ID.'" class="bi hu">'.$i.': '.$_name;
1564 if ($post_time_text ne '') {
1565 print $fh ' • '.$post_time_text;
1567 if ($edit_time_text ne '') {
1568 print $fh ' • '.$edit_time_text;
1570 print $fh '</a>'."\n";
1571 print $fh ' <div class="pr">'."\n";
1572 print $fh ' <a href="'.$_quote_url.'" class="bi hu">quote</a> | <a href="'.$_edit_url.'" class="bi hu">edit</a> | <a href="'.$_remove_url.'" class="bi hu">remove</a>'."\n";
1573 print $fh ' </div>'."\n";
1574 print $fh ' </div>'."\n";
1575 print $fh ' <div class="opomba_text">'."\n";
1576 print $fh bb_to_html(
1578 $post_data{'content'},
1580 $password_ok ? $settings->{'password'} : ''
1583 print $fh ' </div>'."\n";
1584 print $fh ' </div>'."\n";
1585 print $fh ' <br>'."\n";
1589 print $fh ' <form method="post" action="'.$_post_url.'">'."\n";
1590 print $fh ' <b>Your words:</b>'."\n";
1591 print $fh ' <textarea class="inta" name="words" rows="4"></textarea>'."\n";
1592 print $fh ' <table cellpadding="0" cellspacing="0" border="0"><tr>'."\n";
1593 print $fh ' <td><b>Your name: </b></td>'."\n";
1594 print $fh ' <td><input class="intx" type="text" name="username" value=""></td>'."\n";
1595 print $fh ' <td></td>'."\n";
1596 print $fh ' </tr><tr>'."\n";
1597 print $fh ' <td><b>Optional password: </b></td>'."\n";
1598 print $fh ' <td><input class="intx" type="password" name="password" value=""></td>'."\n";
1599 print $fh ' <td>(if you want to edit later)</td>'."\n";
1600 print $fh ' </tr><tr>'."\n";
1601 print $fh ' <td><b>Leave this empty: </b></td>'."\n";
1602 print $fh ' <td><input class="intx" type="text" name="password2" value=""></td>'."\n";
1603 print $fh ' <td>'."\n";
1604 print $fh ' <input class="inbt" type="submit" name="post" value="Send">'."\n";
1605 print $fh ' <input class="inbt" type="submit" name="preview" value="Preview">'."\n";
1606 print $fh ' </td>'."\n";
1607 print $fh ' </tr></table>'."\n";
1608 print $fh ' <input type="hidden" name="f" value="'.$frame.'">'."\n";
1610 print $fh ' <input type="hidden" name="p" value="'.$_password.'">'."\n";
1612 print $fh ' </form>'."\n";
1613 print $fh ' </div>'."\n";
1619 print $fh ' </div>'."\n";
1632 my $ong_state = int($state->{'state'});
1634 unless (open_encoded($fh, ">:encoding(UTF-8)", WWW_INDEX_PATH())) {
1638 # normal running story
1639 if ($ong_state > STATE->{'inactive'}) {
1640 my %default = read_default();
1641 my %frame_data = read_frame_data(0, \%default);
1642 my %next_frame_data= read_frame_data(1, \%default);
1643 my %words_data = read_words_list(
1648 $r = print_viewer_page(
1655 'timer_unlocked' => 3, # not relevant
1656 'timer' => 0, # not relevant
1658 'show_command' => 1,
1659 'text_mode' => TEXT_MODE->{'normal'},
1660 'words_page' => 0, # not relevant
1671 # no conditions met, pretend a normal Apache2 index
1672 elsif ($pass != 1) {
1673 my $index_of = CGI_PATH;
1674 $index_of =~ s/\/$//g;
1676 my $_index_of = html_entity_encode_dec($index_of , 1);
1677 my $_2words_date = html_entity_encode_dec(INTF_DATE(), 1);
1678 my $_coin_date = html_entity_encode_dec(COIN_DATE(), 1);
1679 my $_website = html_entity_encode_dec(WEBSITE() , 1);
1681 print_html_start ($fh);
1682 print $fh ' <head>'."\n";
1683 print $fh ' <meta http-equiv="Content-type" content="text/html; charset=UTF-8">'."\n";
1684 print $fh ' <title>Index of '.$_index_of.'</title>'."\n";
1685 print $fh ' </head>'."\n";
1686 print $fh ' <body>'."\n";
1687 print $fh ' <h1>Index of '.$_index_of.'</h1>'."\n";
1688 print $fh ' <table>'."\n";
1689 print $fh ' <tr>'."\n";
1690 print $fh ' <th><img src="/icons/blank.gif" alt="[ICO]"></th>'."\n";
1691 print $fh ' <th><a href="?C=N;O=D">Name</a></th>'."\n";
1692 print $fh ' <th><a href="?C=M;O=A">Last modified</a></th>'."\n";
1693 print $fh ' <th><a href="?C=S;O=A">Size</a></th>'."\n";
1694 print $fh ' <th><a href="?C=D;O=A">Description</a></th>'."\n";
1695 print $fh ' </tr><tr>'."\n";
1696 print $fh ' <th colspan="5"><hr></th>'."\n";
1697 print $fh ' </tr><tr>'."\n";
1698 print $fh ' <td valign="top"><img src="/icons/back.gif" alt="[DIR]"></td>'."\n";
1699 print $fh ' <td><a href="/">Parent Directory</a></td>'."\n";
1700 print $fh ' <td> </td>'."\n";
1701 print $fh ' <td align="right"> - </td>'."\n";
1702 print $fh ' <td> </td>'."\n";
1703 print $fh ' </tr><tr>'."\n";
1704 print $fh ' <td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td>'."\n";
1705 print $fh ' <td><a href="2words/">2words/</a></td>'."\n";
1706 print $fh ' <td align="right">'.$_2words_date.' </td>'."\n";
1707 print $fh ' <td align="right"> - </td><td> </td>'."\n";
1708 print $fh ' </tr><tr>'."\n";
1709 print $fh ' <td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td>'."\n";
1710 print $fh ' <td><a href="coin/">coin/</a></td>'."\n";
1711 print $fh ' <td align="right">'.$_coin_date.' </td>'."\n";
1712 print $fh ' <td align="right"> - </td><td> Coincidence </td>'."\n";
1713 print $fh ' </tr><tr>'."\n";
1714 print $fh ' <th colspan="5"><hr></th>'."\n";
1715 print $fh ' </tr>'."\n";
1716 print $fh ' </table>'."\n";
1717 print $fh ' <address>Apache/2.2.22 (Debian) Server at '.$_website.' Port 80</address>'."\n";
1718 print $fh ' </body>'."\n";
1719 print_html_end ($fh);
1723 my %default = read_default();
1724 my %frame_data = read_frame_data(0, \%default);
1725 my %next_frame_data= read_frame_data(1, \%default);
1726 my %coin_data = read_coincidence();
1728 if (($mode == INTF_STATE->{'>'}) && $pause) {
1729 $r = print_viewer_page(
1736 'timer_unlocked' => 3,
1739 'show_command' => 1,
1740 'text_mode' => TEXT_MODE->{'normal'},
1741 'words_page' => 0, # not relevant
1749 {'posts' => 0} # words_data
1754 my $index_of = CGI_PATH;
1755 $index_of =~ s/\/$//g;
1759 my $show_parent_dir = 0;
1761 my $show_folders = 0;
1763 my $timer_color = 'ni';
1764 if ($mode == INTF_STATE->{'>'}) {
1765 $title = $settings->{'story'}; # $frame_data{'title'} ?
1766 $frame_file = 'intf-tr.gif';
1770 elsif ($mode == INTF_STATE->{'<<'}) {
1771 $title = 'Index of';
1772 $frame_file = 'intf-ll.gif';
1773 $show_parent_dir = 1;
1776 $timer_color = 'br';
1778 elsif ($mode == INTF_STATE->{'>>'}) {
1779 $title = 'Index of';
1780 $frame_file = 'intf-pp.gif';
1781 $show_parent_dir = 1;
1787 $title = 'Index of '.$index_of;
1788 $frame_file = 'intf-kw.gif';
1789 $show_parent_dir = 1;
1792 my $frame_url = merge_url(
1793 {'path' => CGI_PATH()},
1794 {'path' => $frame_file}
1796 my $coin_server = $coin_data{'server'};
1798 my $_title = html_entity_encode_dec($title , 1);
1799 my $_website_name = html_entity_encode_dec(WEBSITE_NAME() , 1);
1800 my $_frame_url = html_entity_encode_dec($frame_url , 1);
1801 my $_undertext = html_entity_encode_dec($undertext , 1);
1802 my $_2words_date = html_entity_encode_dec(INTF_DATE() , 1);
1803 my $_coin_date = html_entity_encode_dec(COIN_DATE() , 1);
1804 my $_coin_server = html_entity_encode_dec($coin_server , 1);
1805 my $_2words_url = html_entity_encode_dec(CGI_2WORDS_PATH(), 1);
1806 my $_coin_url = html_entity_encode_dec(CGI_COIN_PATH() , 1);
1808 print_html_start($fh);
1809 print_html_head_start($fh);
1811 print $fh ' <title>'.$_title.' • '.$_website_name.'</title>'."\n";
1813 print_html_head_end($fh);
1814 print_html_body_start($fh);
1816 print $fh ' <div id="inst" class="ins">'."\n";
1818 print $fh ' <div id="title">'."\n";
1819 print $fh ' <h1 id="titletext">'.$_title.'</h1>'."\n";
1820 print $fh ' </div>'."\n";
1822 print $fh ' </div>'."\n";
1823 print $fh ' <div id="framespace">'."\n";
1825 print $fh ' <img src="'.$_frame_url.'" id="frame" alt="0">'."\n"; # title="'.$_title.'"
1827 print $fh ' </div>'."\n";
1828 print $fh ' <div id="insb" class="ins">'."\n";
1830 print $fh ' <div id="undertext">'."\n";
1832 if ($show_parent_dir) {
1833 print $fh ' <img src="/icons/back.gif" alt="[DIR]"> <a href="..">Parent Directory</a><br>'."\n";
1835 if ($show_folders) {
1836 print $fh ' <img src="/icons/folder.gif" alt="[DIR]"> <a href="'.$_2words_url.'">2words/</a> '.$_2words_date.' - <br>'."\n";
1837 print $fh ' <img src="/icons/folder.gif" alt="[DIR]"> <a href="'.$_coin_url.'">coin/</a> '.$_coin_date.' - '.$_coin_server."\n";
1840 print $fh ' <img src="/icons/folder.gif" alt="[DIR]"> <a href="'.$_2words_url.'">yyyyb/</a>'."\n";
1842 if ($undertext ne '') {
1843 print $fh ' '.$_undertext."\n";
1846 print $fh ' </div>'."\n";
1849 print $fh ' <div id="command">'."\n";
1851 print $fh ' [<span id="ongh" class="'.$timer_color.'">'.$timer.'</span>';
1852 print $fh ':<span id="ongm" class="'.$timer_color.'">'.$timer.'</span>';
1853 print $fh ':<span id="ongs" class="'.$timer_color.'">'.$timer.'</span>]<br>'."\n";
1855 if ($undertext ne '') {
1856 print $fh '><a href="'.$_2words_url.'">'.$_undertext.'</a><span class="inp">_</span>'."\n";
1858 print $fh " </div>\n";
1861 print $fh " </div>\n";
1863 print_html_body_end($fh, $ong_state == STATE->{'inactive'});
1864 print_html_end($fh);
1870 sub write_static_viewer_page {
1877 my $prev_frame_data_ref,
1878 my $next_frame_data_ref,
1886 my %prev_frame_data;
1887 my %next_frame_data;
1892 $frame = int($frame);
1893 my $prev_frame = $frame -1;
1894 my $next_frame = $frame +1;
1896 %state = (ref ($state_ref)) ?
1899 my $ong_state = int($state{'state'});
1900 my $last_frame = int($state{'last'});
1902 unless ($ong_state > STATE->{'inactive'}) {
1907 ($frame < $last_frame) || (
1908 ($frame <= $last_frame) &&
1909 ($ong_state >= STATE->{'end'})
1916 %settings = (ref ($settings_ref)) ?
1919 %default = (ref ($default_ref)) ?
1923 %frame_data = (ref ($frame_data_ref)) ?
1925 read_frame_data($frame);
1927 %prev_frame_data = (ref ($prev_frame_data_ref)) ?
1928 %$prev_frame_data_ref : (
1929 ($prev_frame >= 0) ?
1930 read_frame_data($prev_frame) :
1934 %next_frame_data = (ref ($next_frame_data_ref)) ?
1935 %$next_frame_data_ref :
1936 read_frame_data($next_frame);
1938 %words_data = (ref ($words_data_ref)) ?
1945 %frame_data = merge_settings(\%default, \%frame_data);
1946 %prev_frame_data = merge_settings(\%default, \%prev_frame_data);
1947 %next_frame_data = merge_settings(\%default, \%next_frame_data);
1949 $file = get_page_file($frame, \%frame_data, \%settings);
1950 $file = join_path(PATH_SEPARATOR(), WWW_PATH(), $file);
1952 return print_viewer_page(
1959 'timer_unlocked'=> 3, # not relevant
1960 'timer' => 0, # not relevant
1962 'show_command' => 1,
1963 'text_mode' => TEXT_MODE->{'normal'},
1964 'words_page' => 0, # not relevant
1976 sub write_static_goto {
1977 (my $state_ref, my $settings_ref, my $goto_ref) = @_;
1982 %state = (ref ($state_ref)) ?
1985 %settings = (ref ($settings_ref)) ?
1988 %goto_list = (ref ($goto_ref)) ?
2001 # ONG the frame + attachment & stuff. NOT update state file.
2004 my $ID, my $ongtime, my $timer, my $update, my $print,
2005 my $settings_ref, my $default_ref, my $data_ref, my $goto_ref
2011 my $frame_data_path;
2019 my %frame_full_data;
2022 if ($ongtime eq '') {
2029 elsif ($ID eq 'c') {
2061 %settings = (ref ($settings_ref)) ?
2064 %default = (ref ($default_ref)) ? %$default_ref : read_default();
2065 $frame_data_path = $cfrt ?
2066 DATA_NOACCESS_PATH() :
2067 join_path(PATH_SEPARATOR(), DATA_PATH(), $frame);
2068 %frame_data = (ref ($data_ref)) ?
2070 read_frame_data($frame_data_path);
2071 %frame_full_data = merge_settings(\%default, \%frame_data);
2072 @files = (get_frame_file($frame, \%frame_full_data, \%settings), );
2074 %goto_list = (ref ($goto_ref)) ?
2077 for (my $i=0; ;$i+=1) {
2078 my %file_data = read_attachment($i);
2079 if ($file_data{'frame'} eq '') {
2082 if (int($file_data{'frame'}) != $frame) {
2085 if ($file_data{'content'} ne '') {
2088 unshift @files, $file_data{'filename'};
2092 ($frame_full_data{'ongtime'} eq '')
2094 $frame_data {'ongtime'} = $ongtime;
2095 $frame_full_data{'ongtime'} = $ongtime;
2101 ($frame_full_data{'timer'} eq '')
2104 $frame_data{'timer'} = int($timer);
2108 $r = write_frame_data($frame_data_path, \%frame_data);
2110 print STDERR "fail writing $frame_data_path\n";
2112 print "write frame data fail\n";
2117 $goto_list{'title-' .$frame} = $frame_full_data{'title'};
2118 $goto_list{'ongtime-'.$frame} = $frame_full_data{'ongtime'};
2119 $r = write_goto('', \%goto_list);
2121 print STDERR "fail writing ".DATA_LIST_PATH()."\n";
2123 print "write GOTO list fail\n";
2129 foreach my $file (@files) {
2130 $in_path = join_path(PATH_SEPARATOR(), DATA_PATH(), $file);
2131 $out_path = join_path(PATH_SEPARATOR(), WWW_PATH() , $file);
2133 print $in_path.' -> '.$out_path;
2135 $r = copy_encoded($in_path, $out_path);
2137 print (($r) ? " OK\n" : " FAIL\n");
2140 print STDERR "fail copy $in_path $out_path\n";
2149 sub get_frame_file {
2150 (my $frame, my $frame_data, my $settings) = @_;
2154 if ($frame_data->{'frame'} ne '') {
2155 $file = $frame_data->{'frame'};
2158 $pattern = validate_filename($settings->{'frame'}, '%d.%ext');
2161 int($frame), $frame_data->{'ext'}
2164 return validate_filename($file);
2168 (my $frame, my $frame_data, my $settings) = @_;
2175 if ($frame_data->{'page'} ne '') {
2176 $file = $frame_data->{'page'};
2179 $pattern = validate_filename($settings->{'frame'}, '%d.%ext');
2185 return validate_filename($file);
2188 sub validate_filename {
2189 (my $filename, my $fallback) = @_;
2190 if ($fallback eq '') {
2196 if ($filename =~ /^\./) {
2199 if (index($filename, PATH_SEPARATOR()) >= 0) {
2205 sub validate_frame_data {
2207 my %data = %$data_in;
2209 if ($data{'ongtime'} ne '') {
2210 $data{'ongtime'} = int($data{'ongtime'});
2212 if ($data{'timer'} ne '') {
2213 $data{'timer'} = int($data{'timer'});
2215 if ($data{'width'} ne '') {
2216 $data{'width'} = int($data{'width'});
2218 if ($data{'height'} ne '') {
2219 $data{'height'} = int($data{'height'});
2221 if ($data{'page'} ne '') {
2222 $data{'page'} = validate_filename($data{'page'});
2224 if ($data{'frame'} ne '') {
2225 $data{'frame'} = validate_filename($data{'frame'});
2231 sub validate_settings {
2233 my %data = %$data_in;
2235 if ($data{'ongtime'} ne '') {
2236 $data{'ongtime'} = int($data{'ongtime'});
2238 if ($data{'dynamicongtime'} ne '') {
2239 $data{'dynamicongtime'} = int($data{'dynamicongtime'});
2241 if ($data{'firstongtime'} ne '') {
2242 $data{'firstongtime'} = int($data{'firstongtime'});
2244 if ($data{'last'} ne '') {
2245 $data{'last'} = int($data{'last'});
2247 $data{'frame'} = validate_filename($data{'frame'}, '%d.%s');
2252 sub validate_state {
2254 my %data = %$data_in;
2256 if ($data{'state'} ne '') {
2257 $data{'state'} = int($data{'state'});
2259 if ($data{'last'} ne '') {
2260 $data{'last'} = int($data{'last'});
2262 if ($data{'nextong'} ne '') {
2263 $data{'nextong'} = int($data{'nextong'});
2269 sub validate_words_list {
2270 (my $data_in, my $not_list) = @_;
2271 my %data = %$data_in;
2273 if ($data{'ongtime'} ne '') {
2274 $data{'ongtime'} = int($data{'ongtime'});
2279 foreach my $ID (split(/\r?\n/, $data{'content'})) {
2280 $ID = validate_filename($ID);
2282 $id_list .= $ID."\n";
2285 $data{'content'} = $id_list;
2289 foreach my $ID (@{$data{'content'}}) {
2291 $ID = validate_filename($ID);
2296 $data{'content'} = [@id_list];
2302 sub validate_words {
2304 my %data = %$data_in;
2306 if ($data{'posttime'} ne '') {
2307 $data{'posttime'} = int($data{'posttime'});
2309 if ($data{'edittime'} ne '') {
2310 $data{'edittime'} = int($data{'edittime'});
2316 sub validate_story {
2318 my %data = %$data_in;
2320 if ($data{'id'} ne '') {
2321 $data{'id'} = int($data{'id'});
2323 if ($data{'pass'} ne '') {
2324 $data{'pass'} = int($data{'pass'});
2326 if ($data{'state'} ne '') {
2327 $data{'state'} = int($data{'state'});
2335 my %data = %$data_in;
2337 foreach my $key (keys %data) {
2338 if ($key =~ /^ongtime-([0-9]+)$/) {
2339 my $new_key = 'ongtime-'.int($1);
2340 $data{$new_key} = int($data{$key});
2341 if ($new_key != $key) {
2350 sub validate_attachment {
2352 my %data = %$data_in;
2354 if ($data{'frame'} ne '') {
2355 $data{'frame'} = int($data{'frame'});
2357 $data{'filename'} = validate_filename($data{'filename'});
2362 sub validate_coincidence {
2364 my %data = %$data_in;
2366 if ($data{'server'} ne '') {
2367 $data{'server'} = int($data{'server'});
2373 sub read_frame_data {
2374 (my $f, my $default) = @_;
2378 if (ref ($f)) { # already open file
2381 elsif ($f =~ /^[0-9]+$/) { # frame ID
2382 $file = join_path(PATH_SEPARATOR(), DATA_PATH(), int($&));
2384 elsif ($f =~ /^(c(frt)?)|(noaccess)$/) { # CFRT (no access)
2385 $file = DATA_NOACCESS_PATH();
2387 elsif ($f =~ /^d(efault)?$/) { # default
2388 $file = DATA_DEFAULT_PATH();
2390 elsif ($f ne '') { # path
2394 $file = DATA_DEFAULT_PATH();
2397 %data = read_data_file($file);
2398 if (ref ($default)) {
2399 %data = merge_settings($default, \%data);
2401 elsif ($default ne '') {
2402 my %default_data = read_data_file(DATA_DEFAULT_PATH());
2403 %data = merge_settings(\%default_data, \%data);
2406 return validate_frame_data(\%data);
2409 sub write_frame_data {
2410 (my $f, my $data) = @_;
2413 if (ref ($f)) { # already open file
2416 elsif ($f =~ /^[0-9]+$/) { # frame ID
2417 $file = join_path(PATH_SEPARATOR(), DATA_PATH(), int($&));
2419 elsif ($f =~ /^(c(frt)?)|(noaccess)$/) { # CFRT (no access)
2420 return 0; # forbidden
2422 elsif ($f =~ /^d(efault)?$/) { # default
2423 return 0; # forbidden
2425 elsif ($f ne '') { # path
2429 return 0; # forbidden
2432 my %_data = validate_frame_data($data);
2434 return write_data_file($file, \%_data);
2438 return read_frame_data('default');
2443 return read_frame_data('noaccess', $default);
2451 if (ref ($f)) { # already open file
2454 elsif ($f ne '') { # path
2458 $file = DATA_SETTINGS_PATH();
2461 %data = read_data_file($file);
2463 return validate_settings(\%data);
2471 if (ref ($f)) { # already open file
2474 elsif ($f ne '') { # path
2478 $file = DATA_STATE_PATH();
2481 %data = read_data_file($file);
2483 return validate_state(\%data);
2487 (my $f, my $data) = @_;
2490 if (ref ($f)) { # already open file
2493 elsif ($f ne '') { # path
2497 $file = PERL_DATA_STATE_PATH();
2500 my %_data = validate_state($data);
2502 return write_data_file($file, \%_data);
2505 sub read_words_list {
2506 (my $f, my $header_only, my $not_list) = @_;
2510 if (ref ($f)) { # already open file
2513 elsif ($f =~ /^[0-9]+$/) { # frame ID
2514 $file = join_path(PATH_SEPARATOR(), DATA_WORDS_PATH(), int($&));
2516 elsif ($f ne '') { # path
2519 else { # which frame ???
2520 return ('posts' => 0);
2523 %data = read_data_file(
2528 not $not_list # as list
2531 return validate_words_list(\%data, $not_list);
2534 sub write_words_list {
2535 (my $f, my $data) = @_;
2538 if (ref ($f)) { # already open file
2541 elsif ($f =~ /^[0-9]+$/) { # frame ID
2542 $file = join_path(PATH_SEPARATOR(), DATA_WORDS_PATH(), int($&));
2544 elsif ($f ne '') { # path
2547 else { # which frame ???
2551 my %_data = validate_words_list($data);
2553 return write_data_file(
2564 (my $f, my $default) = @_;
2568 if (ref ($f)) { # already open file
2571 elsif ($f =~ /^[0-9]+\.[0-9\.]+$/) { # post ID
2572 $file = join_path(PATH_SEPARATOR(), DATA_WORDS_PATH(), $&);
2574 elsif ($f ne '') { # path
2577 else { # which post ???
2581 %data = read_data_file($file);
2583 return validate_words(\%data);
2587 (my $f, my $data) = @_;
2590 if (ref ($f)) { # already open file
2593 elsif ($f =~ /^[0-9\.]+$/) { # post ID
2594 $file = join_path(PATH_SEPARATOR(), DATA_WORDS_PATH(), $&);
2596 elsif ($f ne '') { # path
2599 else { # which post ???
2603 my %_data = validate_words($data);
2605 return write_data_file($file, \%_data);
2613 if (ref ($f)) { # already open file
2616 elsif ($f =~ /^[0-9]+$/) { # story ID
2617 $file = DATA_STORY_PATH().int($&);
2619 elsif ($f ne '') { # path
2623 $file = DATA_STORY_PATH();
2626 %data = read_data_file($file);
2628 return validate_story(\%data);
2632 (my $f, my $data) = @_;
2635 if (ref ($f)) { # already open file
2638 elsif ($f =~ /^[0-9]+$/) { # story ID
2639 $file = DATA_STORY_PATH().int($&);
2641 elsif ($f ne '') { # path
2645 $file = DATA_STORY_PATH();
2648 my %_data = validate_story($data);
2650 return write_data_file($file, \%_data);
2658 if (ref ($f)) { # already open file
2661 elsif ($f ne '') { # path
2665 $file = DATA_LIST_PATH();
2668 %data = read_data_file($file);
2670 return validate_goto(\%data);
2674 (my $f, my $data) = @_;
2677 if (ref ($f)) { # already open file
2680 elsif ($f ne '') { # path
2684 $file = DATA_LIST_PATH();
2687 my %_data = validate_goto($data);
2689 return write_data_file($file, \%_data);
2692 sub read_attachment {
2693 (my $f, my $default) = @_;
2697 if (ref ($f)) { # already open file
2700 elsif ($f =~ /^[0-9]+$/) { # attachment ID
2701 $file = DATA_ATTACH_PATH().int($&);
2703 elsif ($f ne '') { # path
2710 %data = read_data_file($file);
2712 return validate_attachment(\%data);
2715 sub read_coincidence {
2720 if (ref ($f)) { # already open file
2723 elsif ($f ne '') { # path
2727 $file = DATA_COIN_PATH();
2730 %data = read_data_file($file);
2732 return validate_coincidence(\%data);
2740 if (ref ($f)) { # already open file
2743 elsif ($f =~ /^[0-9]+$/) { # chat ID
2744 $file = DATA_CHAT_PATH().int($&);
2746 elsif ($f ne '') { # path
2750 $file = DATA_CHAT_PATH();
2753 return read_data_file($file);
2759 (my $f, my $data) = @_;
2762 if (ref ($f)) { # already open file
2765 elsif ($f =~ /^[0-9]+$/) { # chat ID
2766 $file = DATA_CHAT_PATH().int($&);
2768 elsif ($f ne '') { # path
2772 $file = DATA_CHAT_PATH();
2777 return write_data_file($file, $data);