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',
40 'fail_method', 'fail_content_type', 'fail_open_file', 'fail_attachment', 'fail_500',
42 'get_remote_addr', 'get_id', 'get_frame', 'get_password',
44 'print_html_start', 'print_html_end',
45 'print_html_head_start', 'print_html_head_end',
46 'print_html_body_start', 'print_html_body_end',
47 'print_viewer_page', 'print_goto',
48 'write_index', 'write_static_viewer_page', 'write_static_goto',
49 'get_frame_file', 'get_page_file',
50 'read_frame_data', 'write_frame_data', 'read_default', 'read_noaccess',
51 'read_state', 'write_state',
52 'read_words_list', 'write_words_list', 'read_words', 'write_words',
53 'read_story', 'write_story',
54 'read_goto', 'write_goto',
55 'read_chat', 'write_chat',
56 'read_settings', 'read_attachment', 'read_coincidence',
58 'eval_bb', 'bb_to_bbcode', 'bb_to_html'
61 ###PERL_LIB: use lib /botm/lib/bsta
64 'url_query_decode', 'url_query_encode',
65 'url_decode', 'url_encode',
66 'html_entity_encode_dec',
69 'read_data_file', 'write_data_file',
71 'copy_encoded', 'open_encoded', '_x_encoded',
72 'http_header_line', 'http_status',
73 'http_header_status', 'http_header_allow', 'http_header_location'
76 ###PERL_PATH_SEPARATOR: PATH_SEPARATOR = /
78 ###PERL_CGI_PATH: CGI_PATH = /bsta/
79 ###PERL_CGI_ATTACH_PATH: CGI_ATTACH_PATH = /bsta/a
80 ###PERL_CGI_2WORDS_PATH: CGI_2WORDS_PATH = /bsta/2words
81 ###PERL_CGI_BBCODE_PATH: CGI_BBCODE_PATH = /bsta/b
82 ###PERL_DATA_CHAT_PATH: DATA_CHAT_PATH = /botm/data/bsta/chat
83 ###PERL_CGI_COIN_PATH: CGI_COIN_PATH = /bsta/coin
84 ###PERL_CGI_CSS_PATH: CGI_CSS_PATH = /bsta/bsta.css
85 ###PERL_CGI_FRAME_PATH: CGI_FRAME_PATH = /bsta/f
86 ###PERL_CGI_GOTO_PATH: CGI_GOTO_PATH = /bsta/g
87 ###PERL_CGI_INFO_PATH: CGI_INFO_PATH = /bsta/i
88 ###PERL_CGI_LIST_PATH: CGI_LIST_PATH = /bsta/goto.htm
89 ###PERL_CGI_LOGO_PATH: CGI_LOGO_PATH = /bsta/botmlogo.png
90 ###PERL_CGI_TIMER_PATH: CGI_TIMER_PATH = /bsta/timer.js
91 ###PERL_CGI_VIEWER_PATH: CGI_VIEWER_PATH = /bsta/v
92 ###PERL_CGI_WORDS_PATH: CGI_WORDS_PATH = /bsta/w
94 ###PERL_DATA_PATH: DATA_PATH = /botm/data/bsta/
95 ###PERL_DATA_ATTACH_PATH: DATA_ATTACH_PATH = /botm/data/bsta/a
96 ###PERL_DATA_COIN_PATH: DATA_COIN_PATH = /botm/data/bsta/coincidence
97 ###PERL_DATA_DEFAULT_PATH: DATA_DEFAULT_PATH = /botm/data/bsta/default
98 ###PERL_DATA_LIST_PATH: DATA_LIST_PATH = /botm/data/bsta/list
99 ###PERL_DATA_NOACCESS_PATH: DATA_NOACCESS_PATH = /botm/data/bsta/noaccess
100 ###PERL_DATA_SETTINGS_PATH: DATA_SETTINGS_PATH = /botm/data/bsta/settings
101 ###PERL_DATA_STATE_PATH: DATA_STATE_PATH = /botm/data/bsta/state
102 ###PERL_DATA_STORY_PATH: DATA_STORY_PATH = /botm/data/bsta/story
103 ###PERL_DATA_WORDS_PATH: DATA_WORDS_PATH = /botm/data/bsta/words/
105 ###PERL_WWW_PATH: WWW_PATH = /botm/www/
106 ###PERL_WWW_GOTO_PATH: WWW_GOTO_PATH = /botm/www/1190/bsta/goto.htm
107 ###PERL_WWW_INDEX_PATH: WWW_INDEX_PATH = /botm/www/1190/bsta/index.htm
109 ###PERL_SCHEME: SCHEME = http
110 ###PERL_WEBSITE: WEBSITE = 1190.bicyclesonthemoon.info
111 ###PERL_WEBSITE_NAME: WEBSITE_NAME = Bicycles on the Moon
112 ###PERL_FAVICON_PATH: FAVICON_PATH = /img/favicon.png
114 ###PERL_COIN_DATE: COIN_DATE = 13-Nov-2016 22:15
115 ###PERL_INTF_DATE: INTF_DATE = 28-Sep-2016 20:34
117 ###PERL_STORY_CREDITS: STORY_CREDITS = "BSTA" by Balthasar Szczepański
118 ###PERL_INTF_CREDITS: INTF_CREDITS = Online interface © Balthasar Szczepański; AGPL 3 license
119 ###PERL_SOURCE_URL: SOURCE_URL = http://bicyclesonthemoon.info/git-projects/?p=ott/bsta
121 ###PERL_COMMENT_PAGE_LENGTH:COMMENT_PAGE_LENGTH= 16
123 use constant STATE => {
129 use constant INTF_STATE => {
142 use constant TEXT_MODE => {
148 use constant CHAT_STATE => {
153 use constant CHAT_ACTION => {
161 use constant tags_bbcode => {
168 'quote' => '[quote]',
169 'quote=' => '[quote="',
171 '/quote' => '[/quote]',
172 'ni' => '[color=#0057AF]',
174 'br' => '[color=#BB6622]',
176 'po' => '[color=#FF8800]',
187 '/list' => '[/list]',
191 '/?' => '[/unknown!]',
193 use constant tags_html => {
196 'fq' => '<div class="fq">',
198 'tq' => '<div class="tq">',
200 'quote' => '<div class="opomba"><div class="opomba_text">',
201 'quote=' => '<div class="opomba"><div class="opomba_info"><b>',
202 'quote/='=> '</b> wrote:</div><div class="opomba_text">',
203 '/quote' => '</div></div>',
204 'ni' => '<span class="ni">',
206 'br' => '<span class="br">',
208 'po' => '<span class="po">',
210 'url' => '<a href="#">',#think: how to add selfincluding?
211 'url=' => '<a href="',
217 'list=' => '<ol style="list-style-type: ',
218 'list=1' => 'decimal',
219 'list=A' => 'upper-alpha',
220 'list=a' => 'lower-alpha',
221 'list=I' => 'upper-roman',
222 'list=i' => 'lower-roman',
229 '/?' => '[/unknown!]',
233 # Function to return an error page
234 # arguments: 1 - header fields, 2 - page title, 3 - error message, 4 method
236 (my $header, my $title, my $message, my $method, my $hyperlink) = @_;
239 foreach my $header_name (keys %$header) {
240 print http_header_line($header_name, $header->{$header_name});
243 elsif($header ne '') {
246 if($method eq 'HEAD') {
250 my $_title = html_entity_encode_dec($title , 1);
251 my $_message = html_entity_encode_dec($message , 1);
252 my $_hyperlink = html_entity_encode_dec($hyperlink, 1);
254 print "Content-type: text/html; charset=UTF-8\n\n";
256 print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "">'."\n";
257 print ' <html lang="en">'."\n";
258 print ' <head>'."\n";
259 print ' <meta http-equiv="Content-type" content="text/html; charset=UTF-8">'."\n";
261 print ' <title>'.$_title.'</title>'."\n";
263 print ' </head>'."\n";
264 print ' <body>'."\n";
266 print ' <h1>'.$_title.'</h1>'."\n";
268 if (($message ne '') || ($hyperlink ne '')) {
270 if ($message ne '') {
271 print ' '.$_message.($hyperlink ne '' ? '<br>' : '')."\n";
273 if ($hyperlink ne '') {
274 print ' <a href="'.$_hyperlink.'">'.$_hyperlink."</a>\n";
278 print ' </body>'."\n";
279 print '</html>'."\n";
283 (my $method, my $allowed) = @_;
285 my $status = http_status(HTTP_STATUS->{'method_not_allowed'});
287 http_header_line('status', $status) .
288 http_header_allow($allowed);
293 "The interface does not support the $method method.",
298 sub fail_content_type
300 (my $method, my $content_type) = @_;
302 my $status = http_status(HTTP_STATUS->{'unsupported_media_type'});
303 my $header = http_header_line('status', $status);
308 "Unsupported Content-type: $content_type.",
315 (my $method, my $type, my $path) = @_;
317 my $status = http_status(HTTP_STATUS->{'not_found'});
318 my $header = http_header_line('status', $status);
324 ($type ne '' ? $type : 'file').
325 ($path ne '' ? ': "'.$path.'"' : '').
333 (my $method, my $ID) = @_;
335 my $status = http_status(HTTP_STATUS->{'not_found'});
336 my $header = http_header_line('status', $status);
341 "Attachment $ID not found.",
348 (my $method, my $text) = @_;
350 my $status = http_status(HTTP_STATUS->{'internal_server_error'});
351 my $header = http_header_line('status', $status);
363 (my $method, my $uri, my $code) = @_;
367 $code = HTTP_STATUS->{'found'};
369 # https://insanecoding.blogspot.com/2014/02/http-308-incompetence-expected.html
370 # 301 Moved Permanently
373 # 307 Temporary Redirect
374 # 308 Permanent Redirect
375 $status = http_status($code);
376 $header = http_header_line('status', $status);
377 $header .= http_header_location($uri);
389 # function to obtain address of remote agent
390 sub get_remote_addr {
391 if ($ENV{'HTTP_X_FORWARDED_FOR'} =~ /^.+$/) {
394 elsif ($ENV{'REMOTE_ADDR'} =~ /^.+$/) {
402 # functions to get ID/number etc.
404 (my $cgi, my $default, my $cgi_name) = @_;
405 if ($default eq '') {
408 if ($cgi_name eq '') {
412 if ($cgi->{$cgi_name} =~ /^.+$/) {
415 elsif ($ENV{'PATH_INFO'} =~ /^\/(.+)$/) {
419 return int($default);
423 # function to obtain frame number
425 (my $cgi, my $default) = @_;
426 return get_id($cgi, $default, 'f');
429 # function to obtain password
433 if ($cgi->{'p'} =~ /^.+$/) {
445 foreach my $settings (@_) {
446 foreach my $ind (keys %$settings) {
447 $final_settings{$ind} = $settings->{$ind};
450 return %final_settings;
455 # different & simpler implementation than in post library
459 #analyse bbcode text to build tag tree
460 #TODO make [/*] optional!
462 (my $bb, my $printdebug) = @_;
475 $bbtree{"_.name" } = "ht";
476 $bbtree{"_.value" } = '';
477 $bbtree{"_.type" } = "tag";
478 $bbtree{"_.count" } = 0;
479 $bbtree{"_.closed"} = 0;
480 $debug .= debug($printdebug,
482 "<!--GENERATING BBCODE TREE:\n".
483 '[_]automatic tag: [ht]'."\n"
487 my $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
489 if($bb =~ m/\[(\/?)([A-Za-z]+|\*)(=([^\[\]]*))?\]/g) {
496 if ($tag_value =~ /^"(.*)"$/) {
500 if ($pre_text ne '') {
501 $debug .= debug($printdebug, "[$new_ind]text: $pre_text\n");
502 $bbtree{$new_ind.'.type' } = 'text';
503 $bbtree{$new_ind.'.value'} = $pre_text;
504 $bbtree{ $ind.'.count'}+= 1;
505 $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
508 if($tag_name =~ /^(fq|tq|quote|br|ni|po|url|i|list|\*)$/) {
509 if ($tag_end ne '') {
511 ($tag_name ne $bbtree{$ind.'.name'}) ||
514 $debug .= debug($printdebug, "[$new_ind]text: $tag\n");
515 $bbtree{$new_ind.'.type' } = 'text';
516 $bbtree{$new_ind.'.value'} = $tag;
517 $bbtree{ $ind.'.count'}+= 1;
518 # $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
521 $debug .= debug($printdebug, "[$new_ind]tag: $tag\n");
522 $bbtree{$new_ind.'.type' } = 'tag';
523 $bbtree{$new_ind.'.name' } = '/'.$tag_name;
524 $bbtree{$new_ind.'.value' } = $tag_value;
525 $bbtree{ $ind.'.count' }+= 1;
526 $bbtree{ $ind.'.closed'} = 1;
528 $ind =~ s/\.[0-9]+$//;
529 # $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
534 $debug .= debug($printdebug, "[$new_ind]tag: $tag\n");
535 $bbtree{$new_ind.'.type' } = 'tag';
536 $bbtree{$new_ind.'.name' } = $tag_name;
537 $bbtree{$new_ind.'.value' } = $tag_value;
538 $bbtree{$new_ind.'.count' } = 0;
539 $bbtree{$new_ind.'.closed'} = 0;
540 $bbtree{ $ind.'.count' }+= 1;
543 # $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
547 $debug .= debug($printdebug, "[$new_ind]text: $tag\n");
548 $bbtree{$new_ind.'.type' } = 'text';
549 $bbtree{$new_ind.'.value'} = $tag;
550 $bbtree{ $ind.'.count'}+= 1;
551 # $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
555 $debug .= debug($printdebug, "[$new_ind]text: $bb\n");
556 $bbtree{$new_ind.'.type' } = 'text';
557 $bbtree{$new_ind.'.value'} = $bb;
558 $bbtree{ $ind.'.count'}+= 1;
559 # $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
563 my $final_ind = '_.'.$bbtree{"_.count"};
564 $debug .= debug($printdebug, "[$final_ind]automatic tag: [/ht]\n -->\n");
565 $bbtree{$final_ind.'.type' } = "tag";
566 $bbtree{$final_ind.'.name' } = '/ht';
567 $bbtree{ '_.count' }+= 1;
568 $bbtree{ '_.closed'} = 1;
570 return ($debug, %bbtree);
573 #convert tag tree to final text
575 (my $printdebug, my $debug, my $lang, my $bbtree) = @_;
580 my $tags = ($lang eq 'html') ? tags_html : tags_bbcode;
581 my $escape = ($lang eq 'html');
583 # $debug .= debug($printdebug, "\n****\n");
584 # foreach my $iiii (keys %tags) {
585 # $debug .= debug($printdebug, $iiii.'='.$tags->{$iiii}."\n");
587 # $debug .= debug($printdebug, "****\n");
592 $debug .= debug($printdebug, "\n<!--PROCESSING BBCODE TREE:\n");
594 while ($level >= 0) {
596 $debug .= debug($printdebug, "[$level:$ind:".int($bbtree->{$ind.'.count'})."]");
598 if ($bbtree->{$ind.'.type'} eq 'text') {
599 my $text = $bbtree->{$ind.'.value'};
600 $debug .= debug($printdebug, "text: ".$text);
601 $out .= $escape ? html_encode_line($text) : $text;
606 elsif ($bbtree->{$ind.'.type'} eq 'tag') {
607 my $name = $bbtree->{$ind.'.name'};
609 if ($name =~ /^\//) {
610 $debug .= debug($printdebug, "tag: [$name]");
612 $indd =~ s/\.([0-9]+)$//;
613 if (exists($tags->{$name.'='}) && ($bbtree->{$indd.'.value'} ne '')) {
614 $out .= $tags->{$name.'='};
616 elsif (exists($tags->{$name})) {
617 $out .= $tags->{$name};
620 $out .= $tags->{'/?'};
621 $debug .= debug($printdebug, "[unknown!]");
624 $ind =~ s/\.([0-9]+)$//;
626 $debug .= debug($printdebug, "[<]");
637 my $value = $bbtree->{$ind.'.value'};
638 if($bbtree->{$ind.'.closed'} ne '') {
639 $debug .= debug($printdebug, "tag: [$name]");
641 if (exists($tags->{$name.'='}) && ($value ne '')) {
642 if (exists($tags->{$name.'='.$value})) {
645 $tags->{$name.'='.$value} .
651 ($escape ? html_entity_encode_dec($value, 1) : $value) .
655 elsif (exists($tags->{$name})) {
656 $out .= $tags->{$name};
659 $out .= $out.$tags->{'?'};
660 $debug .= debug($printdebug, "[unknown!]");
664 $debug .= debug($printdebug, "unclosed tag: [$name]");
665 my $text = $name . (($value ne '') ? ('='.$value) : '');
666 $out .= '['.($escape ? html_encode_line($text) : $text).']';
668 if ($bbtree->{$ind.'.count'} > 0) {
671 $debug .= debug($printdebug, "[>]");
680 $debug .= debug($printdebug, "unknown thing: ".$bbtree->{$ind.'.type'});
681 #should not occur with a correct bbtree
682 #unless unimplemented
683 $ind =~ s/\.([0-9]+)$//;
685 $debug .= debug($printdebug, "[<ui]");
694 if ($goto_next ne '') {
696 $ind =~ s/\.([0-9]+)$//;
698 if (($i < $bbtree->{$ind.'.count'}) and ($1 ne '')){
705 # should not occur with a correct bbtree
706 $debug .= debug($printdebug, "[<$goto_next]");
709 } while ($level >= 0);}
712 $debug .= debug($printdebug, "[>$level:$ind]\n");
715 $debug .= debug($printdebug, "-->\n");
716 return ($debug, $out);
721 (my $bb, my $printdebug) = @_;
726 ($debug, %bbtree) = bbtree($bb, $printdebug);
727 ($debug, $ht) = convtree ($printdebug, $debug, 'html', \%bbtree);
734 (my $bb, my $printdebug) = @_;
739 ($debug, %bbtree) = bbtree($bb, $printdebug);
740 ($debug, $ht) = convtree ($printdebug, $debug, 'bb', \%bbtree);
746 (my $bb, my $full_url, my $password) = @_;
751 my $base_url = $full_url ?
752 {'scheme' => SCHEME(), 'host' => WEBSITE()} :
755 while ($bb =~ m/###([^#;]*);/g) {
760 if ($value =~ /^att&([0-9]+)$/) {
763 {'path' => CGI_ATTACH_PATH()},
767 elsif ($value =~ /^vw&([0-9]+)$/) {
770 {'path' => CGI_VIEWER_PATH()},
774 elsif ($value =~ /^fr&([0-9]+)$/) {
777 {'path' => CGI_FRAME_PATH()},
784 if (($value ne '') && ($password ne '')) {
787 {'query' => {'p' => $password}}
790 $bb = $before . $value . $after;
796 sub html_encode_line {
797 (my $text, my $non_ascii, my $all) = @_;
801 $text =~ s/\r\n/\n/gs;
804 while ($text ne '') {
805 $ind = index($text, "\n");
807 $html .= html_entity_encode_dec(substr($text, 0, $ind), $non_ascii, $all)."<br>\n";
808 $text = substr($text, $ind+1);
812 $html .= html_entity_encode_dec($text, 1);
820 (my $print, my $text) = @_;
830 sub print_html_start {
832 print $fh '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "">'."\n";
833 print $fh '<html lang="en">'."\n";
838 print $fh '</html>'."\n";
841 sub print_html_head_start {
843 print $fh ' <head>'."\n";
844 print $fh ' <meta http-equiv="Content-type" content="text/html; charset=UTF-8">'."\n";
845 print $fh ' <link rel="icon" type="image/png" href="'.html_entity_encode_dec(FAVICON_PATH(),1).'">'."\n";
846 print $fh ' <link rel="stylesheet" href="'.html_entity_encode_dec(CGI_CSS_PATH(),1).'">'."\n";
849 sub print_html_head_end {
851 print $fh ' </head>'."\n";
854 sub print_html_body_start {
856 print $fh ' <body>'."\n";
857 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";
858 print $fh ' <div id="all">'."\n";
861 sub print_html_body_end {
862 (my $fh, my $hide_credits) = @_;
863 print $fh ' </div>'."\n";
864 unless ($hide_credits) {
865 print $fh ' <p>'."\n";
866 print $fh ' '.html_entity_encode_dec(STORY_CREDITS(),1).'<br>'."\n";
867 print $fh ' '.html_entity_encode_dec(INTF_CREDITS(),1).'<br>'."\n";
868 print $fh ' <a href="'.html_entity_encode_dec(SOURCE_URL(),1).'" class="cz">source code</a>'."\n";
869 print $fh ' </p>'."\n";
871 print $fh ' <a href="/" class="cz">'.html_entity_encode_dec(WEBSITE(),1).'</a>'."\n";
872 print $fh ' </body>'."\n";
875 sub print_html_data {
876 (my $fh, my $data) = @_;
878 foreach my $key (keys %$data) {
879 unless ($key eq 'content') {
880 my $val = $data->{$key};
881 $val =~ s/(\r)?\n/\n /gs; # does the space make sense in HTML anyway?
882 print $fh html_encode_line("$key: $val\n", 1);
885 print $fh html_encode_line("\n".$data->{'content'});
902 $last_frame = int($state->{'last'});
903 $ong_state = int($state->{'state'});
904 $password_query = url_query_encode({'p', $settings->{'password'}});
906 my $_title = html_entity_encode_dec($settings->{'story'}, 1);
907 my $_website_name = html_entity_encode_dec(WEBSITE_NAME() , 1);
908 my $_base_url = html_entity_encode_dec(CGI_PATH() , 1);
912 unless (seek($fh, 0, 0)) {
913 #don't actually fail here
917 unless (open_encoded($fh, ">:encoding(UTF-8)", $file)) {
922 print_html_start($fh);
923 print_html_head_start($fh);
925 print $fh ' <title>GOTO • '.$_title.' • '.$_website_name.'</title>'."\n";
927 print_html_head_end($fh);
928 print_html_body_start($fh);
930 print $fh ' <div id="inst" class="ins">'."\n";
932 print $fh ' <div id="title">'."\n";
933 print $fh ' <h1 id="titletext">'.$_title.'</h1>'."\n";
934 print $fh ' </div>'."\n";
936 print $fh ' </div>'."\n";
937 print $fh ' <div id="insb" class="ins">'."\n";
939 print $fh ' <div id="chat">'."\n";
941 for (my $frame = 0; ; $frame += 1) {
944 ($frame <= $last_frame) &&
945 ($ong_state >= STATE->{'waiting'})
958 $ongtime = $goto_list->{'ongtime-'.$frame};
959 $title = $goto_list->{'title-' .$frame};
960 if (($ongtime eq '') && ($title eq '')) {
961 my %frame_data = read_frame_data($frame);
962 $ongtime = $frame_data{'ongtime'};
963 $title = $frame_data{'title'};
964 unless (keys %frame_data) {
969 if ($ongtime ne '') {
970 @time_tab = gmtime($ongtime);
971 $time_text = sprintf(
972 '%02d.%02d.%02d %02d:%02d',
981 $time_text = (($frame <= $last_frame) && ($ong_state >= STATE->{'waiting'})) ?
982 'EE.EE.EE EE:EE' : '--.--.-- --:--';
987 $timer_color = (($frame > $last_frame) || ($ong_state < STATE->{'waiting'})) ?
989 (($frame == $last_frame) && ($ong_state < STATE->{'ready'})) ?
992 $frame_text = sprintf('%03d',$frame);
993 $viewer_url = merge_url(
994 {'path' => CGI_VIEWER_PATH()},
996 ); # TODO: consider static here?
998 $viewer_url = merge_url($viewer_url, {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1001 my $_viewer_url = html_entity_encode_dec($viewer_url, 1);
1002 my $_title = html_entity_encode_dec($title , 1);
1004 print $fh ' <span class="'.$timer_color.'">'.$frame_text.'</span> '.$time_text.' <a href="'.$_viewer_url.'">'.$_title.'</a><br>'."\n";
1006 print $fh ' </div>'."\n";
1008 print $fh ' <div id="underlinks">'."\n";
1009 print $fh ' <a href="'.$_base_url.'">BSTA</a>'."\n";
1010 print $fh ' </div>'."\n";
1012 print $fh ' </div>'."\n";
1014 print_html_body_end($fh, $ong_state == STATE->{'inactive'});
1015 print_html_end($fh);
1017 unless (ref($file)) {
1021 truncate ($fh , tell($fh));
1027 sub print_viewer_page {
1034 my $prev_frame_data,
1035 my $next_frame_data,
1040 my $launch = $context->{'launch'};
1041 my $access = $context->{'access'};
1042 my $password_ok = $context->{'password_ok'};
1043 my $static = $context->{'static'};
1045 my $goto = int($context->{'goto'});
1046 my $frame = int($context->{'frame'});
1047 my $text_mode = int($context->{'text_mode'});
1048 my $timer_unlocked = int($context->{'timer_unlocked'});
1049 my $timer = int($context->{'timer'});
1050 # my $words_page = int($context->{'words_page'});
1052 my $prev_frame = $frame - 1;
1053 my $next_frame = $frame + 1;
1055 my $story = $settings->{'story'};
1056 my $title = $frame_data->{'title'};
1057 my $command = ($frame_data->{'command'} ne '') ?
1058 $frame_data->{'command'} :
1059 $next_frame_data->{'title'};
1061 my $last_frame = int($state->{'last'});
1062 my $ong_state = int($state->{'state'});
1064 my $width = int($frame_data->{'width'});
1065 my $height = int($frame_data->{'height'});
1066 my $frame_type = $frame_data->{'frametype'};
1068 my $timer_color_h = (($timer_unlocked >= 1) || ($ong_state >= STATE->{'ready'})) ? 'br' : 'ni';
1069 my $timer_color_m = (($timer_unlocked >= 2) || ($ong_state >= STATE->{'ready'})) ? 'br' : 'ni';
1070 my $timer_color_s = (($timer_unlocked >= 3) || ($ong_state >= STATE->{'ready'})) ? 'br' : 'ni';
1077 (($timer >= 0) && ($frame == 0))
1079 $timer_s = sprintf('%02d', $timer % 60);
1080 $timer_h = int($timer / 60);
1081 $timer_m = sprintf('%02d', $timer_h % 60);
1082 $timer_h = sprintf('%02d', $timer_h / 60);
1084 elsif (($timer >= -15) && ($ong_state >= STATE->{'ready'})) {
1095 my $words_posts = int($words_data->{'posts'});
1096 my $words_link_text = 'Words'.(($words_posts > 0) ? "[$words_posts]" : '');
1098 my $prev_available = (($frame > 0) && $access);
1099 my $next_available = ($launch || $password_ok || ($next_frame <= $last_frame));
1100 my $prefetch_prev = (
1102 ($prev_frame < $last_frame) || ( # avoid unseen trigger!
1103 ($prev_frame <= $last_frame) &&
1104 ($ong_state >= STATE->{'ready'})
1107 my $prefetch_next = (
1109 ($next_frame < $last_frame) || ( # avoid unseen trigger!
1110 ($next_frame <= $last_frame) &&
1111 ($ong_state >= STATE->{'ready'})
1118 ($frame == $last_frame) && (
1119 ($ong_state == STATE->{'waiting'}) ||
1120 ($ong_state == STATE->{'ready'})
1124 my $show_command = (
1128 ($frame < $last_frame) || (
1129 ($ong_state >= STATE->{'ready'}) &&
1130 $context->{'show_command'}
1133 my $show_command_link = ($next_available || (!$access));
1134 my $show_command_cursor = ((!$next_available) || ($command eq ''));
1135 my $show_words = ($password_ok || ($access && !$launch));
1137 my $frame_indirect = !(
1139 ($frame <= $last_frame) &&
1140 ($ong_state > STATE->{'inactive'})
1143 my $prevframe_indirect = !($prev_frame <= $last_frame);
1144 my $nextframe_indirect = !($next_frame <= $last_frame);
1148 my $base_url = CGI_PATH();
1149 my $timer_url = CGI_TIMER_PATH();
1150 my $viewer_full_url = merge_url(
1151 {'scheme' => SCHEME(), 'host' => WEBSITE()},
1152 {'path' => CGI_VIEWER_PATH()},
1155 my $viewer_url = merge_url(
1156 {'path' => CGI_VIEWER_PATH()},
1159 my $viewer_0_url = merge_url(
1160 {'path' => CGI_VIEWER_PATH()},
1163 my $viewer_prev_url = merge_url(
1164 {'path' => CGI_VIEWER_PATH()},
1165 {'path' => $prev_frame}
1167 my $viewer_next_url = merge_url(
1168 {'path' => CGI_VIEWER_PATH()},
1169 {'path' => $next_frame}
1171 my $viewer_last_url = merge_url(
1172 {'path' => CGI_VIEWER_PATH()},
1173 {'path' => ($static ? -1 : $last_frame)}
1175 my $goto_url = ($goto) ?
1178 {'path' => $viewer_url},
1184 'fragment' => 'goto'
1188 unless ($password_ok) {
1190 $viewer_0_url = $base_url;
1191 if ($prev_frame == 0) {
1192 $viewer_prev_url = $viewer_0_url;
1195 $page_file = get_page_file($prev_frame, $prev_frame_data, $settings);
1196 if (_x_encoded('-f',
1197 join_path(PATH_SEPARATOR(), WWW_PATH() , $page_file)
1199 $viewer_prev_url = merge_url(
1200 {'path' => $base_url},
1201 {'path' => $page_file}
1205 if ($next_frame < $last_frame) {
1206 $page_file = get_page_file($next_frame, $next_frame_data, $settings);
1207 if (_x_encoded('-f',
1208 join_path(PATH_SEPARATOR(), WWW_PATH() , $page_file)
1210 $viewer_next_url = merge_url(
1211 {'path' => $base_url},
1212 {'path' => $page_file}
1218 (_x_encoded('-f',WWW_GOTO_PATH()))
1220 $goto_url = CGI_LIST_PATH();
1223 my $bbcode_url = ($text_mode == TEXT_MODE->{'bb'}) ?
1225 {'path' => CGI_BBCODE_PATH()},
1232 'b' => TEXT_MODE->{'bb'}
1237 my $info_url = ($text_mode == TEXT_MODE->{'info'}) ?
1239 {'path' => CGI_INFO_PATH()},
1246 'b' => TEXT_MODE->{'info'}
1251 my $words_url = merge_url (
1255 'b' => TEXT_MODE->{'words'}
1264 my $frame_normal_url;
1266 $frame_file = get_frame_file($frame, $frame_data, $settings);
1267 $frame_normal_url = merge_url(
1268 {'path' => CGI_PATH()},
1269 {'path' => $frame_file}
1271 $frame_url = $frame_indirect ?
1273 {'path' => CGI_FRAME_PATH()},
1277 $frame_full_url = merge_url(
1278 {'scheme' => SCHEME(), 'host' => WEBSITE()},
1279 {'path' => $frame_normal_url}
1281 if ($prevframe_indirect) {
1282 $frame_prev_url = merge_url(
1283 {'path' => CGI_FRAME_PATH()},
1284 {'path' => $prev_frame}
1288 $frame_prev_url = merge_url(
1289 {'path' => CGI_PATH()},
1290 {'path' => get_frame_file($prev_frame, $prev_frame_data, $settings)}
1293 if ($nextframe_indirect) {
1294 $frame_next_url = merge_url(
1295 {'path' => CGI_FRAME_PATH()},
1296 {'path' => $next_frame}
1300 $frame_next_url = merge_url(
1301 {'path' => CGI_PATH()},
1302 {'path' => get_frame_file($next_frame, $next_frame_data, $settings)}
1307 $password_query = url_query_encode({'p', $settings->{'password'}});
1308 $goto_url = merge_url($goto_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1309 $info_url = merge_url($info_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1310 $words_url = merge_url($words_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1311 $bbcode_url = merge_url($bbcode_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1312 $viewer_url = merge_url($viewer_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1313 $viewer_0_url = merge_url($viewer_0_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1314 $viewer_prev_url = merge_url($viewer_prev_url, {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1315 $viewer_next_url = merge_url($viewer_next_url, {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1316 $viewer_last_url = merge_url($viewer_last_url, {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1317 if ($frame_indirect) {
1318 $frame_url = merge_url($frame_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1320 if ($prevframe_indirect) {
1321 $frame_prev_url= merge_url($frame_prev_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1323 if ($nextframe_indirect) {
1324 $frame_next_url= merge_url($frame_next_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1327 my $_password = $password_ok ? html_entity_encode_dec($settings->{'password'}, 1) : '';
1328 my $_action_url = html_entity_encode_dec(CGI_VIEWER_PATH(), 1);
1329 my $_base_url = html_entity_encode_dec($base_url , 1);
1330 my $_goto_url = html_entity_encode_dec($goto_url , 1);
1331 my $_info_url = html_entity_encode_dec($info_url , 1);
1332 my $_words_url = html_entity_encode_dec($words_url , 1);
1333 my $_bbcode_url = html_entity_encode_dec($bbcode_url , 1);
1334 my $_timer_url = html_entity_encode_dec($timer_url , 1);
1335 my $_viewer_full_url = html_entity_encode_dec($viewer_full_url , 1);
1336 my $_viewer_url = html_entity_encode_dec($viewer_url , 1);
1337 my $_viewer_0_url = html_entity_encode_dec($viewer_0_url , 1);
1338 my $_viewer_prev_url = html_entity_encode_dec($viewer_prev_url , 1);
1339 my $_viewer_next_url = html_entity_encode_dec($viewer_next_url , 1);
1340 my $_viewer_last_url = html_entity_encode_dec($viewer_last_url , 1);
1341 my $_frame_url = html_entity_encode_dec($frame_url , 1);
1342 my $_frame_prev_url = html_entity_encode_dec($frame_prev_url , 1);
1343 my $_frame_next_url = html_entity_encode_dec($frame_next_url , 1);
1344 my $_frame_full_url = html_entity_encode_dec($frame_full_url , 1);
1346 my $_story = html_entity_encode_dec($story , 1);
1347 my $_title = html_entity_encode_dec($title , 1);
1348 my $_command = html_entity_encode_dec($command , 1);
1349 my $_frame_type = html_entity_encode_dec($frame_type, 1);
1351 my $_website_name = html_entity_encode_dec(WEBSITE_NAME(), 1);
1353 if ($text_mode == TEXT_MODE->{'info'}) {
1354 if ($show_command) {
1355 $frame_data->{'command'} = $command;
1358 $frame_data->{'frame'} = $frame_file;
1360 if ($frame_data->{'page'} eq '') {
1361 unless (($access) && ($frame < $last_frame)) {
1362 $frame_data->{'page'} = '';
1365 $frame_data->{'page'} = get_page_file($frame, $frame_data, $settings);
1370 # everything determined, now start generating
1374 unless (seek($fh, 0, 0)) {
1375 #don't actually fail here
1379 unless (open_encoded($fh, ">:encoding(UTF-8)", $file)) {
1384 print_html_start($fh);
1385 print_html_head_start($fh);
1387 print $fh ' <title>'.$_title;
1388 if ($story ne $title) {
1389 print $fh ' • '.$_story;
1391 print $fh ' • '.$_website_name.'</title>'."\n";
1392 print $fh ' <link rel="index" href="'.$_goto_url.'">'."\n";
1393 print $fh ' <link rel="start" href="'.$_viewer_0_url.'">'."\n";
1394 if ($prev_available) {
1395 print $fh ' <link rel="prev" href="'.$_viewer_prev_url.'">'."\n";
1396 if ($prefetch_prev) {
1397 print $fh ' <link rel="prefetch" href="'.$_viewer_prev_url.'">'."\n";
1398 print $fh ' <link rel="prefetch" href="'.$_frame_prev_url.'">'."\n";
1401 if ($next_available) {
1402 print $fh ' <link rel="next" href="'.$_viewer_next_url.'">'."\n";
1403 if ($prefetch_next) {
1404 print $fh ' <link rel="prefetch" href="'.$_viewer_next_url.'">'."\n";
1405 print $fh ' <link rel="prefetch" href="'.$_frame_next_url.'">'."\n";
1409 print $fh ' <script src="'.$_timer_url.'"></script>'."\n";
1412 print_html_head_end($fh);
1413 print_html_body_start($fh);
1415 print $fh ' <div id="inst" class="ins">'."\n";
1417 print $fh ' <div id="title">'."\n";
1418 print $fh ' <h1 id="titletext">'.$_title.'</h1>'."\n";
1419 print $fh ' </div>'."\n";
1421 print $fh ' </div>'."\n";
1422 print $fh ' <div id="framespace">'."\n";
1424 print $fh ' <img src="'.$_frame_url.'" id="frame" class="'.$_frame_type.'" alt="'.$frame.'" title="'.$_title.'" width="'.$width.'" height="'.$height.'">'."\n";
1426 print $fh ' </div>'."\n";
1427 print $fh ' <div id="insb" class="ins">'."\n";
1429 if ($text_mode == TEXT_MODE->{'info'}) {
1430 print $fh ' <div id="chat">'."\n";
1432 print_html_data($fh, $frame_data);
1434 print $fh ' </div>'."\n";
1436 elsif ($text_mode == TEXT_MODE->{'bb'}) {
1437 print $fh ' <div id="chat">'."\n";
1439 print $fh '[quote][center][size=200]'.$_title.'[/size]<br>'."\n";
1440 print $fh '[url='.$_viewer_full_url.'][img]'.$_frame_full_url.'[/img][/url][/center]<br>'."\n";
1441 print $fh html_encode_line(
1444 $frame_data->{'content'},
1449 print $fh '[/quote]'."\n";
1451 print $fh ' </div>'."\n";
1453 elsif ($frame_data->{'content'} ne '') {
1454 print $fh ' <div id="undertext">'."\n";
1455 print $fh bb_to_html(
1457 $frame_data->{'content'},
1459 $password_ok ? $settings->{'password'} : ''
1462 print $fh ' </div>'."\n";
1465 print $fh ' <div id="command">'."\n";
1468 print $fh ' <span id="timer">';
1469 print $fh '[<span id="ongh" class="hv '.$timer_color_h.'">'.$timer_h.'</span>';
1470 print $fh ':<span id="ongm" class="hv '.$timer_color_m.'">'.$timer_m.'</span>';
1471 print $fh ':<span id="ongs" class="hv '.$timer_color_s.'">'.$timer_s.'</span>]';
1472 print $fh '</span><br>'."\n";
1475 if ($show_command_link) {
1476 print $fh '<a href="'.($access ? $_viewer_next_url : $_viewer_last_url).'">';
1478 if ($show_command) {
1479 print $fh $_command;
1481 if ($show_command_cursor) {
1482 print $fh '<span class="inp">_</span>';
1484 if ($show_command_link) {
1488 print $fh " </div>\n";
1490 print $fh ' <div id="underlinks">'."\n ";
1492 unless (($frame == 0) && $static) {
1493 print $fh '<a href="'.$_base_url.'">Once again</a> | ';
1495 if ($prev_available) {
1496 print $fh '<a href="'.$_viewer_prev_url.'">Before</a> | ';
1498 unless ($frame == $last_frame) {
1499 print $fh '<a href="'.$_viewer_last_url.'">Now</a> | ';
1501 print $fh '<a href="'.$_goto_url.'">GOTO</a>'."\n";
1502 print $fh ' <span style="float: right;">'."\n ";
1504 ($text_mode == TEXT_MODE->{'normal'})
1508 print $fh '<a href="'.$_words_url.'">'.$words_link_text.'</a> | ';
1512 print $fh '<a href="'.$_viewer_url.'">Without</a> | ';
1514 print $fh '<a href="'.$_info_url.'">Info</a> | ';
1515 print $fh '<a href="'.$_bbcode_url.'">BB</a>';
1516 print $fh "\n </span>\n";
1518 print $fh " </div>\n";
1521 print $fh ' <div class="underlinks" id="goto">'."\n";
1522 print $fh ' <form class="goto" method="get" action="'.$_action_url.'">'."\n";
1523 print $fh ' GO TO:'."\n";
1524 print $fh ' <input class="intx" type="number" size="4" name="f"'.(
1526 ('value="'.$frame.'"') :
1529 print $fh ' <input class="inbt" type="submit" value="GO">'."\n";
1531 print $fh ' <input type="hidden" name="p" value="'.$_password.'">'."\n";
1533 print $fh ' <input type="hidden" name="g" value="2">'."\n";
1534 print $fh ' </form>'."\n";
1535 print $fh " </div>\n";
1538 print $fh " </div>\n";
1540 if (($text_mode == TEXT_MODE->{'words'}) && $show_words) {
1541 print_comments($fh, $context, $settings, $words_data);
1544 print_html_body_end($fh, $ong_state == STATE->{'inactive'});
1545 print_html_end($fh);
1548 unless (ref($file)) {
1552 truncate ($fh , tell($fh));
1558 sub print_comments {
1559 (my $fh, my $context, my $settings, my $words_data) = @_;
1561 my $password_ok = $context->{'password_ok'};
1562 my $frame = int($context->{'frame'});
1563 my $page = int($context->{'words_page'});
1564 my $post_count = int($words_data->{'posts'});
1565 my $id_start = $page * COMMENT_PAGE_LENGTH();
1566 my $id_stop = $id_start + COMMENT_PAGE_LENGTH();
1567 my $older = ($page > 0) ? ($page-1) : '';
1570 if ($id_stop >= $post_count) {
1571 $id_stop = $post_count;
1578 my $words_url = merge_url(
1579 {'path' => CGI_VIEWER_PATH()},
1582 'query' => {'b' => TEXT_MODE->{'words'}},
1585 my $older_url = merge_url(
1588 'query' => {'i' => $page-1},
1589 'fragment' => 'insw',
1593 my $newer_url = merge_url(
1596 'query' => {'i' => $page+1},
1597 'fragment' => 'insw',
1603 $password_query = url_query_encode({'p', $settings->{'password'}});
1604 $older_url = merge_url($older_url, {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1605 $newer_url = merge_url($older_url, {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1608 my $_password = $password_ok ? html_entity_encode_dec($settings->{'password'}, 1) : '';
1609 my $_post_url = html_entity_encode_dec(CGI_WORDS_PATH(), 1);
1610 my $_older_url = html_entity_encode_dec($older_url, 1);
1611 my $_newer_url = html_entity_encode_dec($newer_url, 1);
1613 if (($older ne '') || ($newer ne '')) {
1614 $links .= ' <div class="underlinks">'."\n";
1617 $links .= '<a href="'.$_older_url.'">Older</a>'
1619 if (($older ne '') && ($newer ne '')) {
1623 $links .= '<a href="'.$_newer_url.'">Newer</a>';
1626 $links .= ' </div>'."\n";
1629 print $fh ' <div class="space"></div>'."\n";
1630 print $fh ' <div id="insw" class="ins">'."\n";
1632 print $fh ' <div class="title" id="wordstitle">'."\n";
1633 print $fh ' <h1 class="titletext" id="wordstitletext">Words</h1>'."\n";
1634 print $fh ' </div>'."\n";
1640 print $fh ' <div class="undertext" id="words">'."\n";
1642 if ($post_count > 0) {
1643 for (my $i=$id_start; $i<$id_stop; ++$i) {
1644 my $ID = $words_data->{'content'}->[$i];
1645 my %post_data = read_words($ID);
1647 my $post_time = int($post_data{'posttime'});
1648 my $edit_time = int($post_data{'edittime'});
1653 if ($post_time != 0) {
1654 my @time_tab = gmtime($post_time);
1655 $post_time_text = sprintf(
1656 '%04d.%02d.%02d %02d:%02d:%02d UTC',
1665 if (($edit_time !=0) && ($edit_time != $post_time)) {
1666 my @time_tab = gmtime($edit_time);
1667 $edit_time_text = sprintf(
1668 '%04d.%02d.%02d %02d:%02d UTC',
1676 my $quote_url = merge_url(
1677 {'path' => CGI_WORDS_PATH()},
1682 'p' => ($password_ok ? $settings->{'password'} : '')
1686 my $edit_url = merge_url(
1687 {'path' => CGI_WORDS_PATH()},
1692 'key' => $post_data{'key'},
1693 'username' => $post_data{'name'},
1694 'p' => ($password_ok ? $settings->{'password'} : '')
1698 my $remove_url = merge_url(
1699 {'path' => CGI_WORDS_PATH()},
1704 'key' => $post_data{'key'},
1705 'username' => $post_data{'name'},
1706 'p' => ($password_ok ? $settings->{'password'} : '')
1711 my $_ID = html_entity_encode_dec($ID, 1);
1712 my $_name = html_entity_encode_dec($post_data{'name'}, 1);
1713 my $_quote_url = html_entity_encode_dec($quote_url, 1);
1714 my $_edit_url = html_entity_encode_dec($edit_url, 1);
1715 my $_remove_url = html_entity_encode_dec($remove_url, 1);
1717 print $fh ' <div id="'.$_ID.'"class="opomba">'."\n";
1718 print $fh ' <div class="opomba_info">'."\n";
1719 print $fh ' <a href="#'.$_ID.'" class="bi hu">'.$i.': '.$_name;
1720 if ($post_time_text ne '') {
1721 print $fh ' • '.$post_time_text;
1723 if ($edit_time_text ne '') {
1724 print $fh ' • '.$edit_time_text;
1726 print $fh '</a>'."\n";
1727 print $fh ' <div class="pr">'."\n";
1728 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";
1729 print $fh ' </div>'."\n";
1730 print $fh ' </div>'."\n";
1731 print $fh ' <div class="opomba_text">'."\n";
1732 print $fh bb_to_html(
1734 $post_data{'content'},
1736 $password_ok ? $settings->{'password'} : ''
1739 print $fh ' </div>'."\n";
1740 print $fh ' </div>'."\n";
1741 print $fh ' <br>'."\n";
1745 print $fh ' <form method="post" action="'.$_post_url.'">'."\n";
1746 print $fh ' <b>Your words:</b>'."\n";
1747 print $fh ' <textarea class="inta" name="words" rows="4"></textarea>'."\n";
1748 print $fh ' <table cellpadding="0" cellspacing="0" border="0"><tr>'."\n";
1749 print $fh ' <td><b>Your name: </b></td>'."\n";
1750 print $fh ' <td><input class="intx" type="text" name="username" value=""></td>'."\n";
1751 print $fh ' <td></td>'."\n";
1752 print $fh ' </tr><tr>'."\n";
1753 print $fh ' <td><b>Optional password: </b></td>'."\n";
1754 print $fh ' <td><input class="intx" type="password" name="password" value=""></td>'."\n";
1755 print $fh ' <td>(if you want to edit later)</td>'."\n";
1756 print $fh ' </tr><tr>'."\n";
1757 print $fh ' <td><b>Leave this empty: </b></td>'."\n";
1758 print $fh ' <td><input class="intx" type="text" name="password2" value=""></td>'."\n";
1759 print $fh ' <td>'."\n";
1760 print $fh ' <input class="inbt" type="submit" name="post" value="Send">'."\n";
1761 print $fh ' <input class="inbt" type="submit" name="preview" value="Preview">'."\n";
1762 print $fh ' </td>'."\n";
1763 print $fh ' </tr></table>'."\n";
1764 print $fh ' <input type="hidden" name="f" value="'.$frame.'">'."\n";
1766 print $fh ' <input type="hidden" name="p" value="'.$_password.'">'."\n";
1768 print $fh ' </form>'."\n";
1769 print $fh ' </div>'."\n";
1775 print $fh ' </div>'."\n";
1788 my $ong_state = int($state->{'state'});
1790 unless (open_encoded($fh, ">:encoding(UTF-8)", WWW_INDEX_PATH())) {
1794 # normal running story
1795 if ($ong_state > STATE->{'inactive'}) {
1796 my %default = read_default();
1797 my %frame_data = read_frame_data(0, \%default);
1798 my %next_frame_data= read_frame_data(1, \%default);
1799 my %words_data = read_words_list(
1804 $r = print_viewer_page(
1811 'timer_unlocked' => 3, # not relevant
1812 'timer' => 0, # not relevant
1814 'show_command' => 1,
1815 'text_mode' => TEXT_MODE->{'normal'},
1816 'words_page' => 0, # not relevant
1827 # no conditions met, pretend a normal Apache2 index
1828 elsif ($pass != 1) {
1829 my $index_of = CGI_PATH;
1830 $index_of =~ s/\/$//g;
1832 my $_index_of = html_entity_encode_dec($index_of , 1);
1833 my $_2words_date = html_entity_encode_dec(INTF_DATE(), 1);
1834 my $_coin_date = html_entity_encode_dec(COIN_DATE(), 1);
1835 my $_website = html_entity_encode_dec(WEBSITE() , 1);
1837 print_html_start ($fh);
1838 print $fh ' <head>'."\n";
1839 print $fh ' <meta http-equiv="Content-type" content="text/html; charset=UTF-8">'."\n";
1840 print $fh ' <title>Index of '.$_index_of.'</title>'."\n";
1841 print $fh ' </head>'."\n";
1842 print $fh ' <body>'."\n";
1843 print $fh ' <h1>Index of '.$_index_of.'</h1>'."\n";
1844 print $fh ' <table>'."\n";
1845 print $fh ' <tr>'."\n";
1846 print $fh ' <th><img src="/icons/blank.gif" alt="[ICO]"></th>'."\n";
1847 print $fh ' <th><a href="?C=N;O=D">Name</a></th>'."\n";
1848 print $fh ' <th><a href="?C=M;O=A">Last modified</a></th>'."\n";
1849 print $fh ' <th><a href="?C=S;O=A">Size</a></th>'."\n";
1850 print $fh ' <th><a href="?C=D;O=A">Description</a></th>'."\n";
1851 print $fh ' </tr><tr>'."\n";
1852 print $fh ' <th colspan="5"><hr></th>'."\n";
1853 print $fh ' </tr><tr>'."\n";
1854 print $fh ' <td valign="top"><img src="/icons/back.gif" alt="[DIR]"></td>'."\n";
1855 print $fh ' <td><a href="/">Parent Directory</a></td>'."\n";
1856 print $fh ' <td> </td>'."\n";
1857 print $fh ' <td align="right"> - </td>'."\n";
1858 print $fh ' <td> </td>'."\n";
1859 print $fh ' </tr><tr>'."\n";
1860 print $fh ' <td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td>'."\n";
1861 print $fh ' <td><a href="2words/">2words/</a></td>'."\n";
1862 print $fh ' <td align="right">'.$_2words_date.' </td>'."\n";
1863 print $fh ' <td align="right"> - </td><td> </td>'."\n";
1864 print $fh ' </tr><tr>'."\n";
1865 print $fh ' <td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td>'."\n";
1866 print $fh ' <td><a href="coin/">coin/</a></td>'."\n";
1867 print $fh ' <td align="right">'.$_coin_date.' </td>'."\n";
1868 print $fh ' <td align="right"> - </td><td> Coincidence </td>'."\n";
1869 print $fh ' </tr><tr>'."\n";
1870 print $fh ' <th colspan="5"><hr></th>'."\n";
1871 print $fh ' </tr>'."\n";
1872 print $fh ' </table>'."\n";
1873 print $fh ' <address>Apache/2.2.22 (Debian) Server at '.$_website.' Port 80</address>'."\n";
1874 print $fh ' </body>'."\n";
1875 print_html_end ($fh);
1879 my %default = read_default();
1880 my %frame_data = read_frame_data(0, \%default);
1881 my %next_frame_data= read_frame_data(1, \%default);
1882 my %coin_data = read_coincidence();
1884 if (($mode == INTF_STATE->{'>'}) && $pause) {
1885 $r = print_viewer_page(
1892 'timer_unlocked' => 3,
1895 'show_command' => 1,
1896 'text_mode' => TEXT_MODE->{'normal'},
1897 'words_page' => 0, # not relevant
1905 {'posts' => 0} # words_data
1910 my $index_of = CGI_PATH;
1911 $index_of =~ s/\/$//g;
1915 my $show_parent_dir = 0;
1917 my $show_folders = 0;
1919 my $timer_color = 'ni';
1920 if ($mode == INTF_STATE->{'>'}) {
1921 $title = $settings->{'story'}; # $frame_data{'title'} ?
1922 $frame_file = 'intf-tr.gif';
1926 elsif ($mode == INTF_STATE->{'<<'}) {
1927 $title = 'Index of';
1928 $frame_file = 'intf-ll.gif';
1929 $show_parent_dir = 1;
1932 $timer_color = 'br';
1934 elsif ($mode == INTF_STATE->{'>>'}) {
1935 $title = 'Index of';
1936 $frame_file = 'intf-pp.gif';
1937 $show_parent_dir = 1;
1943 $title = 'Index of '.$index_of;
1944 $frame_file = 'intf-kw.gif';
1945 $show_parent_dir = 1;
1948 my $frame_url = merge_url(
1949 {'path' => CGI_PATH()},
1950 {'path' => $frame_file}
1952 my $coin_server = $coin_data{'server'};
1954 my $_title = html_entity_encode_dec($title , 1);
1955 my $_website_name = html_entity_encode_dec(WEBSITE_NAME() , 1);
1956 my $_frame_url = html_entity_encode_dec($frame_url , 1);
1957 my $_undertext = html_entity_encode_dec($undertext , 1);
1958 my $_2words_date = html_entity_encode_dec(INTF_DATE() , 1);
1959 my $_coin_date = html_entity_encode_dec(COIN_DATE() , 1);
1960 my $_coin_server = html_entity_encode_dec($coin_server , 1);
1961 my $_2words_url = html_entity_encode_dec(CGI_2WORDS_PATH(), 1);
1962 my $_coin_url = html_entity_encode_dec(CGI_COIN_PATH() , 1);
1964 print_html_start($fh);
1965 print_html_head_start($fh);
1967 print $fh ' <title>'.$_title.' • '.$_website_name.'</title>'."\n";
1969 print_html_head_end($fh);
1970 print_html_body_start($fh);
1972 print $fh ' <div id="inst" class="ins">'."\n";
1974 print $fh ' <div id="title">'."\n";
1975 print $fh ' <h1 id="titletext">'.$_title.'</h1>'."\n";
1976 print $fh ' </div>'."\n";
1978 print $fh ' </div>'."\n";
1979 print $fh ' <div id="framespace">'."\n";
1981 print $fh ' <img src="'.$_frame_url.'" id="frame" alt="0">'."\n"; # title="'.$_title.'"
1983 print $fh ' </div>'."\n";
1984 print $fh ' <div id="insb" class="ins">'."\n";
1986 print $fh ' <div id="undertext">'."\n";
1988 if ($show_parent_dir) {
1989 print $fh ' <img src="/icons/back.gif" alt="[DIR]"> <a href="..">Parent Directory</a><br>'."\n";
1991 if ($show_folders) {
1992 print $fh ' <img src="/icons/folder.gif" alt="[DIR]"> <a href="'.$_2words_url.'">2words/</a> '.$_2words_date.' - <br>'."\n";
1993 print $fh ' <img src="/icons/folder.gif" alt="[DIR]"> <a href="'.$_coin_url.'">coin/</a> '.$_coin_date.' - '.$_coin_server."\n";
1996 print $fh ' <img src="/icons/folder.gif" alt="[DIR]"> <a href="'.$_2words_url.'">yyyyb/</a>'."\n";
1998 if ($undertext ne '') {
1999 print $fh ' '.$_undertext."\n";
2002 print $fh ' </div>'."\n";
2005 print $fh ' <div id="command">'."\n";
2007 print $fh ' [<span id="ongh" class="'.$timer_color.'">'.$timer.'</span>';
2008 print $fh ':<span id="ongm" class="'.$timer_color.'">'.$timer.'</span>';
2009 print $fh ':<span id="ongs" class="'.$timer_color.'">'.$timer.'</span>]<br>'."\n";
2011 if ($undertext ne '') {
2012 print $fh '><a href="'.$_2words_url.'">'.$_undertext.'</a><span class="inp">_</span>'."\n";
2014 print $fh " </div>\n";
2017 print $fh " </div>\n";
2019 print_html_body_end($fh, $ong_state == STATE->{'inactive'});
2020 print_html_end($fh);
2026 sub write_static_viewer_page {
2033 my $prev_frame_data_ref,
2034 my $next_frame_data_ref,
2042 my %prev_frame_data;
2043 my %next_frame_data;
2048 $frame = int($frame);
2049 my $prev_frame = $frame -1;
2050 my $next_frame = $frame +1;
2052 %state = (ref ($state_ref)) ?
2055 my $ong_state = int($state{'state'});
2056 my $last_frame = int($state{'last'});
2058 unless ($ong_state > STATE->{'inactive'}) {
2063 ($frame < $last_frame) || (
2064 ($frame <= $last_frame) &&
2065 ($ong_state >= STATE->{'end'})
2072 %settings = (ref ($settings_ref)) ?
2075 %default = (ref ($default_ref)) ?
2079 %frame_data = (ref ($frame_data_ref)) ?
2081 read_frame_data($frame);
2083 %prev_frame_data = (ref ($prev_frame_data_ref)) ?
2084 %$prev_frame_data_ref : (
2085 ($prev_frame >= 0) ?
2086 read_frame_data($prev_frame) :
2090 %next_frame_data = (ref ($next_frame_data_ref)) ?
2091 %$next_frame_data_ref :
2092 read_frame_data($next_frame);
2094 %words_data = (ref ($words_data_ref)) ?
2101 %frame_data = merge_settings(\%default, \%frame_data);
2102 %prev_frame_data = merge_settings(\%default, \%prev_frame_data);
2103 %next_frame_data = merge_settings(\%default, \%next_frame_data);
2105 $file = get_page_file($frame, \%frame_data, \%settings);
2106 $file = join_path(PATH_SEPARATOR(), WWW_PATH(), $file);
2108 return print_viewer_page(
2115 'timer_unlocked'=> 3, # not relevant
2116 'timer' => 0, # not relevant
2118 'show_command' => 1,
2119 'text_mode' => TEXT_MODE->{'normal'},
2120 'words_page' => 0, # not relevant
2132 sub write_static_goto {
2133 (my $state_ref, my $settings_ref, my $goto_ref) = @_;
2138 %state = (ref ($state_ref)) ?
2141 %settings = (ref ($settings_ref)) ?
2144 %goto_list = (ref ($goto_ref)) ?
2157 # ONG the frame + attachment & stuff. NOT update state file.
2160 my $ID, my $ongtime, my $timer, my $update, my $print,
2161 my $settings_ref, my $default_ref, my $data_ref, my $goto_ref
2167 my $frame_data_path;
2175 my %frame_full_data;
2178 if ($ongtime eq '') {
2185 elsif ($ID eq 'c') {
2217 %settings = (ref ($settings_ref)) ?
2220 %default = (ref ($default_ref)) ? %$default_ref : read_default();
2221 $frame_data_path = $cfrt ?
2222 DATA_NOACCESS_PATH() :
2223 join_path(PATH_SEPARATOR(), DATA_PATH(), $frame);
2224 %frame_data = (ref ($data_ref)) ?
2226 read_frame_data($frame_data_path);
2227 %frame_full_data = merge_settings(\%default, \%frame_data);
2228 @files = (get_frame_file($frame, \%frame_full_data, \%settings), );
2230 %goto_list = (ref ($goto_ref)) ?
2233 for (my $i=0; ;$i+=1) {
2234 my %file_data = read_attachment($i);
2235 if ($file_data{'frame'} eq '') {
2238 if (int($file_data{'frame'}) != $frame) {
2241 if ($file_data{'content'} ne '') {
2244 unshift @files, $file_data{'filename'};
2248 ($frame_full_data{'ongtime'} eq '')
2250 $frame_data {'ongtime'} = $ongtime;
2251 $frame_full_data{'ongtime'} = $ongtime;
2257 ($frame_full_data{'timer'} eq '')
2260 $frame_data{'timer'} = int($timer);
2264 $r = write_frame_data($frame_data_path, \%frame_data);
2266 print STDERR "fail writing $frame_data_path\n";
2268 print "write frame data fail\n";
2273 $goto_list{'title-' .$frame} = $frame_full_data{'title'};
2274 $goto_list{'ongtime-'.$frame} = $frame_full_data{'ongtime'};
2275 $r = write_goto('', \%goto_list);
2277 print STDERR "fail writing ".DATA_LIST_PATH()."\n";
2279 print "write GOTO list fail\n";
2285 foreach my $file (@files) {
2286 $in_path = join_path(PATH_SEPARATOR(), DATA_PATH(), $file);
2287 $out_path = join_path(PATH_SEPARATOR(), WWW_PATH() , $file);
2289 print $in_path.' -> '.$out_path;
2291 $r = copy_encoded($in_path, $out_path);
2293 print (($r) ? " OK\n" : " FAIL\n");
2296 print STDERR "fail copy $in_path $out_path\n";
2305 sub get_frame_file {
2306 (my $frame, my $frame_data, my $settings) = @_;
2310 if ($frame_data->{'frame'} ne '') {
2311 $file = $frame_data->{'frame'};
2314 $pattern = validate_filename($settings->{'frame'}, '%d.%ext');
2317 int($frame), $frame_data->{'ext'}
2320 return validate_filename($file);
2324 (my $frame, my $frame_data, my $settings) = @_;
2331 if ($frame_data->{'page'} ne '') {
2332 $file = $frame_data->{'page'};
2335 $pattern = validate_filename($settings->{'frame'}, '%d.%ext');
2341 return validate_filename($file);
2344 sub validate_filename {
2345 (my $filename, my $fallback) = @_;
2346 if ($fallback eq '') {
2352 if ($filename =~ /^\./) {
2355 if (index($filename, PATH_SEPARATOR()) >= 0) {
2361 sub validate_frame_data {
2363 my %data = %$data_in;
2365 if ($data{'ongtime'} ne '') {
2366 $data{'ongtime'} = int($data{'ongtime'});
2368 if ($data{'timer'} ne '') {
2369 $data{'timer'} = int($data{'timer'});
2371 if ($data{'width'} ne '') {
2372 $data{'width'} = int($data{'width'});
2374 if ($data{'height'} ne '') {
2375 $data{'height'} = int($data{'height'});
2377 if ($data{'page'} ne '') {
2378 $data{'page'} = validate_filename($data{'page'});
2380 if ($data{'frame'} ne '') {
2381 $data{'frame'} = validate_filename($data{'frame'});
2387 sub validate_settings {
2389 my %data = %$data_in;
2391 if ($data{'ongtime'} ne '') {
2392 $data{'ongtime'} = int($data{'ongtime'});
2394 if ($data{'dynamicongtime'} ne '') {
2395 $data{'dynamicongtime'} = int($data{'dynamicongtime'});
2397 if ($data{'firstongtime'} ne '') {
2398 $data{'firstongtime'} = int($data{'firstongtime'});
2400 if ($data{'last'} ne '') {
2401 $data{'last'} = int($data{'last'});
2403 $data{'frame'} = validate_filename($data{'frame'}, '%d.%s');
2408 sub validate_state {
2410 my %data = %$data_in;
2412 if ($data{'state'} ne '') {
2413 $data{'state'} = int($data{'state'});
2415 if ($data{'last'} ne '') {
2416 $data{'last'} = int($data{'last'});
2418 if ($data{'nextong'} ne '') {
2419 $data{'nextong'} = int($data{'nextong'});
2425 sub validate_words_list {
2426 (my $data_in, my $not_list) = @_;
2427 my %data = %$data_in;
2429 if ($data{'ongtime'} ne '') {
2430 $data{'ongtime'} = int($data{'ongtime'});
2435 foreach my $ID (split(/\r?\n/, $data{'content'})) {
2436 $ID = validate_filename($ID);
2438 $id_list .= $ID."\n";
2441 $data{'content'} = $id_list;
2445 foreach my $ID (@{$data{'content'}}) {
2447 $ID = validate_filename($ID);
2452 $data{'content'} = [@id_list];
2458 sub validate_words {
2460 my %data = %$data_in;
2462 if ($data{'posttime'} ne '') {
2463 $data{'posttime'} = int($data{'posttime'});
2465 if ($data{'edittime'} ne '') {
2466 $data{'edittime'} = int($data{'edittime'});
2472 sub validate_story {
2474 my %data = %$data_in;
2476 if ($data{'id'} ne '') {
2477 $data{'id'} = int($data{'id'});
2479 if ($data{'pass'} ne '') {
2480 $data{'pass'} = int($data{'pass'});
2482 if ($data{'state'} ne '') {
2483 $data{'state'} = int($data{'state'});
2491 my %data = %$data_in;
2493 foreach my $key (keys %data) {
2494 if ($key =~ /^ongtime-([0-9]+)$/) {
2495 my $new_key = 'ongtime-'.int($1);
2496 $data{$new_key} = int($data{$key});
2497 if ($new_key != $key) {
2506 sub validate_attachment {
2508 my %data = %$data_in;
2510 if ($data{'frame'} ne '') {
2511 $data{'frame'} = int($data{'frame'});
2513 $data{'filename'} = validate_filename($data{'filename'});
2518 sub validate_coincidence {
2520 my %data = %$data_in;
2522 if ($data{'server'} ne '') {
2523 $data{'server'} = int($data{'server'});
2529 sub read_frame_data {
2530 (my $f, my $default) = @_;
2534 if (ref ($f)) { # already open file
2537 elsif ($f =~ /^[0-9]+$/) { # frame ID
2538 $file = join_path(PATH_SEPARATOR(), DATA_PATH(), int($&));
2540 elsif ($f =~ /^(c(frt)?)|(noaccess)$/) { # CFRT (no access)
2541 $file = DATA_NOACCESS_PATH();
2543 elsif ($f =~ /^d(efault)?$/) { # default
2544 $file = DATA_DEFAULT_PATH();
2546 elsif ($f ne '') { # path
2550 $file = DATA_DEFAULT_PATH();
2553 %data = read_data_file($file);
2554 if (ref ($default)) {
2555 %data = merge_settings($default, \%data);
2557 elsif ($default ne '') {
2558 my %default_data = read_data_file(DATA_DEFAULT_PATH());
2559 %data = merge_settings(\%default_data, \%data);
2562 return validate_frame_data(\%data);
2565 sub write_frame_data {
2566 (my $f, my $data) = @_;
2569 if (ref ($f)) { # already open file
2572 elsif ($f =~ /^[0-9]+$/) { # frame ID
2573 $file = join_path(PATH_SEPARATOR(), DATA_PATH(), int($&));
2575 elsif ($f =~ /^(c(frt)?)|(noaccess)$/) { # CFRT (no access)
2576 return 0; # forbidden
2578 elsif ($f =~ /^d(efault)?$/) { # default
2579 return 0; # forbidden
2581 elsif ($f ne '') { # path
2585 return 0; # forbidden
2588 my %_data = validate_frame_data($data);
2590 return write_data_file($file, \%_data);
2594 return read_frame_data('default');
2599 return read_frame_data('noaccess', $default);
2607 if (ref ($f)) { # already open file
2610 elsif ($f ne '') { # path
2614 $file = DATA_SETTINGS_PATH();
2617 %data = read_data_file($file);
2619 return validate_settings(\%data);
2627 if (ref ($f)) { # already open file
2630 elsif ($f ne '') { # path
2634 $file = DATA_STATE_PATH();
2637 %data = read_data_file($file);
2639 return validate_state(\%data);
2643 (my $f, my $data) = @_;
2646 if (ref ($f)) { # already open file
2649 elsif ($f ne '') { # path
2653 $file = PERL_DATA_STATE_PATH();
2656 my %_data = validate_state($data);
2658 return write_data_file($file, \%_data);
2661 sub read_words_list {
2662 (my $f, my $header_only, my $not_list) = @_;
2666 if (ref ($f)) { # already open file
2669 elsif ($f =~ /^[0-9]+$/) { # frame ID
2670 $file = join_path(PATH_SEPARATOR(), DATA_WORDS_PATH(), int($&));
2672 elsif ($f ne '') { # path
2675 else { # which frame ???
2676 return ('posts' => 0);
2679 %data = read_data_file(
2684 not $not_list # as list
2687 return validate_words_list(\%data, $not_list);
2690 sub write_words_list {
2691 (my $f, my $data) = @_;
2694 if (ref ($f)) { # already open file
2697 elsif ($f =~ /^[0-9]+$/) { # frame ID
2698 $file = join_path(PATH_SEPARATOR(), DATA_WORDS_PATH(), int($&));
2700 elsif ($f ne '') { # path
2703 else { # which frame ???
2707 my %_data = validate_words_list($data);
2709 return write_data_file(
2720 (my $f, my $default) = @_;
2724 if (ref ($f)) { # already open file
2727 elsif ($f =~ /^[0-9]+\.[0-9\.]+$/) { # post ID
2728 $file = join_path(PATH_SEPARATOR(), DATA_WORDS_PATH(), $&);
2730 elsif ($f ne '') { # path
2733 else { # which post ???
2737 %data = read_data_file($file);
2739 return validate_words(\%data);
2743 (my $f, my $data) = @_;
2746 if (ref ($f)) { # already open file
2749 elsif ($f =~ /^[0-9\.]+$/) { # post ID
2750 $file = join_path(PATH_SEPARATOR(), DATA_WORDS_PATH(), $&);
2752 elsif ($f ne '') { # path
2755 else { # which post ???
2759 my %_data = validate_words($data);
2761 return write_data_file($file, \%_data);
2769 if (ref ($f)) { # already open file
2772 elsif ($f =~ /^[0-9]+$/) { # story ID
2773 $file = DATA_STORY_PATH().int($&);
2775 elsif ($f ne '') { # path
2779 $file = DATA_STORY_PATH();
2782 %data = read_data_file($file);
2784 return validate_story(\%data);
2788 (my $f, my $data) = @_;
2791 if (ref ($f)) { # already open file
2794 elsif ($f =~ /^[0-9]+$/) { # story ID
2795 $file = DATA_STORY_PATH().int($&);
2797 elsif ($f ne '') { # path
2801 $file = DATA_STORY_PATH();
2804 my %_data = validate_story($data);
2806 return write_data_file($file, \%_data);
2814 if (ref ($f)) { # already open file
2817 elsif ($f ne '') { # path
2821 $file = DATA_LIST_PATH();
2824 %data = read_data_file($file);
2826 return validate_goto(\%data);
2830 (my $f, my $data) = @_;
2833 if (ref ($f)) { # already open file
2836 elsif ($f ne '') { # path
2840 $file = DATA_LIST_PATH();
2843 my %_data = validate_goto($data);
2845 return write_data_file($file, \%_data);
2848 sub read_attachment {
2849 (my $f, my $default) = @_;
2853 if (ref ($f)) { # already open file
2856 elsif ($f =~ /^[0-9]+$/) { # attachment ID
2857 $file = DATA_ATTACH_PATH().int($&);
2859 elsif ($f ne '') { # path
2866 %data = read_data_file($file);
2868 return validate_attachment(\%data);
2871 sub read_coincidence {
2876 if (ref ($f)) { # already open file
2879 elsif ($f ne '') { # path
2883 $file = DATA_COIN_PATH();
2886 %data = read_data_file($file);
2888 return validate_coincidence(\%data);
2896 if (ref ($f)) { # already open file
2899 elsif ($f =~ /^[0-9]+$/) { # chat ID
2900 $file = DATA_CHAT_PATH().int($&);
2902 elsif ($f ne '') { # path
2906 $file = DATA_CHAT_PATH();
2909 return read_data_file($file);
2915 (my $f, my $data) = @_;
2918 if (ref ($f)) { # already open file
2921 elsif ($f =~ /^[0-9]+$/) { # chat ID
2922 $file = DATA_CHAT_PATH().int($&);
2924 elsif ($f ne '') { # path
2928 $file = DATA_CHAT_PATH();
2933 return write_data_file($file, $data);