]> bicyclesonthemoon.info Git - ott/bsta/blob - bsta_lib.1.pm
update static page after comment
[ott/bsta] / bsta_lib.1.pm
1 # bsta_lib.pm is generated from bsta_lib.1.pm
2
3 # Library of functions
4 #
5 # Copyright (C) 2016, 2017, 2019, 2020, 2022, 2023, 2024  Balthasar Szczepański
6 #
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.
11 #
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.
16 #
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/>.
19
20 # TODO: FQ NBSP ?
21 # TODO: DEBUG
22 # TODO: timer JS
23 # TODO: BB & INFO indent
24
25 package bsta_lib;
26
27 use strict;
28 #use warnings
29
30 use utf8;
31 use Encode ('encode', 'decode');
32 use Exporter;
33 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
34
35 ###PERL_EXPORT_VERSION: our $VERSION = 'x.x.x';
36 our @ISA         = qw(Exporter);
37 our @EXPORT      = ();
38 our @EXPORT_OK   = (
39         'STATE', 'TEXT_MODE', 'INTF_STATE', 'CHAT_STATE', 'CHAT_ACTION',
40         'failpage',
41         'fail_method', 'fail_content_type', 'fail_open_file', 'fail_attachment', 'fail_500',
42         'redirect',
43         'get_remote_addr', 'get_id', 'get_frame', 'get_password',
44         'merge_settings',
45         'print_html_start', 'print_html_end',
46         'print_html_head_start', 'print_html_head_end',
47         'print_html_body_start', 'print_html_body_end',
48         'print_viewer_page',
49         'write_index', 'write_static_viewer_page', 'write_static_goto',
50         'ong',
51         'eval_bb', 'bb_to_bbcode', 'bb_to_html'
52 );
53
54 ###PERL_LIB: use lib /botm/lib/bsta
55 use botm_common (
56         'HTTP_STATUS',
57         'url_query_decode', 'url_query_encode',
58         'url_decode', 'url_encode',
59         'html_entity_encode_dec',
60         'merge_url',
61         'read_header_env',
62         'read_data_file', 'write_data_file',
63         'join_path',
64         'copy_encoded', 'open_encoded', '_x_encoded',
65         'http_header_line', 'http_status',
66         'http_header_status', 'http_header_allow', 'http_header_location'
67 );
68
69 ###PERL_PATH_SEPARATOR:     PATH_SEPARATOR     = /
70
71 ###PERL_CGI_PATH:           CGI_PATH           = /bsta/
72 ###PERL_CGI_ATTACH_PATH:    CGI_ATTACH_PATH    = /bsta/a
73 ###PERL_CGI_2WORDS_PATH:    CGI_2WORDS_PATH    = /bsta/2words
74 ###PERL_CGI_BBCODE_PATH:    CGI_BBCODE_PATH    = /bsta/b
75 ###PERL_CGI_COIN_PATH:      CGI_COIN_PATH      = /bsta/coin
76 ###PERL_CGI_CSS_PATH:       CGI_CSS_PATH       = /bsta/bsta.css
77 ###PERL_CGI_FRAME_PATH:     CGI_FRAME_PATH     = /bsta/f
78 ###PERL_CGI_GOTO_PATH:      CGI_GOTO_PATH      = /bsta/g
79 ###PERL_CGI_INFO_PATH:      CGI_INFO_PATH      = /bsta/i
80 ###PERL_CGI_LOGO_PATH:      CGI_LOGO_PATH      = /bsta/botmlogo.png
81 ###PERL_CGI_TIMER_PATH:     CGI_TIMER_PATH     = /bsta/timer.js
82 ###PERL_CGI_VIEWER_PATH:    CGI_VIEWER_PATH    = /bsta/v
83 ###PERL_CGI_WORDS_PATH:     CGI_WORDS_PATH     = /bsta/w
84
85 ###PERL_DATA_PATH:          DATA_PATH          = /botm/data/bsta/
86 ###PERL_DATA_ATTACH_PATH:   DATA_ATTACH_PATH   = /botm/data/bsta/a
87 ###PERL_DATA_COIN_PATH:     DATA_COIN_PATH     = /botm/data/bsta/coincidence
88 ###PERL_DATA_DEFAULT_PATH:  DATA_DEFAULT_PATH  = /botm/data/bsta/default
89 ###PERL_DATA_LIST_PATH:     DATA_LIST_PATH     = /botm/data/bsta/list
90 ###PERL_DATA_NOACCESS_PATH: DATA_NOACCESS_PATH = /botm/data/bsta/noaccess
91 ###PERL_DATA_STATE_PATH:    DATA_STATE_PATH    = /botm/data/bsta/state
92 ###PERL_DATA_WORDS_PATH:    DATA_WORDS_PATH    = /botm/data/bsta/words/
93
94 ###PERL_WWW_PATH:           WWW_PATH           = /botm/www/
95 ###PERL_WWW_INDEX_PATH:     WWW_INDEX_PATH     = /botm/www/1190/bsta/index.htm
96
97 ###PERL_SCHEME:             SCHEME             = http
98 ###PERL_WEBSITE:            WEBSITE            = 1190.bicyclesonthemoon.info
99 ###PERL_WEBSITE_NAME:       WEBSITE_NAME       = Bicycles on the Moon
100 ###PERL_FAVICON_PATH:       FAVICON_PATH       = /img/favicon.png
101
102 ###PERL_COIN_DATE:          COIN_DATE          = 13-Nov-2016 22:15
103 ###PERL_INTF_DATE:          INTF_DATE          = 28-Sep-2016 20:34
104
105 ###PERL_STORY_CREDITS:      STORY_CREDITS      = "BSTA" by Balthasar Szczepański
106 ###PERL_INTF_CREDITS:       INTF_CREDITS       = Online interface © Balthasar Szczepański; AGPL 3 license
107 ###PERL_SOURCE_URL:         SOURCE_URL         = http://bicyclesonthemoon.info/git-projects/?p=ott/bsta
108
109 ###PERL_COMMENT_PAGE_LENGTH:COMMENT_PAGE_LENGTH= 16
110
111 use constant STATE => {
112         'inactive' => 0,
113         'waiting'  => 1,
114         'ready'    => 2,
115         'end'      => 3,
116 };
117 use constant INTF_STATE => {
118         'X'  => 0b000000,
119         'x'  => 0b000000,
120         '||' => 0b000001,
121         '>>' => 0b000100,
122         '>>|'=> 0b000101,
123         '<<' => 0b001000,
124         '|<<'=> 0b001001,
125         '>'  => 0b010000,
126         '>|' => 0b010001,
127         'mask'=>0b111111,
128         'mode'=>0b111110,
129 };
130 use constant TEXT_MODE => {
131         'normal' => 0,
132         'bb'     => 1,
133         'info'   => 2,
134         'words'  => 3
135 };
136 use constant CHAT_STATE => {
137         'disconnected' => 0,
138         'ready'        => 1,
139         'active'       => 2,
140 };
141 use constant CHAT_ACTION => {
142         'none'   => 0,
143         'join'   => 1,
144         'leave'  => 2,
145         'nopost' => 3,
146         'file'   => 4,
147 };
148
149 use constant tags_bbcode => {
150         'ht'     => '',
151         '/ht'    => '',
152         'fq'     => '[quote]',
153         '/fq'    => '[/quote]',
154         'tq'     => '[quote]',
155         '/tq'    => '[/quote]',
156         'quote'  => '[quote]',
157         'quote=' => '[quote="',
158         'quote/='=> '"]',
159         '/quote' => '[/quote]',
160         'ni'     => '[color=#0057AF]',
161         '/ni'    => '[/color]',
162         'br'     => '[color=#BB6622]',
163         '/br'    => '[/color]',
164         'po'     => '[color=#FF8800]',
165         '/po'    => '[/color]',
166         'url'    => '[url]',
167         'url='   => '[url=',
168         'url/='  => ']',
169         '/url'   => '[/url]',
170         'i'      => '[i]',
171         '/i'     => '[/i]',
172         'list'   => '[list]',
173         'list='  => '[list=',
174         'list/=' => ']',
175         '/list'  => '[/list]',
176         '*'      => '[*]',
177         '/*'     => '[/*]',
178         '?'      => '[unknown!]',
179         '/?'     => '[/unknown!]',
180 };
181 use constant tags_html => {
182         'ht'     => '',
183         '/ht'    => '',
184         'fq'     => '<div class="fq">',
185         '/fq'    => '</div>',
186         'tq'     => '<div class="tq">',
187         '/tq'    => '</div>',
188         'quote'  => '<div class="opomba"><div class="opomba_text">',
189         'quote=' => '<div class="opomba"><div class="opomba_info"><b>',
190         'quote/='=> '</b> wrote:</div><div class="opomba_text">',
191         '/quote' => '</div></div>',
192         'ni'     => '<span class="ni">',
193         '/ni'    => '</span>',
194         'br'     => '<span class="br">',
195         '/br'    => '</span>',
196         'po'     => '<span class="po">',
197         '/po'    => '</span>',
198         'url'    => '<a href="#">',#think: how to add selfincluding?
199         'url='   => '<a href="',
200         'url/='  => '">',
201         '/url'   => '</a>',
202         'i'      => '<i>',
203         '/i'     => '</i>',
204         'list'   => '<ul>',
205         'list='  => '<ol style="list-style-type: ',
206         'list=1' => 'decimal',
207         'list=A' => 'upper-alpha',
208         'list=a' => 'lower-alpha',
209         'list=I' => 'upper-roman',
210         'list=i' => 'lower-roman',
211         'list/=' => '">',
212         '/list'  => '</ul>',
213         '/list=' => '</ol>',
214         '*'      => '<li>',
215         '/*'     => '</li>',
216         '?'      => '[unknown!]',
217         '/?'     => '[/unknown!]',
218 };
219
220
221 # Function to return an error page
222 # arguments: 1 - header fields, 2 - page title, 3 - error message, 4 method
223 sub failpage {
224         (my $header, my $title, my $message, my $method, my $hyperlink) = @_;
225         
226         if (ref($header)) {
227                 foreach my $header_name (keys %$header) {
228                         print http_header_line($header_name, $header->{$header_name});
229                 }
230         }
231         elsif($header ne '') {
232                 print $header;
233         }
234         if($method eq 'HEAD') {
235                 print "\n";
236                 return;
237         }
238         my $_title     = html_entity_encode_dec($title    , 1);
239         my $_message   = html_entity_encode_dec($message  , 1);
240         my $_hyperlink = html_entity_encode_dec($hyperlink, 1);
241         
242         print "Content-type: text/html; charset=UTF-8\n\n";
243         
244         print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "">'."\n";
245         print ' <html lang="en">'."\n";
246         print '  <head>'."\n";
247         print '   <meta http-equiv="Content-type" content="text/html; charset=UTF-8">'."\n";
248         if ($title ne '') {
249                 print '   <title>'.$_title.'</title>'."\n";
250         }
251         print '  </head>'."\n";
252         print ' <body>'."\n";
253         if ($title ne '') {
254                 print '  <h1>'.$_title.'</h1>'."\n";
255         }
256         if (($message ne '') || ($hyperlink ne '')) {
257                 print "  <p>\n";
258                 if ($message ne '') {
259                         print '   '.$_message.($hyperlink ne '' ? '<br>' : '')."\n";
260                 }
261                 if ($hyperlink ne '') {
262                         print '   <a href="'.$_hyperlink.'">'.$_hyperlink."</a>\n";
263                 }
264                 print "  </p>\n";
265         }
266         print ' </body>'."\n";
267         print '</html>'."\n";
268 }
269
270 sub fail_method {
271         (my $method, my $allowed) = @_;
272         
273         my $status = http_status(HTTP_STATUS->{'method_not_allowed'});
274         my $header =
275                 http_header_line('status', $status) .
276                 http_header_allow($allowed);
277         
278         return failpage(
279                 $header,
280                 $status,
281                 "The interface does not support the $method method.",
282                 $method
283         );
284 }
285
286 sub fail_content_type
287 {
288         (my $method, my $content_type) = @_;
289         
290         my $status = http_status(HTTP_STATUS->{'unsupported_media_type'});
291         my $header = http_header_line('status', $status);
292         
293         return failpage(
294                 $header,
295                 $status,
296                 "Unsupported Content-type: $content_type.",
297                 $method
298         );
299 }
300
301 sub fail_open_file
302 {
303         (my $method, my $type, my $path) = @_;
304         
305         my $status = http_status(HTTP_STATUS->{'not_found'});
306         my $header = http_header_line('status', $status);
307         
308         return failpage(
309                 $header,
310                 $status,
311                         "Can't open ".
312                         ($type ne '' ? $type : 'file').
313                         ($path ne '' ? ': "'.$path.'"' : '').
314                         '.',
315                 $method
316         );
317 }
318
319 sub fail_attachment
320 {
321         (my $method, my $ID) = @_;
322         
323         my $status = http_status(HTTP_STATUS->{'not_found'});
324         my $header = http_header_line('status', $status);
325
326         return failpage(
327                 $header,
328                 $status,
329                 "Attachment $ID not found.",
330                 $method
331         );
332 }
333
334 sub fail_500
335 {
336         (my $method, my $text) = @_;
337         
338         my $status = http_status(HTTP_STATUS->{'internal_server_error'});
339         my $header = http_header_line('status', $status);
340         
341         return failpage(
342                 $header,
343                 $status,
344                 $text,
345                 $method
346         );
347 }
348
349 sub redirect
350 {
351         (my $method, my $uri, my $code) = @_;
352         my $header;
353         my $status;
354         if ($code eq '') {
355                 $code = HTTP_STATUS->{'found'};
356         }
357         # 301 Moved Permanently
358         # 302 Found
359         # 303 See Other
360         # 307 Temporary Redirect
361         # 308 Permanent Redirect
362         $status = http_status($code);
363         $header = http_header_line('status', $status);
364         $header .= http_header_location($uri);
365         
366         return failpage(
367                 $header,
368                 $status,
369                 '',
370                 $method,
371                 $uri
372         );
373 }
374
375
376 # function to obtain address of remote agent
377 sub get_remote_addr {
378         if ($ENV{'HTTP_X_FORWARDED_FOR'} =~ /^.+$/) {
379                 return $&;
380         }
381         elsif ($ENV{'REMOTE_ADDR'} =~ /^.+$/) {
382                 return $&;
383         }
384         else {
385                 return '0.0.0.0';
386         }
387 }
388
389 # functions to get ID/number etc.
390 sub get_id {
391         (my $cgi, my $default, my $cgi_name) = @_;
392                 if ($default eq '') {
393                 $default = 0;
394         }
395         if ($cgi_name eq '') {
396                 $cgi_name = 'i';
397         }
398         
399         if ($cgi->{$cgi_name} =~ /^.+$/) {
400                 return int($&);
401         }
402         elsif ($ENV{'PATH_INFO'} =~ /^\/(.+)$/) {
403                 return int($1);
404         }
405         else {
406                 return int($default);
407         }
408 }
409
410 # function to obtain frame number
411 sub get_frame {
412         (my $cgi, my $default) = @_;
413         return get_id($cgi, $default, 'f');
414 }
415
416 # function to obtain password
417 sub get_password {
418         (my $cgi) = @_;
419         
420         if ($cgi->{'p'} =~ /^.+$/) {
421                 return $&;
422         }
423         else {
424                 return '';
425         }
426 }
427
428
429 sub merge_settings {
430                 my %final_settings;
431         
432         foreach my $settings (@_) {
433                 foreach my $ind (keys %$settings) {
434                         $final_settings{$ind} = $settings->{$ind};
435                 }
436         }
437         return %final_settings;
438 }
439
440
441 # BB code stuff
442 # different & simpler implementation than in post library
443 # to consider:
444 # a BBcode library?
445
446 #analyse bbcode text to build tag tree
447 #TODO make [/*] optional!
448 sub bbtree {
449         (my $bb, my $printdebug) = @_;
450         my %bbtree;
451         my $ind;
452         my $tag;
453         my $tag_name;
454         my $tag_value;
455         my $tag_end;
456         my $level=0;
457         my $pre_text;
458         my $debug;
459         
460         $ind="_";
461         $level=0;
462         $bbtree{"_.name" }  = "ht";
463         $bbtree{"_.value" } = '';
464         $bbtree{"_.type"  } = "tag";
465         $bbtree{"_.count" } = 0;
466         $bbtree{"_.closed"} = 0;
467         $debug .= debug($printdebug,
468                 "\n".
469                 "<!--GENERATING BBCODE TREE:\n".
470                 '[_]automatic tag: [ht]'."\n"
471         );
472         
473         while ($bb ne '') {
474                 my $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
475                 
476                 if($bb =~ m/\[(\/?)([A-Za-z]+|\*)(=([^\[\]]*))?\]/g) {
477                         $pre_text = $`;
478                         $tag = $&;
479                         $tag_end = $1;
480                         $tag_name = lc($2);
481                         $tag_value = $4;
482                         $bb = $';
483                         if ($tag_value =~ /^"(.*)"$/) {
484                                 $tag_value = $1;
485                         }
486                         
487                         if ($pre_text ne '') {
488                                 $debug .= debug($printdebug, "[$new_ind]text: $pre_text\n");
489                                 $bbtree{$new_ind.'.type' } = 'text';
490                                 $bbtree{$new_ind.'.value'} = $pre_text;
491                                 $bbtree{    $ind.'.count'}+= 1;
492                                 $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
493                         }
494                         
495                         if($tag_name =~ /^(fq|tq|quote|br|ni|po|url|i|list|\*)$/) {
496                                 if ($tag_end ne '') {
497                                         if (
498                                                 ($tag_name ne $bbtree{$ind.'.name'}) ||
499                                                 ($level <= 0)
500                                         ) {
501                                                 $debug .= debug($printdebug, "[$new_ind]text: $tag\n");
502                                                 $bbtree{$new_ind.'.type' } = 'text';
503                                                 $bbtree{$new_ind.'.value'} = $tag;
504                                                 $bbtree{    $ind.'.count'}+= 1;
505                                                 # $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
506                                         }
507                                         else {
508                                                 $debug .= debug($printdebug, "[$new_ind]tag: $tag\n");
509                                                 $bbtree{$new_ind.'.type'  } = 'tag';
510                                                 $bbtree{$new_ind.'.name'  } = '/'.$tag_name;
511                                                 $bbtree{$new_ind.'.value' } = $tag_value;
512                                                 $bbtree{    $ind.'.count' }+= 1;
513                                                 $bbtree{    $ind.'.closed'} = 1;
514                                                 $level -= 1;
515                                                 $ind =~ s/\.[0-9]+$//;
516                                                 # $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
517                                         }
518                                 }
519                                 else
520                                 {
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{$new_ind.'.count' } = 0;
526                                         $bbtree{$new_ind.'.closed'} = 0;
527                                         $bbtree{    $ind.'.count' }+= 1;
528                                         $level += 1;
529                                         $ind = $new_ind;
530                                         # $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
531                                 }
532                         }
533                         else {
534                                 $debug .= debug($printdebug, "[$new_ind]text: $tag\n");
535                                 $bbtree{$new_ind.'.type' } = 'text';
536                                 $bbtree{$new_ind.'.value'} = $tag;
537                                 $bbtree{    $ind.'.count'}+= 1;
538                                 # $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
539                         }
540                 }
541                 else {
542                         $debug .= debug($printdebug, "[$new_ind]text: $bb\n");
543                         $bbtree{$new_ind.'.type' } = 'text';
544                         $bbtree{$new_ind.'.value'} = $bb;
545                         $bbtree{    $ind.'.count'}+= 1;
546                         # $new_ind = $ind.'.'.$bbtree{$ind.'.count'};
547                         $bb = '';
548                 }
549         }
550         my $final_ind = '_.'.$bbtree{"_.count"};
551         $debug .= debug($printdebug, "[$final_ind]automatic tag: [/ht]\n -->\n");
552         $bbtree{$final_ind.'.type' } = "tag";
553         $bbtree{$final_ind.'.name' } = '/ht';
554         $bbtree{         '_.count' }+= 1;
555         $bbtree{         '_.closed'} = 1;
556         
557         return ($debug, %bbtree);
558 }
559
560 #convert tag tree to final text
561 sub convtree {
562         (my $printdebug, my $debug, my $lang, my $bbtree) = @_;
563         my $out;
564         my $ind;
565         my $indd;
566         my $level = 0;
567         my $tags = ($lang eq 'html') ? tags_html : tags_bbcode;
568         my $escape = ($lang eq 'html');
569         
570         # $debug .= debug($printdebug, "\n****\n");
571         # foreach my $iiii (keys %tags) {
572                 # $debug .= debug($printdebug, $iiii.'='.$tags->{$iiii}."\n");
573         # }
574         # $debug .= debug($printdebug, "****\n");
575         
576         $level = 0;
577         $ind = '_';
578         $out = '';
579         $debug .= debug($printdebug, "\n<!--PROCESSING BBCODE TREE:\n");
580         
581         while ($level >= 0) {
582                 my $goto_next = '';
583                 $debug .= debug($printdebug, "[$level:$ind:".int($bbtree->{$ind.'.count'})."]");
584                 #normal text
585                 if ($bbtree->{$ind.'.type'} eq 'text') {
586                         my $text = $bbtree->{$ind.'.value'};
587                         $debug .= debug($printdebug, "text: ".$text);
588                         $out .= $escape ? html_encode_line($text) : $text;
589                         
590                         $goto_next = 'tx';
591                 }
592                 #tag
593                 elsif ($bbtree->{$ind.'.type'} eq 'tag') {
594                         my $name = $bbtree->{$ind.'.name'};
595                         #endtag
596                         if ($name =~ /^\//) {
597                                 $debug .= debug($printdebug, "tag: [$name]");
598                                 $indd = $ind;
599                                 $indd =~ s/\.([0-9]+)$//;
600                                 if (exists($tags->{$name.'='}) && ($bbtree->{$indd.'.value'} ne '')) {
601                                         $out .= $tags->{$name.'='};
602                                 }
603                                 elsif (exists($tags->{$name})) {
604                                         $out .= $tags->{$name};
605                                 }
606                                 else {
607                                         $out .= $tags->{'/?'};
608                                         $debug .= debug($printdebug, "[unknown!]");
609                                 }
610                                 
611                                 $ind =~ s/\.([0-9]+)$//;
612                                 $level -= 1;
613                                 $debug .= debug($printdebug, "[<]");
614                                 if ($level > 0) {
615                                         $goto_next = 'nd';
616                                 }
617                                 else {
618                                         # time to end this
619                                         $level = -1;
620                                 }
621                         }
622                         #starttag
623                         else {
624                                 my $value = $bbtree->{$ind.'.value'};
625                                 if($bbtree->{$ind.'.closed'} ne '') {
626                                         $debug .= debug($printdebug, "tag: [$name]");
627                                         
628                                         if (exists($tags->{$name.'='}) && ($value ne '')) {
629                                                 if (exists($tags->{$name.'='.$value})) {
630                                                         $out .=
631                                                                 $tags->{$name.'='} .
632                                                                 $tags->{$name.'='.$value} .
633                                                                 $tags->{$name.'/='};
634                                                 }
635                                                 else {
636                                                         $out .=
637                                                                 $tags->{$name.'='} .
638                                                                 ($escape ? html_entity_encode_dec($value, 1) : $value) .
639                                                                 $tags->{$name.'/='};
640                                                 }
641                                         }
642                                         elsif (exists($tags->{$name})) {
643                                                 $out .= $tags->{$name};
644                                         }
645                                         else {
646                                                 $out .= $out.$tags->{'?'};
647                                                 $debug .= debug($printdebug, "[unknown!]");
648                                         }
649                                 }
650                                 else {
651                                         $debug .= debug($printdebug, "unclosed tag: [$name]");
652                                         my $text = $name . (($value ne '') ? ('='.$value) : '');
653                                         $out .= '['.($escape ? html_encode_line($text) : $text).']';
654                                 }
655                                 if ($bbtree->{$ind.'.count'} > 0) {
656                                         $ind = $ind.'.0';
657                                         $level += 1;
658                                         $debug .= debug($printdebug, "[>]");
659                                 }
660                                 else {
661                                         $goto_next = 'st';
662                                 }
663                         }
664                 }
665                 # what is this
666                 else {
667                         $debug .= debug($printdebug, "unknown thing: ".$bbtree->{$ind.'.type'});
668                         #should not occur with a correct bbtree
669                         #unless unimplemented
670                         $ind =~ s/\.([0-9]+)$//;
671                         $level -= 1;
672                         $debug .= debug($printdebug, "[<ui]");
673                         if ($level > 0) {
674                                 $goto_next = 'un';
675                         }
676                         else {
677                                 # time to end this
678                                 $level = -1;
679                         }
680                 }
681                 if ($goto_next ne '') {
682                         {do{
683                                 $ind =~ s/\.([0-9]+)$//;
684                                 my $i = int($1) + 1;
685                                 if (($i < $bbtree->{$ind.'.count'}) and ($1 ne '')){
686                                         # goto next
687                                         $ind = $ind.'.'.$i;
688                                         last;
689                                 }
690                                 else {
691                                         # step out
692                                         # should not occur with a correct bbtree
693                                         $debug .= debug($printdebug, "[<$goto_next]");
694                                         $level -= 1;
695                                 }
696                         } while ($level >= 0);}
697                 }
698                 
699                 $debug .= debug($printdebug, "[>$level:$ind]\n");
700         }
701         
702         $debug .= debug($printdebug, "-->\n");
703         return ($debug, $out);
704 }
705
706 #bbcode to html, TBD
707 sub bb_to_html {
708         (my $bb, my $printdebug) = @_;
709         my $ht;
710         my %bbtree;
711         my $debug;
712         
713         ($debug, %bbtree) = bbtree($bb, $printdebug);
714         ($debug, $ht) = convtree ($printdebug, $debug, 'html', \%bbtree);
715         
716         return $ht;
717 }
718
719 #bbcode to bb, TBD
720 sub bb_to_bbcode {
721         (my $bb, my $printdebug) = @_;
722         my $ht;
723         my %bbtree;
724         my $debug;
725         
726         ($debug, %bbtree) = bbtree($bb, $printdebug);
727         ($debug, $ht) = convtree ($printdebug, $debug, 'bb', \%bbtree);
728         
729         return $ht;
730 }
731
732 sub eval_bb {
733         (my $bb, my $full_url, my $password) = @_;
734         my $value;
735         my $before;
736         my $after;
737         
738         my $base_url = $full_url ?
739                 {'scheme' => SCHEME(), 'host' => WEBSITE()} :
740                 {'path' => ''};
741         
742         while ($bb =~ m/###([^#;]*);/g) {
743                 $value = $1;
744                 $before = $`;
745                 $after = $';
746                 
747                 if ($value =~ /^att&([0-9]+)$/) {
748                         $value = merge_url(
749                                 $base_url,
750                                 {'path' => CGI_ATTACH_PATH()},
751                                 {'path' => int($1)}
752                         )
753                 }
754                 elsif ($value =~ /^vw&([0-9]+)$/) {
755                         $value = merge_url(
756                                 $base_url,
757                                 {'path' => CGI_VIEWER_PATH()},
758                                 {'path' => int($1)}
759                         )
760                 }
761                 elsif ($value =~ /^fr&([0-9]+)$/) {
762                         $value = merge_url(
763                                 $base_url,
764                                 {'path' => CGI_FRAME_PATH()},
765                                 {'path' => int($1)}
766                         )
767                 }
768                 else {
769                         $value = '';
770                 }
771                 if (($value ne '') && ($password ne '')) {
772                         $value = merge_url(
773                                 $value,
774                                 {'query' => {'p' => $password}}
775                         );
776                 }
777                 $bb = $before . $value . $after;
778         }
779         return $bb;
780 }
781
782
783 sub html_encode_line {
784         (my $text, my $non_ascii, my $all) = @_;
785         my $html;
786         my $ind;
787         
788         $text =~ s/\r\n/\n/gs;
789         $text =~ s/\r/\n/gs;
790         
791         while ($text ne '') {
792                 $ind = index($text, "\n");
793                 if ($ind >= 0) {
794                         $html .= html_entity_encode_dec(substr($text, 0, $ind), $non_ascii, $all)."<br>\n";
795                         $text = substr($text, $ind+1);
796                 }
797                 else
798                 {
799                         $html .= html_entity_encode_dec($text, 1);
800                         $text = '';
801                 }
802         }
803         return $html;
804 }
805
806 sub debug {
807         (my $print, my $text) = @_;
808         
809         if ($print) {
810                 print $text;
811         }
812         
813         return $text;
814 }
815
816
817 sub print_html_start {
818         (my $fh) = @_;
819         print $fh '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "">'."\n";
820         print $fh '<html lang="en">'."\n";
821 }
822
823 sub print_html_end {
824         (my $fh) = @_;
825         print $fh '</html>'."\n";
826 }
827
828 sub print_html_head_start {
829         (my $fh) = @_;
830         print $fh ' <head>'."\n";
831         print $fh '  <meta http-equiv="Content-type" content="text/html; charset=UTF-8">'."\n";
832         print $fh '  <link rel="icon" type="image/png" href="'.html_entity_encode_dec(FAVICON_PATH(),1).'">'."\n";
833         print $fh '  <link rel="stylesheet" href="'.html_entity_encode_dec(CGI_CSS_PATH(),1).'">'."\n";
834 }
835
836 sub print_html_head_end {
837         (my $fh) = @_;
838         print $fh ' </head>'."\n";
839 }
840         
841 sub print_html_body_start {
842         (my $fh) = @_;
843         print $fh ' <body>'."\n";
844         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";
845         print $fh '  <div id="all">'."\n";
846 }
847
848 sub print_html_body_end {
849         (my $fh, my $hide_credits) = @_;
850         print $fh '  </div>'."\n";
851         unless ($hide_credits) {
852                 print $fh '  <p>'."\n";
853                 print $fh '   '.html_entity_encode_dec(STORY_CREDITS(),1).'<br>'."\n";
854                 print $fh '   '.html_entity_encode_dec(INTF_CREDITS(),1).'<br>'."\n";
855                 print $fh '   <a href="'.html_entity_encode_dec(SOURCE_URL(),1).'" class="cz">source code</a>'."\n";
856                 print $fh '  </p>'."\n";
857         }
858         print $fh '  <a href="/" class="cz">'.html_entity_encode_dec(WEBSITE(),1).'</a>'."\n";
859         print $fh ' </body>'."\n";
860 }
861
862 sub print_html_data {
863         (my $fh, my $data) = @_;
864         
865         foreach my $key (keys %$data) {
866                 unless ($key eq 'content') {
867                         my $val = $data->{$key};
868                         $val =~ s/(\r)?\n/\n /gs; # does the space make sense in HTML anyway?
869                         print $fh html_encode_line("$key: $val\n", 1);
870                 }
871         }
872         print $fh html_encode_line("\n".$data->{'content'});
873 }
874
875 sub print_viewer_page {
876         (
877                 my $file,
878                 my $context,
879                 my $state,
880                 my $settings,
881                 my $frame_data,
882                 my $prev_frame_data,
883                 my $next_frame_data,
884                 my $words_data,
885         ) = @_;
886         my $fh;
887         
888         my $launch      = $context->{'launch'};
889         my $access      = $context->{'access'};
890         my $password_ok = $context->{'password_ok'};
891         my $static      = $context->{'static'};
892         
893         my $frame          = int($context->{'frame'});
894         my $text_mode      = int($context->{'text_mode'});
895         my $timer_unlocked = int($context->{'timer_unlocked'});
896         my $timer          = int($context->{'timer'});
897         # my $words_page     = int($context->{'words_page'});
898         
899         my $prev_frame = $frame - 1;
900         my $next_frame = $frame + 1;
901         
902         my $story = $settings->{'story'};
903         my $title = $frame_data->{'title'};
904         my $command = ($frame_data->{'command'} ne '') ?
905                 $frame_data->{'command'} :
906                 $next_frame_data->{'title'};
907         
908         my $last_frame = int($state->{'last'});
909         my $ong_state  = int($state->{'state'});
910         
911         my $width  = int($frame_data->{'width'});
912         my $height = int($frame_data->{'height'});
913         my $frame_type = $frame_data->{'frametype'};
914         
915         my $timer_color_h = (($timer_unlocked >= 1) || ($ong_state >= STATE->{'ready'})) ? 'br' : 'ni';
916         my $timer_color_m = (($timer_unlocked >= 2) || ($ong_state >= STATE->{'ready'})) ? 'br' : 'ni';
917         my $timer_color_s = (($timer_unlocked >= 3) || ($ong_state >= STATE->{'ready'})) ? 'br' : 'ni';
918         
919         my $timer_h;
920         my $timer_m;
921         my $timer_s;
922         if (
923                 ($timer > 0) ||
924                 (($timer >= 0) && ($frame == 0))
925         ) {
926                 $timer_s = sprintf('%02d', $timer % 60);
927                 $timer_h = int($timer / 60);
928                 $timer_m = sprintf('%02d', $timer_h % 60);
929                 $timer_h = sprintf('%02d', $timer_h / 60);
930         }
931         elsif (($timer >= -15) && ($ong_state >= STATE->{'ready'})) {
932                 $timer_h = '00';
933                 $timer_m = '00';
934                 $timer_s = 'NG';
935         }
936         else {
937                 $timer_h = 'EE';
938                 $timer_m = 'EE';
939                 $timer_s = 'EE';
940         }
941         
942         my $words_posts = int($words_data->{'posts'});
943         my $words_link_text = 'Words'.(($words_posts > 0) ? "[$words_posts]" : '');
944         
945         my $prev_available = (($frame > 0) && $access);
946         my $next_available = ($launch || $password_ok || ($next_frame <= $last_frame));
947         my $prefetch_prev = (
948                 $password_ok ||
949                 ($prev_frame < $last_frame) || (  # avoid unseen trigger!
950                         ($prev_frame <= $last_frame) &&
951                         ($ong_state >= STATE->{'ready'})
952                 )
953         );
954         my $prefetch_next  = (
955                 $password_ok ||
956                 ($next_frame < $last_frame) || (  # avoid unseen trigger!
957                         ($next_frame <= $last_frame) &&
958                         ($ong_state >= STATE->{'ready'})
959                 )
960         );
961         my $show_timer = (
962                 (
963                         $access && $launch
964                 ) || (
965                         ($frame == $last_frame) && (
966                                 ($ong_state == STATE->{'waiting'}) ||
967                                 ($ong_state == STATE->{'ready'})
968                         )
969                 )
970         );
971         my $show_command = (
972                 $launch ||
973                 $password_ok ||
974                 (!$access) ||
975                 ($frame < $last_frame) || (
976                         ($ong_state >= STATE->{'ready'}) &&
977                         $context->{'show_command'}
978                 )
979         );
980         my $show_command_link = ($next_available || (!$access));
981         my $show_command_cursor = ((!$next_available) || ($command eq ''));
982         my $show_words = ($password_ok || ($access && !$launch));
983         
984         my $frame_indirect = !(
985                 (!$access) || (
986                         ($frame <= $last_frame) &&
987                         ($ong_state > STATE->{'inactive'})
988                 )
989         );
990         my $prevframe_indirect = !($prev_frame <= $last_frame);
991         my $nextframe_indirect = !($next_frame <= $last_frame);
992         
993         my $password_query;
994         
995         my $base_url   = CGI_PATH();
996         my $goto_url   = CGI_GOTO_PATH();
997         my $timer_url  = CGI_TIMER_PATH();
998         my $viewer_full_url = merge_url(
999                 {'scheme' => SCHEME(), 'host' => WEBSITE()},
1000                 {'path' => CGI_VIEWER_PATH()},
1001                 {'path' => $frame}
1002         );
1003         my $viewer_url = merge_url(
1004                 {'path' => CGI_VIEWER_PATH()},
1005                 {'path' => $frame}
1006         );
1007         my $viewer_0_url = merge_url(
1008                 {'path' => CGI_VIEWER_PATH()},
1009                 {'path' => 0}
1010         );
1011         my $viewer_prev_url = merge_url(
1012                 {'path' => CGI_VIEWER_PATH()},
1013                 {'path' => $prev_frame}
1014         );
1015         my $viewer_next_url = merge_url(
1016                 {'path' => CGI_VIEWER_PATH()},
1017                 {'path' => $next_frame}
1018         );
1019         my $viewer_last_url = merge_url(
1020                 {'path' => CGI_VIEWER_PATH()},
1021                 {'path' => ($static ? -1 : $last_frame)}
1022         );
1023         unless ($password_ok) {
1024                 my $page_file;
1025                 $viewer_0_url = $base_url;
1026                 if ($prev_frame == 0) {
1027                         $viewer_prev_url = $viewer_0_url;
1028                 }
1029                 else {
1030                         if ($prev_frame_data->{'page'} ne '') {
1031                                 $page_file = $prev_frame_data->{'page'};
1032                         }
1033                         else {
1034                                 $page_file = sprintf(
1035                                         $settings->{'frame'},
1036                                         $prev_frame, 'htm'
1037                                 );
1038                         }
1039                         if (_x_encoded('-f',
1040                                 join_path(PATH_SEPARATOR(), WWW_PATH() , $page_file)
1041                         )) {
1042                                 $viewer_prev_url = merge_url(
1043                                         {'path' => $base_url},
1044                                         {'path' => $page_file}
1045                                 );
1046                         }
1047                 }
1048                 if ($next_frame < $last_frame) {
1049                         if ($next_frame_data->{'page'} ne '') {
1050                                 $page_file = $next_frame_data->{'page'};
1051                         }
1052                         else {
1053                                 $page_file = sprintf(
1054                                         $settings->{'frame'},
1055                                         $next_frame, 'htm'
1056                                 );
1057                         }
1058                         if (_x_encoded('-f',
1059                                 join_path(PATH_SEPARATOR(), WWW_PATH() , $page_file)
1060                         )) {
1061                                 $viewer_next_url = merge_url(
1062                                         {'path' => $base_url},
1063                                         {'path' => $page_file}
1064                                 );
1065                         }
1066                 }
1067         }
1068         my $bbcode_url = ($text_mode == TEXT_MODE->{'bb'}) ?
1069                 merge_url(
1070                         {'path' => CGI_BBCODE_PATH()},
1071                         {'path' => $frame}
1072                 ) :
1073                 merge_url (
1074                         $viewer_url,
1075                         {
1076                                 'query'=>{
1077                                 'b' => TEXT_MODE->{'bb'}
1078                                 },
1079                                 'fragment'=>'insb'
1080                         }
1081                 );
1082         my $info_url = ($text_mode == TEXT_MODE->{'info'}) ?
1083                 merge_url(
1084                         {'path' => CGI_INFO_PATH()},
1085                         {'path' => $frame}
1086                 ) :
1087                 merge_url (
1088                         $viewer_url,
1089                         {
1090                                 'query'=>{
1091                                 'b' => TEXT_MODE->{'info'}
1092                                 },
1093                                 'fragment'=>'insb'
1094                         }
1095                 );
1096         my $words_url = merge_url (
1097                 $viewer_url,
1098                 {
1099                         'query'=>{
1100                         'b' => TEXT_MODE->{'words'}
1101                         },
1102                         'fragment'=>'insw'
1103                 }
1104         );
1105         my $frame_file;
1106         my $frame_url;
1107         my $frame_prev_url;
1108         my $frame_next_url;
1109         my $frame_normal_url;
1110         my $frame_full_url;
1111         if ($frame_data->{'frame'} ne '') {
1112                 $frame_file = $frame_data->{'frame'};
1113         }
1114         else {
1115                 $frame_file = sprintf(
1116                         $settings->{'frame'},
1117                         $frame, $frame_data->{'ext'}
1118                 );
1119         }
1120         $frame_normal_url = merge_url(
1121                         {'path' => CGI_PATH()},
1122                         {'path' => $frame_file}
1123                 );
1124         $frame_url = $frame_indirect ?
1125                 merge_url(
1126                         {'path' => CGI_FRAME_PATH()},
1127                         {'path' => $frame}
1128                 ) :
1129                 $frame_normal_url;
1130         $frame_full_url = merge_url(
1131                 {'scheme' => SCHEME(), 'host' => WEBSITE()},
1132                 {'path' => $frame_normal_url}
1133         );
1134         if ($prevframe_indirect) {
1135                 $frame_prev_url = merge_url(
1136                         {'path' => CGI_FRAME_PATH()},
1137                         {'path' => $prev_frame}
1138                 );
1139         }
1140         elsif ($prev_frame_data->{'frame'} ne '') {
1141                 $frame_prev_url = merge_url(
1142                         {'path' => CGI_PATH()},
1143                         {'path' => $prev_frame_data->{'frame'}}
1144                 );
1145         }
1146         else {
1147                 $frame_prev_url = merge_url(CGI_PATH(), sprintf(
1148                         $settings->{'frame'}, $prev_frame, $prev_frame_data->{'ext'}
1149                 ));
1150         }
1151         if ($nextframe_indirect) {
1152                 $frame_next_url = merge_url(
1153                         {'path' => CGI_FRAME_PATH()},
1154                         {'path' => $next_frame}
1155                 );
1156         }
1157         elsif ($next_frame_data->{'frame'} ne '') {
1158                 $frame_next_url = merge_url(
1159                         {'path' => CGI_PATH()},
1160                         {'path' => $next_frame_data->{'frame'}}
1161                 );
1162         }
1163         else {
1164                 $frame_next_url = merge_url(CGI_PATH(), sprintf(
1165                         $settings->{'frame'}, $next_frame, $next_frame_data->{'ext'}
1166                 ));
1167         }
1168         
1169         if ($password_ok) {
1170                 $password_query = url_query_encode({'p', $settings->{'password'}});
1171                 $goto_url        = merge_url($goto_url       , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1172                 $info_url        = merge_url($info_url       , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1173                 $words_url       = merge_url($words_url      , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1174                 $bbcode_url      = merge_url($bbcode_url     , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1175                 $viewer_url      = merge_url($viewer_url     , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1176                 $viewer_0_url    = merge_url($viewer_0_url   , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1177                 $viewer_prev_url = merge_url($viewer_prev_url, {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1178                 $viewer_next_url = merge_url($viewer_next_url, {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1179                 $viewer_last_url = merge_url($viewer_last_url, {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1180                 if ($frame_indirect) {
1181                         $frame_url     = merge_url($frame_url      , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1182                 }
1183                 if ($prevframe_indirect) {
1184                         $frame_prev_url= merge_url($frame_prev_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1185                 }
1186                 if ($nextframe_indirect) {
1187                         $frame_next_url= merge_url($frame_next_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1188                 }
1189         }
1190         my $_base_url        = html_entity_encode_dec($base_url       , 1);
1191         my $_goto_url        = html_entity_encode_dec($goto_url       , 1);
1192         my $_info_url        = html_entity_encode_dec($info_url       , 1);
1193         my $_words_url       = html_entity_encode_dec($words_url      , 1);
1194         my $_bbcode_url      = html_entity_encode_dec($bbcode_url     , 1);
1195         my $_timer_url       = html_entity_encode_dec($timer_url      , 1);
1196         my $_viewer_full_url = html_entity_encode_dec($viewer_full_url, 1);
1197         my $_viewer_url      = html_entity_encode_dec($viewer_url     , 1);
1198         my $_viewer_0_url    = html_entity_encode_dec($viewer_0_url   , 1);
1199         my $_viewer_prev_url = html_entity_encode_dec($viewer_prev_url, 1);
1200         my $_viewer_next_url = html_entity_encode_dec($viewer_next_url, 1);
1201         my $_viewer_last_url = html_entity_encode_dec($viewer_last_url, 1);
1202         my $_frame_url       = html_entity_encode_dec($frame_url      , 1);
1203         my $_frame_prev_url  = html_entity_encode_dec($frame_prev_url , 1);
1204         my $_frame_next_url  = html_entity_encode_dec($frame_next_url , 1);
1205         my $_frame_full_url  = html_entity_encode_dec($frame_full_url , 1);
1206         
1207         my $_story      = html_entity_encode_dec($story     , 1);
1208         my $_title      = html_entity_encode_dec($title     , 1);
1209         my $_command    = html_entity_encode_dec($command   , 1);
1210         my $_frame_type = html_entity_encode_dec($frame_type, 1);
1211         
1212         my $_website_name = html_entity_encode_dec(WEBSITE_NAME(), 1);
1213         
1214         if ($text_mode == TEXT_MODE->{'info'}) {
1215                 if ($show_command) {
1216                         $frame_data->{'command'} = $command;
1217                 }
1218                 if ($context->{'access'}) {
1219                         $frame_data->{'frame'} = $frame_file;
1220                 }
1221         }
1222         
1223         # everything determined, now start generating
1224         
1225         if (ref($file)) {
1226                 $fh=$file;
1227                 unless (seek($fh, 0, 0)) {
1228                         #don't actually fail here
1229                 }
1230         }
1231         else {
1232                 unless (open_encoded($fh, ">:encoding(UTF-8)", $file)) {
1233                         return 0;
1234                 }
1235         }
1236         
1237         print_html_start($fh);
1238         print_html_head_start($fh);
1239         
1240         print $fh '  <title>'.$_title;
1241         if ($story ne $title) {
1242                 print $fh ' &bull; '.$_story;
1243         }
1244         print $fh ' &bull; '.$_website_name.'</title>'."\n";
1245         print $fh '  <link rel="index" href="'.$_goto_url.'">'."\n";
1246         print $fh '  <link rel="start" href="'.$_viewer_0_url.'">'."\n";
1247         if ($prev_available) {
1248                 print $fh '  <link rel="prev" href="'.$_viewer_prev_url.'">'."\n";
1249                 if ($prefetch_prev) {
1250                         print $fh '  <link rel="prefetch" href="'.$_viewer_prev_url.'">'."\n";
1251                         print $fh '  <link rel="prefetch" href="'.$_frame_prev_url.'">'."\n";
1252                 }
1253         }
1254         if ($next_available) {
1255                 print $fh '  <link rel="next" href="'.$_viewer_next_url.'">'."\n";
1256                 if ($prefetch_next) {
1257                         print $fh '  <link rel="prefetch" href="'.$_viewer_next_url.'">'."\n";
1258                         print $fh '  <link rel="prefetch" href="'.$_frame_next_url.'">'."\n";
1259                 }
1260         }
1261         if ($show_timer) {
1262                 print $fh '  <script src="'.$_timer_url.'"></script>'."\n";
1263         }
1264         
1265         print_html_head_end($fh);
1266         print_html_body_start($fh);
1267         
1268         print $fh '   <div id="inst" class="ins">'."\n";
1269         
1270         print $fh '    <div id="title">'."\n";
1271         print $fh '     <h1 id="titletext">'.$_title.'</h1>'."\n";
1272         print $fh '    </div>'."\n";
1273         
1274         print $fh '   </div>'."\n";
1275         print $fh '   <div id="framespace">'."\n";
1276         
1277         print $fh '    <img src="'.$_frame_url.'" id="frame" class="'.$_frame_type.'" alt="'.$frame.'" title="'.$_title.'" width="'.$width.'" height="'.$height.'">'."\n";
1278         
1279         print $fh '   </div>'."\n";
1280         print $fh '   <div id="insb" class="ins">'."\n";
1281         
1282         if ($text_mode == TEXT_MODE->{'info'}) {
1283                 print $fh '    <div id="chat">'."\n";
1284                 
1285                 print_html_data($fh, $frame_data); 
1286                 
1287                 print $fh '    </div>'."\n";
1288         }
1289         elsif ($text_mode == TEXT_MODE->{'bb'}) {
1290                 print $fh '    <div id="chat">'."\n";
1291                 
1292                 print $fh '[quote][center][size=200]'.$_title.'[/size]<br>'."\n";
1293                 print $fh '[url='.$_viewer_full_url.'][img]'.$_frame_full_url.'[/img][/url][/center]<br>'."\n";
1294                 print $fh html_encode_line(
1295                         bb_to_bbcode(
1296                                 eval_bb(
1297                                         $frame_data->{'content'},
1298                                         1
1299                                 )
1300                         )
1301                 );
1302                 print $fh '[/quote]'."\n";
1303                 
1304                 print $fh '    </div>'."\n";
1305         }
1306         elsif ($frame_data->{'content'} ne '') {
1307                 print $fh '    <div id="undertext">'."\n";
1308                 print $fh bb_to_html(
1309                         eval_bb(
1310                                 $frame_data->{'content'},
1311                                 0,
1312                                 $password_ok ? $settings->{'password'} : ''
1313                         )
1314                 )."\n";
1315                 print $fh '    </div>'."\n";
1316         }
1317         
1318         print $fh '    <div id="command">'."\n";
1319         
1320         if ($show_timer) {
1321                 print $fh '     <span id="timer">';
1322                 print $fh '[<span id="ongh" class="hv '.$timer_color_h.'">'.$timer_h.'</span>';
1323                 print $fh ':<span id="ongm" class="hv '.$timer_color_m.'">'.$timer_m.'</span>';
1324                 print $fh ':<span id="ongs" class="hv '.$timer_color_s.'">'.$timer_s.'</span>]';
1325                 print $fh '</span><br>'."\n";
1326         }
1327         print $fh '     &gt;';
1328         if ($show_command_link) {
1329                 print $fh '<a href="'.($access ? $_viewer_next_url : $_viewer_last_url).'">';
1330         }
1331         if ($show_command) {
1332                 print $fh $_command;
1333         }
1334         if ($show_command_cursor) {
1335                 print $fh '<span class="inp">_</span>';
1336         }
1337         if ($show_command_link) {
1338                 print $fh '</a>';
1339         }
1340         print $fh "<br>\n";
1341         print $fh "    </div>\n";
1342         
1343         print $fh '    <div id="underlinks">'."\n     ";
1344         
1345         unless (($frame == 0) && $static) {
1346                 print $fh '<a href="'.$_base_url.'">Once again</a> | ';
1347         }
1348         if ($prev_available) {
1349                 print $fh '<a href="'.$_viewer_prev_url.'">Before</a> | ';
1350         }
1351         unless ($frame == $last_frame) {
1352                 print $fh '<a href="'.$_viewer_last_url.'">Now</a> | ';
1353         }
1354         print $fh '<a href="'.$_goto_url.'">GOTO</a>'."\n";
1355         print $fh '     <span style="float: right;">'."\n      ";
1356         if ($text_mode == TEXT_MODE->{'normal'}) {
1357                 if ($show_words) {
1358                         print $fh '<a href="'.$_words_url.'">'.$words_link_text.'</a> | ';
1359                 }
1360         }
1361         else {
1362                 print $fh '<a href="'.$_viewer_url.'">Without</a> | ';
1363         }
1364         print $fh '<a href="'.$_info_url.'">Info</a> | ';
1365         print $fh '<a href="'.$_bbcode_url.'">BB</a>';
1366         print $fh "\n     </span>\n";
1367         
1368         print $fh "    </div>\n";
1369         print $fh "   </div>\n";
1370         
1371         if (($text_mode == TEXT_MODE->{'words'}) && $show_words) {
1372                 print_comments($fh, $context, $settings, $words_data);
1373         }
1374         
1375         print_html_body_end($fh, $ong_state == STATE->{'inactive'});
1376         print_html_end($fh);
1377         
1378         
1379         unless (ref($file)) {
1380                 close ($fh);
1381         }
1382         else {
1383                 truncate ($fh , tell($fh));
1384         }
1385         
1386         return 1;
1387 }
1388
1389 sub print_comments {
1390         (my $fh, my $context, my $settings, my $words_data) = @_;
1391         
1392         my $password_ok = $context->{'password_ok'};
1393         my $frame = int($context->{'frame'});
1394         my $page = int($context->{'words_page'});
1395         my $post_count = int($words_data->{'posts'});
1396         my $id_start = $page * COMMENT_PAGE_LENGTH();
1397         my $id_stop = $id_start + COMMENT_PAGE_LENGTH();
1398         my $older = ($page > 0) ? ($page-1) : '';
1399         my $newer;
1400         my $password_query;
1401         if ($id_stop >= $post_count) {
1402                         $id_stop = $post_count;
1403         }
1404         else {
1405                 $newer = $page+1;
1406         }
1407         my $links;
1408         
1409         my $words_url = merge_url(
1410                 {'path' => CGI_VIEWER_PATH()},
1411                 {
1412                         'path' => $frame,
1413                         'query' => {'b' => TEXT_MODE->{'words'}},
1414                 }
1415         );
1416         my $older_url = merge_url(
1417                 $words_url,
1418                 {
1419                         'query' => {'i' => $page-1},
1420                         'fragment' => 'insw',
1421                         'append_query' => 1
1422                 }
1423         );
1424         my $newer_url = merge_url(
1425                 $words_url,
1426                 {
1427                         'query' => {'i' => $page+1},
1428                         'fragment' => 'insw',
1429                         'append_query' => 1
1430                 }
1431         );
1432         
1433         if ($password_ok) {
1434                 $password_query = url_query_encode({'p', $settings->{'password'}});
1435                 $older_url = merge_url($older_url, {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1436                 $newer_url = merge_url($older_url, {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
1437         }
1438         
1439         my $_post_url = html_entity_encode_dec(CGI_WORDS_PATH(), 1);
1440         my $_password = html_entity_encode_dec($settings->{'password'}, 1);
1441         my $_older_url = html_entity_encode_dec($older_url, 1);
1442         my $_newer_url = html_entity_encode_dec($newer_url, 1);
1443         
1444         if (($older ne '') || ($newer ne '')) {
1445                 $links .= '    <div class="underlinks">'."\n";
1446                 $links .= '     ';
1447                 if ($older ne '') {
1448                         $links .= '<a href="'.$_older_url.'">Older</a>'
1449                 }
1450                 if (($older ne '') && ($newer ne '')) {
1451                         $links .= ' | ';
1452                 }
1453                 if ($newer ne '') {
1454                         $links .= '<a href="'.$_newer_url.'">Newer</a>';
1455                 }
1456                 $links .= "\n";
1457                 $links .= '    </div>'."\n";
1458         }
1459         
1460         print $fh '   <div class="space"></div>'."\n";
1461         print $fh '   <div id="insw" class="ins">'."\n";
1462         
1463         print $fh '    <div class="title" id="wordstitle">'."\n";
1464         print $fh '     <h1 class="titletext" id="wordstitletext">Words</h1>'."\n";
1465         print $fh '    </div>'."\n";
1466         
1467         if ($links ne '') {
1468                 print $fh $links;
1469         }
1470         
1471         print $fh '    <div class="undertext" id="words">'."\n";
1472         
1473         if ($post_count > 0) {
1474                 for (my $i=$id_start; $i<$id_stop; ++$i) {
1475                         my $ID = $words_data->{'content'}->[$i];
1476                         my $post_path = join_path(PATH_SEPARATOR(), DATA_WORDS_PATH(), $ID);
1477                         my %post_data = read_data_file($post_path);
1478                         
1479                         my $post_time = int($post_data{'posttime'});
1480                         my $edit_time = int($post_data{'edittime'});
1481                         
1482                         my $post_time_text;
1483                         my $edit_time_text;
1484                         
1485                         if ($post_time != 0) {
1486                                 my @time_tab = gmtime($post_time);
1487                                 $post_time_text = sprintf(
1488                                         '%04d.%02d.%02d %02d:%02d:%02d UTC',
1489                                         $time_tab[5]+1900,
1490                                         $time_tab[4]+1,
1491                                         $time_tab[3],
1492                                         $time_tab[2],
1493                                         $time_tab[1],
1494                                         $time_tab[0]
1495                                 );
1496                         }
1497                         if (($edit_time !=0) && ($edit_time != $post_time)) {
1498                                 my @time_tab = gmtime($edit_time);
1499                                 $edit_time_text = sprintf(
1500                                         '%04d.%02d.%02d %02d:%02d UTC',
1501                                         $time_tab[5]+1900,
1502                                         $time_tab[4]+1,
1503                                         $time_tab[3],
1504                                         $time_tab[2],
1505                                         $time_tab[1]
1506                                 );
1507                         }
1508                         my $quote_url = merge_url(
1509                                 {'path' => CGI_WORDS_PATH()},
1510                                 {
1511                                         'query' => {
1512                                                 'f' => $frame,
1513                                                 'quote' => $ID,
1514                                                 'p' => ($password_ok ? $settings->{'password'} : '')
1515                                         }
1516                                 }
1517                         );
1518                         my $edit_url = merge_url(
1519                                 {'path' => CGI_WORDS_PATH()},
1520                                 {
1521                                         'query' => {
1522                                                 'f' => $frame,
1523                                                 'edit' => $ID,
1524                                                 'key' => $post_data{'key'},
1525                                                 'username' => $post_data{'name'},
1526                                                 'p' => ($password_ok ? $settings->{'password'} : '')
1527                                         }
1528                                 }
1529                         );
1530                         my $remove_url = merge_url(
1531                                 {'path' => CGI_WORDS_PATH()},
1532                                 {
1533                                         'query' => {
1534                                                 'f' => $frame,
1535                                                 'remove' => $ID,
1536                                                 'key' => $post_data{'key'},
1537                                                 'username' => $post_data{'name'},
1538                                                 'p' => ($password_ok ? $settings->{'password'} : '')
1539                                         }
1540                                 }
1541                         );
1542                         
1543                         my $_ID         = html_entity_encode_dec($ID, 1);
1544                         my $_name       = html_entity_encode_dec($post_data{'name'}, 1);
1545                         my $_quote_url  = html_entity_encode_dec($quote_url, 1);
1546                         my $_edit_url   = html_entity_encode_dec($edit_url, 1);
1547                         my $_remove_url = html_entity_encode_dec($remove_url, 1);
1548                         
1549                         print $fh '     <div id="'.$_ID.'"class="opomba">'."\n";
1550                         print $fh '      <div class="opomba_info">'."\n";
1551                         print $fh '       <a href="#'.$_ID.'" class="bi hu">'.$i.': '.$_name;
1552                         if ($post_time_text ne '') {
1553                                 print $fh ' &bull; '.$post_time_text;
1554                         }
1555                         if ($edit_time_text ne '') {
1556                                 print $fh ' &bull; '.$edit_time_text;
1557                         }
1558                         print $fh '</a>'."\n";
1559                         print $fh '       <div class="pr">'."\n";
1560                         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";
1561                         print $fh '       </div>'."\n";
1562                         print $fh '      </div>'."\n";
1563                         print $fh '      <div class="opomba_text">'."\n";
1564                   print $fh bb_to_html(
1565                                 eval_bb(
1566                                         $post_data{'content'},
1567                                         0,
1568                                         $password_ok ? $settings->{'password'} : ''
1569                                 )
1570                         )."\n";
1571                         print $fh '      </div>'."\n";
1572                         print $fh '     </div>'."\n";
1573                         print $fh '     <br>'."\n";
1574                 }
1575         }
1576         
1577         print $fh '     <form method="post" action="'.$_post_url.'">'."\n";
1578         print $fh '      <b>Your words:</b>'."\n";
1579         print $fh '      <textarea class="inta" name="words" rows="4"></textarea>'."\n";
1580         print $fh '      <table cellpadding="0" cellspacing="0" border="0"><tr>'."\n";
1581         print $fh '       <td><b>Your name: </b></td>'."\n";
1582         print $fh '       <td><input class="intx" type="text" name="username" value=""></td>'."\n";
1583         print $fh '       <td></td>'."\n";
1584         print $fh '      </tr><tr>'."\n";
1585         print $fh '       <td><b>Optional password: </b></td>'."\n";
1586         print $fh '       <td><input class="intx" type="password" name="password" value=""></td>'."\n";
1587         print $fh '       <td>(if you want to edit later)</td>'."\n";
1588         print $fh '      </tr><tr>'."\n";
1589         print $fh '       <td><b>Leave this empty: </b></td>'."\n";
1590         print $fh '       <td><input class="intx" type="text" name="password2" value=""></td>'."\n";
1591         print $fh '       <td>'."\n";
1592         print $fh '        <input class="inbt" type="submit" name="post" value="Send">'."\n";
1593         print $fh '        <input class="inbt" type="submit" name="preview" value="Preview">'."\n";
1594         print $fh '       </td>'."\n";
1595         print $fh '      </tr></table>'."\n";
1596         print $fh '      <input type="hidden" name="f" value="'.$frame.'">'."\n";
1597         if ($password_ok) {
1598                 print $fh '      <input type="hidden" name="p" value="'.$_password.'">'."\n";
1599         }
1600         print $fh '     </form>'."\n";
1601         print $fh '    </div>'."\n";
1602         
1603         if ($links ne '') {
1604                 print $fh $links;
1605         }
1606         
1607         print $fh '   </div>'."\n";
1608 }
1609
1610 sub write_index {
1611         (
1612                 my $state,
1613                 my $settings,
1614                 my $pass,
1615                 my $mode,
1616                 my $pause
1617         ) = @_;
1618         my $fh;
1619         my $r = 1;
1620         my $ong_state = int($state->{'state'});
1621         
1622         unless (open_encoded($fh, ">:encoding(UTF-8)", WWW_INDEX_PATH())) {
1623                 return 0;
1624         }
1625         
1626         # normal running story
1627         if ($ong_state > STATE->{'inactive'}) {
1628                 my %frame_data     = read_data_file(join_path(PATH_SEPARATOR(), DATA_PATH(), 0));
1629                 my %next_frame_data= read_data_file(join_path(PATH_SEPARATOR(), DATA_PATH(), 1));
1630                 my %default        = read_data_file(DATA_DEFAULT_PATH());
1631                 my %words_data     = read_data_file(
1632                         join_path(PATH_SEPARATOR(), DATA_WORDS_PATH(), 0),
1633                         '', # encoding
1634                         0,  # no header
1635                         1,  # header only
1636                         1,  # as list
1637                 );
1638                 
1639                 %frame_data     = merge_settings(\%default,      \%frame_data);
1640                 %next_frame_data= merge_settings(\%default, \%next_frame_data);
1641                 
1642                 $r = print_viewer_page(
1643                         $fh,
1644                         {
1645                                 'launch'         => 0,
1646                                 'frame'          => 0,
1647                                 'access'         => 1,
1648                                 'password_ok'    => 0,
1649                                 'timer_unlocked' => 3, # not relevant
1650                                 'timer'          => 0, # not relevant
1651                                 'static'         => 1,
1652                                 'show_command'   => 1,
1653                                 'text_mode'      => TEXT_MODE->{'normal'},
1654                                 'words_page'     => 0 # not relevant
1655                         },
1656                         $state,
1657                         $settings,
1658                         \%frame_data,
1659                         \%default, # prev
1660                         \%next_frame_data,
1661                         \%words_data
1662                 );
1663         }
1664         # no conditions met, pretend a normal Apache2 index
1665         elsif ($pass != 1) { 
1666                 my $index_of = CGI_PATH;
1667                 $index_of =~ s/\/$//g;
1668                 
1669                 my $_index_of     = html_entity_encode_dec($index_of  , 1);
1670                 my $_2words_date  = html_entity_encode_dec(INTF_DATE(), 1);
1671                 my $_coin_date    = html_entity_encode_dec(COIN_DATE(), 1);
1672                 my $_website      = html_entity_encode_dec(WEBSITE()  , 1);
1673                 
1674                 print_html_start ($fh);
1675                 print $fh ' <head>'."\n";
1676                 print $fh '  <meta http-equiv="Content-type" content="text/html; charset=UTF-8">'."\n";
1677                 print $fh '  <title>Index of '.$_index_of.'</title>'."\n";
1678                 print $fh ' </head>'."\n";
1679                 print $fh ' <body>'."\n";
1680                 print $fh '  <h1>Index of '.$_index_of.'</h1>'."\n";
1681                 print $fh '  <table>'."\n";
1682                 print $fh '   <tr>'."\n";
1683                 print $fh '    <th><img src="/icons/blank.gif" alt="[ICO]"></th>'."\n";
1684                 print $fh '    <th><a href="?C=N;O=D">Name</a></th>'."\n";
1685                 print $fh '    <th><a href="?C=M;O=A">Last modified</a></th>'."\n";
1686                 print $fh '    <th><a href="?C=S;O=A">Size</a></th>'."\n";
1687                 print $fh '    <th><a href="?C=D;O=A">Description</a></th>'."\n";
1688                 print $fh '   </tr><tr>'."\n";
1689                 print $fh '    <th colspan="5"><hr></th>'."\n";
1690                 print $fh '   </tr><tr>'."\n";
1691                 print $fh '    <td valign="top"><img src="/icons/back.gif" alt="[DIR]"></td>'."\n";
1692                 print $fh '    <td><a href="/">Parent Directory</a></td>'."\n";
1693                 print $fh '    <td>&nbsp;</td>'."\n";
1694                 print $fh '    <td align="right">  - </td>'."\n";
1695                 print $fh '    <td>&nbsp;</td>'."\n";
1696                 print $fh '   </tr><tr>'."\n";
1697                 print $fh '    <td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td>'."\n";
1698                 print $fh '    <td><a href="2words/">2words/</a></td>'."\n";
1699                 print $fh '    <td align="right">'.$_2words_date.'  </td>'."\n";
1700                 print $fh '    <td align="right">  - </td><td>&nbsp;</td>'."\n";
1701                 print $fh '   </tr><tr>'."\n";
1702                 print $fh '    <td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td>'."\n";
1703                 print $fh '    <td><a href="coin/">coin/</a></td>'."\n";
1704                 print $fh '    <td align="right">'.$_coin_date.'  </td>'."\n";
1705                 print $fh '    <td align="right">  - </td><td> Coincidence </td>'."\n";
1706                 print $fh '   </tr><tr>'."\n";
1707                 print $fh '    <th colspan="5"><hr></th>'."\n";
1708                 print $fh '   </tr>'."\n";
1709                 print $fh '  </table>'."\n";
1710                 print $fh '  <address>Apache/2.2.22 (Debian) Server at '.$_website.' Port 80</address>'."\n";
1711                 print $fh '  </body>'."\n";
1712                 print_html_end ($fh);
1713         }
1714         # the launch index
1715         else {
1716                 my %frame_data     = read_data_file(join_path(PATH_SEPARATOR(), DATA_PATH(), 0));
1717                 my %next_frame_data= read_data_file(join_path(PATH_SEPARATOR(), DATA_PATH(), 1));
1718                 my %default        = read_data_file(DATA_DEFAULT_PATH());
1719                 my %coin_data      = read_data_file(DATA_COIN_PATH());
1720                 
1721                 %frame_data     = merge_settings(\%default,      \%frame_data);
1722                 %next_frame_data= merge_settings(\%default, \%next_frame_data);
1723                 
1724                 if (($mode == INTF_STATE->{'>'}) && $pause) {
1725                         $r = print_viewer_page(
1726                                 $fh,
1727                                 {
1728                                         'launch'         => 1,
1729                                         'frame'          => 0,
1730                                         'access'         => 1,
1731                                         'password_ok'    => 0,
1732                                         'timer_unlocked' => 3,
1733                                         'timer'          => 0,
1734                                         'static'         => 1,
1735                                         'show_command'   => 1,
1736                                         'text_mode'      => TEXT_MODE->{'normal'},
1737                                         'words_page'     => 0 # not relevant
1738                                 },
1739                                 $state,
1740                                 $settings,
1741                                 \%frame_data,
1742                                 \%default, # prev
1743                                 \%next_frame_data,
1744                                 {'posts' => 0} # words_data
1745                         );
1746                         return $r;
1747                 }
1748                 
1749                 my $index_of = CGI_PATH;
1750                 $index_of =~ s/\/$//g;
1751                 my $title;
1752                 my $frame_file;
1753                 my $undertext = '';
1754                 my $show_parent_dir = 0;
1755                 my $show_yb = 0;
1756                 my $show_folders = 0;
1757                 my $timer = '';
1758                 my $timer_color = 'ni';
1759                 if ($mode == INTF_STATE->{'>'}) {
1760                         $title = $settings->{'story'}; # $frame_data{'title'} ?
1761                         $frame_file = 'intf-tr.gif';
1762                         $undertext = '...';
1763                         $timer = '--';
1764                 }
1765                 elsif ($mode == INTF_STATE->{'<<'}) {
1766                         $title = 'Index of';
1767                         $frame_file = 'intf-ll.gif';
1768                         $show_parent_dir = 1;
1769                         $show_yb = 1;
1770                         $timer = 'EE';
1771                         $timer_color = 'br';
1772                 }
1773                 elsif ($mode == INTF_STATE->{'>>'}) {
1774                         $title = 'Index of';
1775                         $frame_file = 'intf-pp.gif';
1776                         $show_parent_dir = 1;
1777                         $show_yb = 1;
1778                         $timer = 'EE';
1779                 }
1780                 else
1781                 {
1782                         $title = 'Index of '.$index_of;
1783                         $frame_file = 'intf-kw.gif';
1784                         $show_parent_dir = 1;
1785                         $show_folders = 1;
1786                 }
1787                 my $frame_url = merge_url(
1788                         {'path' => CGI_PATH()},
1789                         {'path' => $frame_file}
1790                 );
1791                 my $coin_server = $coin_data{'server'};
1792                 
1793                 my $_title        = html_entity_encode_dec($title           , 1);
1794                 my $_website_name = html_entity_encode_dec(WEBSITE_NAME()   , 1);
1795                 my $_frame_url    = html_entity_encode_dec($frame_url       , 1);
1796                 my $_undertext    = html_entity_encode_dec($undertext       , 1);
1797                 my $_2words_date  = html_entity_encode_dec(INTF_DATE()      , 1);
1798                 my $_coin_date    = html_entity_encode_dec(COIN_DATE()      , 1);
1799                 my $_coin_server  = html_entity_encode_dec($coin_server     , 1);
1800                 my $_2words_url   = html_entity_encode_dec(CGI_2WORDS_PATH(), 1);
1801                 my $_coin_url     = html_entity_encode_dec(CGI_COIN_PATH()  , 1);
1802                 
1803                 print_html_start($fh);
1804                 print_html_head_start($fh);
1805                 
1806                 print $fh '  <title>'.$_title.' &bull; '.$_website_name.'</title>'."\n";
1807                 
1808                 print_html_head_end($fh);
1809                 print_html_body_start($fh);
1810                 
1811                 print $fh '   <div id="inst" class="ins">'."\n";
1812                 
1813                 print $fh '    <div id="title">'."\n";
1814                 print $fh '     <h1 id="titletext">'.$_title.'</h1>'."\n";
1815                 print $fh '    </div>'."\n";
1816                 
1817                 print $fh '   </div>'."\n";
1818                 print $fh '   <div id="framespace">'."\n";
1819                 
1820                 print $fh '    <img src="'.$_frame_url.'" id="frame" alt="0">'."\n"; # title="'.$_title.'"
1821                 
1822                 print $fh '   </div>'."\n";
1823                 print $fh '   <div id="insb" class="ins">'."\n";
1824                 
1825                 print $fh '    <div id="undertext">'."\n";
1826                 
1827                 if ($show_parent_dir) {
1828                         print $fh '     <img src="/icons/back.gif" alt="[DIR]"> <a href="..">Parent Directory</a><br>'."\n";
1829                 }
1830                 if ($show_folders) {
1831                         print $fh '     <img src="/icons/folder.gif" alt="[DIR]"> <a href="'.$_2words_url.'">2words/</a> '.$_2words_date.' - <br>'."\n";
1832                         print $fh '     <img src="/icons/folder.gif" alt="[DIR]"> <a href="'.$_coin_url.'">coin/</a> '.$_coin_date.' - '.$_coin_server."\n";
1833                 }
1834                 elsif ($show_yb) {
1835                         print $fh '     <img src="/icons/folder.gif" alt="[DIR]"> <a href="'.$_2words_url.'">yyyyb/</a>'."\n";
1836                 }
1837                 if ($undertext ne '') {
1838                         print $fh '     '.$_undertext."\n";
1839                 }
1840                 
1841                 print $fh '    </div>'."\n";
1842                 
1843                 if ($timer ne '') {
1844                         print $fh '    <div id="command">'."\n";
1845                         
1846                         print $fh '     [<span id="ongh" class="'.$timer_color.'">'.$timer.'</span>';
1847                         print $fh      ':<span id="ongm" class="'.$timer_color.'">'.$timer.'</span>';
1848                         print $fh      ':<span id="ongs" class="'.$timer_color.'">'.$timer.'</span>]<br>'."\n";
1849                         
1850                         if ($undertext ne '') {
1851                                 print $fh '&gt;<a href="'.$_2words_url.'">'.$_undertext.'</a><span class="inp">_</span>'."\n";
1852                         }
1853                         print $fh "    </div>\n";
1854                 }
1855                 
1856                 print $fh "   </div>\n";
1857                 
1858                 print_html_body_end($fh, $ong_state == STATE->{'inactive'});
1859                 print_html_end($fh);
1860         }
1861         close ($fh);
1862         return $r
1863 }
1864
1865 sub write_static_viewer_page {
1866         (
1867                 my $frame,
1868                 my $state_ref,
1869                 my $settings_ref,
1870                 my $default_ref,
1871                 my $frame_data_ref,
1872                 my $prev_frame_data_ref,
1873                 my $next_frame_data_ref,
1874                 my $words_data_ref
1875         ) = @_;
1876         
1877         my %state;
1878         my %settings;
1879         my %default;
1880         my %frame_data;
1881         my %prev_frame_data;
1882         my %next_frame_data;
1883         my %words_data;
1884         
1885         my $file;
1886         
1887         $frame = int($frame);
1888         my $prev_frame = $frame -1;
1889         my $next_frame = $frame +1;
1890         
1891         %state = (ref ($state_ref)) ?
1892                 %$state_ref :
1893                 read_data_file(DATA_STATE_PATH());
1894         my $ong_state = int($state{'state'});
1895         my $last_frame = int($state{'last'});
1896         
1897         unless ($ong_state > STATE->{'inactive'}) {
1898                 return 0;
1899         }
1900         unless (
1901                 ($frame >= 0) && (
1902                         ($frame < $last_frame) || (
1903                                 ($frame <= $last_frame) &&
1904                                 ($ong_state >= STATE->{'end'})
1905                         )
1906                 )
1907         ) {
1908                 return 0;
1909         }
1910         
1911         %settings = (ref ($settings_ref)) ?
1912                 %$settings_ref :
1913                 read_data_file(DATA_SETTINGS_PATH());
1914         %default = (ref ($default_ref)) ?
1915                 %$default_ref :
1916                 read_data_file(DATA_DEFAULT_PATH());
1917         
1918         %frame_data = (ref ($frame_data_ref)) ?
1919                 %$frame_data_ref :
1920                 read_data_file(join_path(PATH_SEPARATOR(), DATA_PATH(), $frame));
1921         
1922         %prev_frame_data = (ref ($prev_frame_data_ref)) ?
1923                 %$prev_frame_data_ref : (
1924                         ($prev_frame >= 0) ?
1925                         read_data_file(join_path(PATH_SEPARATOR(), DATA_PATH(), $prev_frame)) :
1926                         %default
1927                 );
1928                 
1929         %next_frame_data = (ref ($next_frame_data_ref)) ?
1930                 %$next_frame_data_ref : (
1931                         (($next_frame < $last_frame) || (
1932                                 ($next_frame <= $last_frame) &&
1933                                 ($next_frame >= STATE->{'end'})
1934                         )) ?
1935                         read_data_file(join_path(PATH_SEPARATOR(), DATA_PATH(), $next_frame)) :
1936                         %default
1937                 );
1938         
1939         %frame_data      = merge_settings(\%default, \%frame_data);
1940         %prev_frame_data = merge_settings(\%default, \%prev_frame_data);
1941         %next_frame_data = merge_settings(\%default, \%next_frame_data);
1942         
1943         %words_data = (ref ($words_data_ref)) ?
1944                 %$words_data_ref :
1945                 read_data_file(
1946                         join_path(PATH_SEPARATOR(), DATA_WORDS_PATH(), $frame), # file
1947                         '', # encoding
1948                         0,  # no header
1949                         1,  # header only
1950                         1,  # as list; not relevant
1951                 );
1952         
1953         if ($frame_data{'page'} ne '') {
1954                 $file = $frame_data{'page'}
1955         }
1956         else {
1957                 $file = sprintf(
1958                         $settings{'frame'},
1959                         $frame, 'htm'
1960                 );
1961         }
1962         $file = join_path(PATH_SEPARATOR(), WWW_PATH(), $file);
1963         
1964         return print_viewer_page(
1965                 $file,
1966                 {
1967                         'launch'        => 0,
1968                         'frame'         => $frame,
1969                         'access'        => 1,
1970                         'password_ok'   => 0,
1971                         'timer_unlocked'=> 3, # not relevant
1972                         'timer'         => 0, # not relevant
1973                         'static'        => 1,
1974                         'show_command'  => 1,
1975                         'text_mode'     => TEXT_MODE->{'normal'},
1976                         'words_page'    => 0, # not relevant
1977                 },
1978                 \%state,
1979                 \%settings,
1980                 \%frame_data,
1981                 \%prev_frame_data,
1982                 \%next_frame_data,
1983                 \%words_data
1984         );
1985 }
1986
1987 sub write_static_goto {
1988         
1989 }
1990
1991 # ONG the frame + attachment & stuff. NOT update state file.
1992 sub ong {
1993         (
1994                 my $ID, my $ongtime, my $timer, my $update, my $print,
1995                 my $settings_ref, my $default_ref, my $data_ref, my $goto_ref
1996         ) = @_;
1997         my @files;
1998         my $cfrt;
1999         my $intf;
2000         my $frame;
2001         my $frame_data_path;
2002         my $write_data;
2003         my $in_path;
2004         my $out_path;
2005         my $r;
2006         my %settings;
2007         my %default;
2008         my %frame_data;
2009         my %frame_full_data;
2010         my %goto_list;
2011         
2012         if ($ongtime eq '') {
2013                 $ongtime = time();
2014         }
2015         
2016         if ($ID eq 'i') {
2017                 $intf = 1;
2018         }
2019         elsif ($ID eq 'c') {
2020                 $cfrt = 1;
2021         }
2022         else {
2023                 $frame = int($ID);
2024         }
2025         
2026         if ($intf) {
2027                 @files = (
2028                         'intf-00.gif',
2029                         'intf-00_04.gif',
2030                         'intf-00_08.gif',
2031                         'intf-00_10.gif',
2032                         'intf-01.gif',
2033                         'intf-01_.gif',
2034                         'intf-02.gif',
2035                         'intf-02_.gif',
2036                         'intf-04.gif',
2037                         'intf-04_.gif',
2038                         'intf-08.gif',
2039                         'intf-08_.gif',
2040                         'intf-10.gif',
2041                         'intf-10_.gif',
2042                         'intf-20.gif',
2043                         'intf-20_.gif',
2044                         'intf-kw.gif',
2045                         'intf-ll.gif',
2046                         'intf-pp.gif',
2047                         'intf-tr.gif',
2048                 );
2049         }
2050         else {
2051                 %settings = (ref ($settings_ref)) ?
2052                         %$settings_ref :
2053                         read_data_file(DATA_SETTINGS_PATH());
2054                 %default = (ref ($default_ref)) ?
2055                         %$default_ref :
2056                         read_data_file(DATA_DEFAULT_PATH());
2057                 $frame_data_path = $cfrt ?
2058                         DATA_NOACCESS_PATH() :
2059                         join_path(PATH_SEPARATOR(), DATA_PATH(), $frame);
2060                 %frame_data = (ref ($data_ref)) ?
2061                         %$data_ref :
2062                         read_data_file($frame_data_path);
2063                 %frame_full_data = merge_settings(\%default, \%frame_data);
2064                 @files = (
2065                         ($frame_full_data{'frame'} ne '') ?
2066                                 $frame_full_data{'frame'} :
2067                                 sprintf(
2068                                         $settings{'frame'},
2069                                         $frame, $frame_full_data{'ext'}
2070                                 )
2071                         ,
2072                 );
2073                 unless ($cfrt) {
2074                         %goto_list = (ref ($goto_ref)) ?
2075                                 %$goto_ref :
2076                                 read_data_file(DATA_LIST_PATH());
2077                         for (my $i=0; ;$i+=1) {
2078                                 my %file_data = read_data_file(DATA_ATTACH_PATH().$i);
2079                                 if ($file_data{'frame'} eq '') {
2080                                         last;
2081                                 }
2082                                 if (int($file_data{'frame'}) != $frame) {
2083                                         next;
2084                                 }
2085                                 if ($file_data{'content'} ne '') {
2086                                         next;
2087                                 }
2088                                 unshift @files, $file_data{'filename'};
2089                         }
2090                         if (
2091                                 (!$update) ||
2092                                 ($frame_full_data{'ongtime'} eq '')
2093                         ) {
2094                                 $frame_data     {'ongtime'} = $ongtime;
2095                                 $frame_full_data{'ongtime'} = $ongtime;
2096                                 $write_data = 1;
2097                         }
2098                         if (
2099                                 ($timer ne '') && (
2100                                         (!$update) ||
2101                                         ($frame_full_data{'timer'} eq '')
2102                                 )
2103                         ) {
2104                                 $frame_data{'timer'} = int($timer);
2105                                 $write_data = 1;
2106                         }
2107                         if ($write_data) {
2108                                 $r = write_data_file($frame_data_path, \%frame_data);
2109                                 unless ($r) {
2110                                         print STDERR "fail writing $frame_data_path\n";
2111                                         if ($print) {
2112                                                 print "write frame data fail\n";
2113                                         }
2114                                         return $r;
2115                                 }
2116                         }
2117                         $goto_list{'title-'  .$frame} = $frame_full_data{'title'};
2118                         $goto_list{'ongtime-'.$frame} = $frame_full_data{'ongtime'};
2119                         $r = write_data_file(DATA_LIST_PATH(), \%goto_list);
2120                         unless ($r) {
2121                                 print STDERR "fail writing ".DATA_LIST_PATH()."\n";
2122                                 if ($print) {
2123                                         print "write GOTO list fail\n";
2124                                 }
2125                                 return $r;
2126                         }
2127                 }
2128         }
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);
2132                 if ($print) {
2133                         print $in_path.' -> '.$out_path;
2134                 }
2135                 $r = copy_encoded($in_path, $out_path);
2136                 if ($print) {
2137                         print (($r) ? " OK\n" : " FAIL\n");
2138                 }
2139                 unless ($r) {
2140                         print STDERR "fail copy $in_path $out_path\n";
2141                         return $r
2142                 }
2143         }
2144         
2145         return 1;
2146 }
2147
2148 1