From 8c4cc224da1b2931a1f77fd7ecdd14a400253e40 Mon Sep 17 00:00:00 2001 From: b Date: Thu, 31 Aug 2023 22:01:25 +0000 Subject: [PATCH] Initial state as of 31.08.2023 --- 2words.1.c | 34 ++ 2words.1.pl | 610 +++++++++++++++++++++++ attach.1.c | 34 ++ attach.1.pl | 154 ++++++ bbcode.1.c | 34 ++ bbcode.1.pl | 181 +++++++ bsta.css | 346 +++++++++++++ bsta_lib.1.pm | 1293 ++++++++++++++++++++++++++++++++++++++++++++++++ chat.1.c | 34 ++ chat.1.pl | 439 ++++++++++++++++ config.1.txt | 29 ++ config.txt | 29 ++ configure.pl | 137 +++++ frame.1.c | 34 ++ frame.1.pl | 154 ++++++ goto.1.c | 34 ++ goto.1.pl | 172 +++++++ info.1.c | 34 ++ info.1.pl | 181 +++++++ make.sh | 28 ++ makefile | 151 ++++++ makefile.1.mak | 156 ++++++ oldlogs.1.pl | 119 +++++ ong.1.pl | 155 ++++++ ongtimes.pl | 49 ++ reset.1.pl | 116 +++++ settings | 64 +++ settings-again | 64 +++ settings-test | 64 +++ testhtml.pl | 15 + timer.js | 85 ++++ updlist.1.pl | 55 ++ viewer.1.c | 34 ++ viewer.1.pl | 627 +++++++++++++++++++++++ 34 files changed, 5745 insertions(+) create mode 100644 2words.1.c create mode 100644 2words.1.pl create mode 100644 attach.1.c create mode 100644 attach.1.pl create mode 100644 bbcode.1.c create mode 100644 bbcode.1.pl create mode 100644 bsta.css create mode 100644 bsta_lib.1.pm create mode 100644 chat.1.c create mode 100644 chat.1.pl create mode 100644 config.1.txt create mode 100644 config.txt create mode 100644 configure.pl create mode 100644 frame.1.c create mode 100644 frame.1.pl create mode 100644 goto.1.c create mode 100644 goto.1.pl create mode 100644 info.1.c create mode 100644 info.1.pl create mode 100755 make.sh create mode 100644 makefile create mode 100644 makefile.1.mak create mode 100644 oldlogs.1.pl create mode 100644 ong.1.pl create mode 100755 ongtimes.pl create mode 100644 reset.1.pl create mode 100644 settings create mode 100644 settings-again create mode 100644 settings-test create mode 100644 testhtml.pl create mode 100644 timer.js create mode 100644 updlist.1.pl create mode 100644 viewer.1.c create mode 100644 viewer.1.pl diff --git a/2words.1.c b/2words.1.c new file mode 100644 index 0000000..5fd947a --- /dev/null +++ b/2words.1.c @@ -0,0 +1,34 @@ +// 2words.c is generated from 2words.1.c +// 28.09.2016 +// +// This is the wrapper for 2words.pl. +// It's run with SETUID to have accesss to some files where the www server +// should not. That's why it has a C wrapper. In modern systems running scripts +// directly with SETUID is considered unsafe and not allowed. +// +// Copyright (C) 2016 Balthasar Szczepański +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include +#include + +###TWOWORDS_PL; +###TWOWORDS_PL_ERRLOG; + +int main(int argc, char *argv[], char *envp[]) +{ + freopen(TWOWORDS_PL_ERRLOG,"at",stderr); + return execve(TWOWORDS_PL,argv,envp); +} diff --git a/2words.1.pl b/2words.1.pl new file mode 100644 index 0000000..590d88b --- /dev/null +++ b/2words.1.pl @@ -0,0 +1,610 @@ +###PERL; +# +# /bsta/2words +# 2words is generated from 2words.1.pl. +# 09.01.2023 +# +# The wordgame interface +# +# Copyright (C) 2016 - 2017, 2023 Balthasar Szczepański +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +use strict; +#use warnings; +###LIB; +use bsta_lib qw(failpage gethttpheader getcgi entityencode readdatafile writedatafile urlencode bb2ht); +use File::Copy; + +###STORY_PATH; +###LOGO_PATH; +###FAVICON_PATH; +###WEBSITE; +###WEBSITE_NAME; +###CSS_PATH; +###TWOWORDS_PATH; +###CGI_PATH; +###SETTINGS_PATH; +###INDEX_PATH; +###INTF_DATE; +###DATA_PATH; +###VIEWER_PATH; +###DEFAULT_PATH; +###STATE_PATH; +###WWW_PATH; +###STORY_LENGTH; +###PAGE_LENGTH; +###FIRSTPAGE_LENGTH; +###COIN_PATH; +###COIN_DATE; +###LIST_PATH; + +my %http; +my %cgi; +my %story; +my %newstory; +my %settings; +my %state; +my %gotolist; + +my $time = time(); +srand ($time-$$); + +my $method; +my $IP; +my $words; +my $color2; +my $lastip; +my $storyid; +my $turn; +my $message; +my $firstletter; +my $secondletter; +my $intfstate; +my $intfpass; +my $intfpause; +my $intfmode; +my $storypath; +my $storyfile; +my $storylock; +my @storylines; +my $ongstate; +my $page; + + +delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; +###PATH; + +if ($ENV{'REQUEST_METHOD'} =~ /^(HEAD|GET|POST)$/) { + $method=$1; +} +else{ + exit failpage("Status: 405 Method Not Allowed\nAllow: GET, POST, HEAD\n","405 Method Not Allowed","The interface does not support the $ENV{'REQUEST_METHOD'} method.",$method); +} + +%http = gethttpheader (\%ENV); +%cgi = getcgi($ENV{'QUERY_STRING'}); + +if ($method eq 'POST') { + if ($http{'content-type'} eq 'application/x-www-form-urlencoded') { + my %cgipost=getcgi( ); + foreach my $ind (keys %cgipost) { + $cgi{$ind}=$cgipost{$ind}; + } + } + # multipart not supported + else{ + exit failpage("Status: 415 Unsupported Media Type\n","415 Unsupported Media Type","Unsupported Content-type: $http{'content-type'}."); + } +} + +if ($ENV{'PATH_INFO'} =~ /^\/(.+)$/) { + $page=int($1); +} +else { + $page=0; +} + +if ($ENV{'HTTP_X_FORWARDED_FOR'} =~ /^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$/) { + $IP=$1; +} +elsif ($ENV{'REMOTE_ADDR'} =~ /^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$/) { + $IP=$1; +} +else { + $IP='0.0.0.0'; +} + +if ($cgi{'words'} ne '') { + $words=$cgi{'words'}; +} + +%settings=readdatafile(SETTINGS_PATH); +%state=readdatafile(STATE_PATH); +$ongstate=int($state{'state'}); + +$storylock=0; +if (open ($storyfile,"+<",STORY_PATH)){ + $storylock=1; + if (flock($storyfile,2)) { + $storylock=2; + } + %story=readdatafile($storyfile); + + if ($story{'lastip'} =~ /^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$/) { + $lastip=$1; + } + else { + $lastip='0.0.0.0'; + } + + $storyid = int($story{'id'}); + $intfpass = int($story{'pass'}); + $intfstate = int($story{'state'}); + $intfmode = $intfstate; + $intfpause = $intfstate & 1; + if ($intfpause) { + $intfmode -= 1; + } + + if ($IP ne $lastip) { + $turn = 1; + } + else { + $turn = 0 ; + } + + if($words =~ /^bstaaaclear(all)?$/ || $intfstate < 0) { + if($words eq 'bstaaaclearall' || $intfstate < -1) { + $story{'id'}=0; + } + $story{'content'}=''; + $story{'lastip'}='0.0.0.0'; + $story{'letter'}=''; + $story{'pass'}='0'; + $story{'state'}='0'; + $turn=0; + if($ongstate == 0) { + writeindex(INDEX_PATH,0,0,0); + } + writedatafile($storyfile,%story); + } + + if ($words ne '') { + if (!$turn) { + $message = "It's not your turn."; + } + else { + if ($words =~ /^([!"\(\),\.:;\?][ \t]*)?([A-Za-z][A-Za-z'\-]*[A-Za-z']?)([!"\(\),\.:;\? \t][ \t]*)([A-Za-z][A-Za-z'\-]*[A-Za-z']?)([!"\(\),\.:;\?]?[ \t]*)$/) { + + $firstletter = lc(substr($2,0,1)); + $secondletter = lc(substr($4,0,1)); + if (($firstletter ne $story{'letter'})&&($story{'letter'} ne '')) { + $message = 'The first word must start with '.uc($story{'letter'}).'.'; + } + elsif ($firstletter eq $secondletter) { + $message = 'The second word can\'t also start with '.uc($firstletter).'.'; + } + else { + $story{'content'} = $story{'content'} . $words."\n"; + $turn = 0; + $story{'lastip'} = $IP; + $story{'letter'} = $secondletter; + + if ($cgi{'next'} ne '') { + if (split(/\r?\n/,$story{'content'}) >= (STORY_LENGTH-1)) { + $storypath = STORY_PATH.$storyid; + writedatafile($storypath,%story); + $newstory{'id'} = $storyid + 1; + $newstory{'letter'}=''; + $newstory{'lastip'}=$IP; + $newstory{'content'}=''; + $newstory{'pass'}='0'; + $newstory{'state'}='0'; + $intfstate=0; + $intfpass=0; + $intfmode=0; + $intfpause=0; + if($ongstate == 0) { + writeindex(INDEX_PATH,0,0,0); + } + writedatafile($storyfile,%newstory); + } + else { + $message = 'To early to finish this wordgame.'; + writedatafile($storyfile,%story); + } + } + else { + if ($intfpass == 1){ + $intfpass = 2; + $story{'pass'} = '2'; + if($ongstate == 0) { + writeindex(INDEX_PATH,2,0,0); + } + } + elsif(lc($2).' '.lc($4) eq $settings{'unlock'}) { + if ($intfpass == 0) { + if($ongstate == 0) { + my %framedata = readdatafile(DATA_PATH.0); + my %default = readdatafile(DEFAULT_PATH); + my $inpath; + my $outpath; + + $framedata{'ongtime'} = $time; + $gotolist{'title-0'} = $framedata{'title'}; + $gotolist{'ongtime-0'} = $framedata{'ongtime'}; + writedatafile(DATA_PATH.0,%framedata); + writedatafile(LIST_PATH,%gotolist); + + foreach my $ind (keys %default) { + unless(defined($framedata{$ind})){ + $framedata{$ind}=$default{$ind}; + } + } + + $inpath = DATA_PATH.sprintf($settings{'frame'},0,$framedata{'ext'}); + $outpath = WWW_PATH.sprintf($settings{'frame'},0,$framedata{'ext'}); + + if(copy ($inpath, $outpath)) { + $intfpass = 1; + $intfstate = 0; + $intfmode=0; + $intfpause=0; + $story{'pass'} = '1'; + $story{'state'} = '0'; + writeindex(INDEX_PATH,1,0,0); + } + } + else { + $message = "???"; + } + } + else { + $message = 'The password has already been used in this story.'; + } + } + writedatafile($storyfile,%story); + } + } + } + else { + $message = 'Please, two words, not more, not less (some punctuation is allowed).'; + } + } + } + elsif (($cgi{'s'} ne '') && ($intfpass==1) && ($ongstate == 0)) { + $intfstate = int($cgi{'s'}); + if($intfstate > 63 || $intfstate <0) { + $intfstate = 0; + } + $intfmode = $intfstate; + $intfpause = $intfstate & 1; + if ($intfpause) { + $intfmode -= 1; + } + $story{'state'} = $intfstate; + writeindex(INDEX_PATH,1,$intfmode,$intfpause); + writedatafile($storyfile,%story); + } + @storylines = split(/\r?\n/,$story{'content'}); + if(@storylines & 1) { + $turn = !$turn; + } + + close($storyfile); +} + +print "Content-type: text/html\n\n"; +if($method eq 'HEAD') { + exit; +} + +print ''."\n"; +print ''."\n"; +print 'Two words • '.WEBSITE_NAME.''."\n"; +print ''."\n"; +print ''."\n"; +print ''."\n"; +print ''."\n"; +print ''."\n"; +print '
'."\n"; + +print '
'."\n"; + +print '
'."\n"; +print '

Two words

'."\n"; +print '
'."\n"; + +if ($page == 0) { + print '
'."\n"; + for (my $i = 0; $i < @storylines; ++$i){ + print ''.entityencode($storylines[$i]).''."\n"; + $turn = !$turn; + } + print '
'."\n"; + + print '
'."\n"; + if ($message ne '') { + print ''.entityencode($message).''."\n"; + } + + if ($turn) { + print '
'."\n"; + if ($message eq '') { + if ($story{"content"} eq '') { + print 'Two words, please:
'."\n"; + } + else { + print 'Please continue, two words:
'."\n"; + } + } + print ''."\n"; + print ''."\n"; + if(@storylines >= (STORY_LENGTH-1)) { + print ''."\n"; + } + print '
'."\n"; + } + else { + if ($message eq '') { + print 'Wait for it.'."\n"; + } + } + print '
'."\n"; +} +elsif ($message ne '') { + print '
'."\n"; + print ''.entityencode($message).''."\n"; + print '
'."\n"; +} + +if(($intfpass == 1) && ($ongstate == 0)) { + print '
'."\n"; + print ''."\n"; + + print ''."\n"; + + print ''."\n"; + print ''."\n"; + print ''."\n"; + print ''."\n"; + print ''."\n"; + print ''."\n"; + print ''."\n"; + print '
o><<>>^||
'."\n"; +} + +print '
'."\n"; +print '
'."\n"; +for (my $i = $storyid-1-(($page!=0)?((($page-1)*PAGE_LENGTH)+FIRSTPAGE_LENGTH):0); $i > ($storyid-1-($page*PAGE_LENGTH)- FIRSTPAGE_LENGTH) && $i >= 0; --$i) { + $storypath = STORY_PATH.$i; + %newstory = readdatafile($storypath); + print '

'.entityencode($newstory{'content'}).'

'."\n"; +} + +print '
'."\n"; +print ''."\n"; + +print '
'."\n"; +print '
'."\n"; +print ''.WEBSITE.''."\n"; +print ''."\n"; + +sub writeindex { + (my $indexpath, my $pass, my $mode, my $pause) = @_; + my $indexfile; + my $indexof; + + if(ref($indexpath)) { + $indexfile=$indexpath; + unless (seek($indexfile, 0, 0)) { + return 0; + } + } + else { + unless (open ($indexfile, ">", $indexpath)) { + return 0; + } + } + + $indexof = CGI_PATH; + $indexof =~ s/\/$//g; + + my %coin = readdatafile(COIN_PATH); + + if ($pass != 1) { + print $indexfile ''."\n"; + print $indexfile ''."\n"; + print $indexfile ' '."\n"; + print $indexfile ' Index of '.$indexof.''."\n"; + print $indexfile ' '."\n"; + print $indexfile ' '."\n"; + print $indexfile '

Index of '.$indexof.'

'."\n"; + print $indexfile ''."\n"; + print $indexfile ''."\n"; + print $indexfile ''."\n"; + print $indexfile ''."\n"; + print $indexfile ''."\n"; + print $indexfile '
[ICO]NameLast modifiedSizeDescription

[DIR]Parent Directory  -  
[DIR]2words/'.INTF_DATE.' -  
[DIR]coin/'.COIN_DATE.' - Coincidence

'."\n"; + print $indexfile '
Apache/2.2.22 (Debian) Server at '.WEBSITE.' Port 80
'."\n"; + print $indexfile ''."\n"; + } + else { + my %framedata; + my %nextframedata; + my %default; + + %framedata = readdatafile(DATA_PATH.0); + %nextframedata = readdatafile(DATA_PATH.1); + %default=readdatafile(DEFAULT_PATH); + + # if($mode == 16 && $pause) { + # $framedata{'ongtime'} = $time; + # writedatafile(DATA_PATH.0,%framedata); + # } + + foreach my $ind (keys %default) { + unless(defined($framedata{$ind})){ + $framedata{$ind}=$default{$ind}; + } + unless(defined($nextframedata{$ind})){ + $nextframedata{$ind}=$default{$ind}; + } + } + + + print $indexfile ''."\n"; + print $indexfile ''."\n"; + print $indexfile ''; + if($mode == 8 || $mode == 4) { + print $indexfile 'Index of'; + } + elsif($mode == 16) { + print $indexfile entityencode($settings{'story'}); + if($pause) { + print $indexfile ' • '.WEBSITE_NAME; + } + } + else { + print $indexfile 'Index of '.$indexof; + } + print $indexfile ''."\n"; + print $indexfile ''."\n"; + print $indexfile ''."\n"; + print $indexfile ''."\n"; + print $indexfile ''."\n"; + print $indexfile ''."\n"; + print $indexfile '
'."\n"; + + print $indexfile '
'."\n"; + + print $indexfile '
'."\n"; + print $indexfile '

'; + if($mode == 8 || $mode == 4) { + print $indexfile 'Index of'; + } + elsif($mode == 16) { + print $indexfile entityencode($settings{'story'}); + } + else { + print $indexfile 'Index of '.$indexof; + } + print $indexfile '

'."\n"; + print $indexfile '
'."\n"; + + print $indexfile '
'."\n"; + print $indexfile '0'."\n"; + + print $indexfile '
'."\n"; + print $indexfile '
'."\n"; + if($mode == 8 || $mode == 4) { + print $indexfile '[DIR] Parent Directory
[DIR] yyyyb/'."\n"; + } + elsif ($mode == 16) { + if($pause) { + print $indexfile bb2ht($framedata{'content'})."\n"; + } + else { + print $indexfile "...\n"; + } + } + else { + print $indexfile '[DIR] Parent Directory
'."\n"; + print $indexfile '[DIR] 2words/ '.INTF_DATE.' -
'."\n"; + print $indexfile '[DIR] coin/ '.COIN_DATE.' - '.entityencode($coin{'server'})."\n"; + } + print $indexfile '
'."\n"; + + print $indexfile '
'."\n"; + if ($mode == 8) { + print $indexfile '[EE:EE:EE]'."\n"; + } + elsif ($mode == 4) { + print $indexfile '[EE:EE:EE]'."\n"; + } + elsif ($mode == 16) { + if($pause) { + print $indexfile '[00:00:00]
'."\n"; + print $indexfile '>'.entityencode($nextframedata{'title'}).''."\n"; + } + else { + print $indexfile '[--:--:--]
'."\n"; + print $indexfile '>..._'."\n"; + } + } + else { + } + print $indexfile '
'."\n"; + + print $indexfile '
'."\n"; + + print $indexfile '
'."\n"; + print $indexfile ''.WEBSITE.''."\n"; + + print $indexfile ''."\n"; + } + + unless (ref($indexpath)) { + close ($indexfile); + } + else { + truncate ($indexfile , tell($indexfile)); + } + + return 1; +} diff --git a/attach.1.c b/attach.1.c new file mode 100644 index 0000000..af32854 --- /dev/null +++ b/attach.1.c @@ -0,0 +1,34 @@ +// attach.c is generated from attach.1.c +// 19.10.2016 +// +// This is the wrapper for frame.pl. +// It's run with SETUID to have accesss to some files where the www server +// should not. That's why it has a C wrapper. In modern systems running scripts +// directly with SETUID is considered unsafe and not allowed. +// +// Copyright (C) 2016 Balthasar Szczepański +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include +#include + +###ATTACH_PL; +###ATTACH_PL_ERRLOG; + +int main(int argc, char *argv[], char *envp[]) +{ + freopen(ATTACH_PL_ERRLOG,"at",stderr); + return execve(ATTACH_PL,argv,envp); +} diff --git a/attach.1.pl b/attach.1.pl new file mode 100644 index 0000000..da38af8 --- /dev/null +++ b/attach.1.pl @@ -0,0 +1,154 @@ +###PERL; +# +# /bsta/a +# attach.pl is generated from attach.1.pl. +# 19.10.2016 +# +# The attachment interface +# +# Copyright (C) 2016 Balthasar Szczepañski +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +use strict; +#use warnings; +###LIB; + +use bsta_lib qw(failpage gethttpheader getcgi readdatafile); + +###SETTINGS_PATH; +###DATA_PATH; +###STATE_PATH; + +my %http; +my %cgi; +my %settings; +my %state; +my %filedata; + +my $time = time(); +srand ($time-$$); + +my $method; +my $ID; +my $frame; +my $password; +my $passwordOK; +my $IP; +my $buffer; +my @fileinfo; +my $file; +my $filepath; +my $direct; + +delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; +###PATH; + +if ($ENV{'REQUEST_METHOD'} =~ /^(HEAD|GET|POST)$/) { + $method=$1; +} +else{ + exit failpage("Status: 405 Method Not Allowed\nAllow: GET, POST, HEAD\n","405 Method Not Allowed","The interface does not support the $ENV{'REQUEST_METHOD'} method.",$method); +} + +%http = gethttpheader (\%ENV); +%cgi = getcgi($ENV{'QUERY_STRING'}); + +if ($method eq 'POST') { + if ($http{'content-type'} eq 'application/x-www-form-urlencoded') { + my %cgipost=getcgi( ); + foreach my $ind (keys %cgipost) { + $cgi{$ind}=$cgipost{$ind}; + } + } + # multipart not supported + else{ + exit failpage("Status: 415 Unsupported Media Type\n","415 Unsupported Media Type","Unsupported Content-type: $http{'content-type'}."); + } +} + +# print "content-type: text/plain\n\n"; + +if ($cgi{'i'} =~ /^(.+)$/) { + $ID=int($1); +} +elsif ($ENV{'PATH_INFO'} =~ /^\/(.+)$/) { + $ID=int($1); +} +else { + $ID = 0; +} + +if ($cgi{'p'} =~ /^(.+)$/) { + $password=$1; +} +else { + $password=''; +} + +%settings=readdatafile(SETTINGS_PATH); +%state=readdatafile(STATE_PATH); +%filedata=readdatafile(DATA_PATH.'a'.$ID); +if ($filedata{'frame'} ne '') { + $frame=int($filedata{'frame'}); +} +else { + $frame = -1; +} + +if($password eq $settings{'password'}){ + $passwordOK = 1; +} +else{ + $passwordOK = 0; +} + +if ($filedata{'filename'} ne '' && ($passwordOK || (int($state{'state'}) >= 1 && $frame <= int($state{'last'}) && $frame >=0))) { +} +else { + exit failpage("Status: 404 Not Found\n","404 Not Found"," Attachment ".$ID." not found."); +} + +if ($filedata{'content'} ne '') { + $direct=1; +} +else { + $direct = 0; + $filepath=DATA_PATH.$filedata{'filename'}; + open($file,'<',$filepath) or exit failpage("Status: 404 Not Found\n","404 Not Found"," Attachment ".$ID." not found."); + unless(binmode($file)) { + close($file); + return failpage("Status: 500 Internal Server Error\n","500 Internal Server Error"," Can't switch to binary mode."); + } + if (my @fileinfo = stat($filepath)){ + print 'Content-length: '.$fileinfo[7]."\n"; + } +} +print 'Content-type: '.$filedata{'content-type'}."\n"; +print 'Content-disposition: attachment; filename="'.$filedata{'filename'}.'"'."\n"; +print "\n"; +if($method ne 'HEAD'){ + if($direct) { + print $filedata{'content'}; + } + else { + while (read ($file,$buffer,1024)) { + print (STDOUT $buffer); + } + } +} +if (!$direct) { + close($file); +} + diff --git a/bbcode.1.c b/bbcode.1.c new file mode 100644 index 0000000..deee30b --- /dev/null +++ b/bbcode.1.c @@ -0,0 +1,34 @@ +// bbcode.c is generated from bbcode.1.c +// 05.06.2017 +// +// This is the wrapper for bbcode.pl. +// It's run with SETUID to have accesss to some files where the www server +// should not. That's why it has a C wrapper. In modern systems running scripts +// directly with SETUID is considered unsafe and not allowed. +// +// Copyright (C) 2017 Balthasar Szczepański +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include +#include + +###BBCODE_PL; +###BBCODE_PL_ERRLOG; + +int main(int argc, char *argv[], char *envp[]) +{ + freopen(BBCODE_PL_ERRLOG,"at",stderr); + return execve(BBCODE_PL,argv,envp); +} diff --git a/bbcode.1.pl b/bbcode.1.pl new file mode 100644 index 0000000..34503b8 --- /dev/null +++ b/bbcode.1.pl @@ -0,0 +1,181 @@ +###PERL; +# +# /bsta/b +# bbcode.pl is generated from bbcode.1.pl. +# 05.06.2017 +# +# The bbcode interface +# +# Copyright (C) 2017 Balthasar Szczepański +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +use strict; +#use warnings; +###LIB; +use bsta_lib qw(failpage gethttpheader getcgi readdatafile bb2bb linehtml); +use File::Copy; + +###DATA_PATH; +###DEFAULT_PATH; +###SETTINGS_PATH; +###STATE_PATH; +###NOACCESS_PATH; +###WEBSITE; +###VIEWER_PATH; +###ATTACH_PATH; +###FRAME_PATH; +###CGI_PATH; + +my %http; +my %cgi; +my %framedata; +my %default; +my %settings; +my %state; + +my $time = time(); +srand ($time-$$); + +my $method; +my $frame; +my $password; +my $passwordOK; +my $access; +my $showcommand; +my $ongtime; +my $seconds; + +delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; +###PATH; + +if ($ENV{'REQUEST_METHOD'} =~ /^(HEAD|GET|POST)$/) { + $method=$1; +} +else{ + exit failpage("Status: 405 Method Not Allowed\nAllow: GET, POST, HEAD\n","405 Method Not Allowed","The interface does not support the $ENV{'REQUEST_METHOD'} method.",$method); +} + +%http = gethttpheader (\%ENV); +%cgi = getcgi($ENV{'QUERY_STRING'}); + +if ($method eq 'POST') { + if ($http{'content-type'} eq 'application/x-www-form-urlencoded') { + my %cgipost=getcgi( ); + foreach my $ind (keys %cgipost) { + $cgi{$ind}=$cgipost{$ind}; + } + } + # multipart not supported + else{ + exit failpage("Status: 415 Unsupported Media Type\n","415 Unsupported Media Type","Unsupported Content-type: $http{'content-type'}."); + } +} + +if ($cgi{'f'} =~ /^(.+)$/) { + $frame=int($1); +} +elsif ($ENV{'PATH_INFO'} =~ /^\/(.+)$/) { + $frame=int($1); +} +else { + $frame = 0; +} + +if ($cgi{'p'} =~ /^(.+)$/) { + $password=$1; +} +else { + $password=''; +} + +%settings=readdatafile(SETTINGS_PATH); +%default=readdatafile(DEFAULT_PATH); +%framedata=readdatafile(DATA_PATH.$frame); +%state=readdatafile(STATE_PATH); +if($password eq $settings{'password'}){ + $passwordOK = 1; +} +else{ + $passwordOK = 0; +} + +if($frame<0) { + $frame = int($state{'last'}) + $frame +1; + %framedata=readdatafile(DATA_PATH.$frame); +} + +foreach my $ind (keys %default) { + unless(defined($framedata{$ind})){ + $framedata{$ind}=$default{$ind}; + } +} + +if ($passwordOK || (int($state{'state'}) >= 1 && $frame <= int($state{'last'}) && $frame >= 0)) { + $access=1; +} +else { + $access=0; + %framedata = readdatafile(NOACCESS_PATH); + foreach my $ind (keys %default) { + unless(defined($framedata{$ind})){ + $framedata{$ind}=$default{$ind}; + } + } +} + +print "Content-type: text/plain\n"; +if(!$access) { + print "Status: 403 Forbidden\n"; +} +print "\n"; +if($method eq 'HEAD') { + exit; +} + +print '[quote][center][size=200]'.$framedata{'title'}.'[/size]'."\n"; +print '[url=http://'.WEBSITE.VIEWER_PATH.'/'.$frame.'][img]http://'.WEBSITE.CGI_PATH.($access?sprintf($settings{'frame'},$frame,$framedata{'ext'}):$framedata{'frame'}).'[/img][/url][/center]'."\n"; +print bb2bbf($framedata{'content'}).'[/quote]'."\n"; + +sub bb2bbf { + (my $bb) = @_; + my $tag; + my $tagvalue; + my $pretext; + my $posttext; + + while($bb =~ m/(###([^#;]*);)/g) { + $tag = $1; + $tagvalue = $2; + $pretext = substr($bb,0,pos ($bb)-length($tag)); + $posttext = substr ($bb,pos ($bb)); + + if ($tagvalue =~ /^att&([0-9]+)$/) { + $tagvalue = 'http://'.WEBSITE.ATTACH_PATH.'/'.int($1); + } + elsif ($tagvalue =~ /^vw&([0-9]+)$/) { + $tagvalue = 'http://'.WEBSITE.VIEWER_PATH.'/'.int($1); + } + elsif ($tagvalue =~ /^fr&([0-9]+)$/) { + $tagvalue = 'http://'.WEBSITE.FRAME_PATH.'/'.int($1); + } + else { + $tagvalue = ''; + } + + $bb = $pretext.$tagvalue.$posttext; + } + + return bb2bb ($bb); +} diff --git a/bsta.css b/bsta.css new file mode 100644 index 0000000..652f9fc --- /dev/null +++ b/bsta.css @@ -0,0 +1,346 @@ +html +{ + background-color: #46a3ff; + /* background-color: #d9ecff; */ + border-color: #000000; + color: #000000; + text-align: center; +} +a +{ + border-color: #0057af; + color: #0057af; + text-decoration:underline; +} +a:visited +{ + border-color: #bb6622; + color: #bb6622; +} +a:hover +{ + border-color: #bb6622; + color: #bb6622; +} +a:hover:visited +{ + border-color: #0057af; + color: #0057af; +} +::selection +{ + color: #ffffff; + background-color: #bb6622; +} + +div#all +{ + background-color: #d9ecff; + /* background-color: #ffffff; */ + margin-left: auto; + margin-right: auto; + margin-top: 0px; + margin-bottom: 0px; + width: 656px; + padding-top: 27px; + padding-bottom: 27px; + text-align: center; +} +div.all +{ + background-color: #d9ecff; + /* background-color: #ffffff; */ + margin-left: auto; + margin-right: auto; + margin-top: 0px; + margin-bottom: 0px; + width: 656px; + padding-top: 27px; + padding-bottom: 27px; + text-align: center; +} + +div.ins +{ + background-color: #ffffff; + margin-left: auto; + margin-right: auto; + margin-top: 0px; + margin-bottom: 0px; + padding: 0px; + border: 0px; + width: 580px; +} +div#inst +{ + /* margin-top: 27px; */ + /* margin-bottom: 0px; */ +} +div#insb +{ + /* margin-top: 0px; */ + /* margin-bottom: 27px; */ +} + +div#title +{ + text-align: center; + padding-top: 21px; + padding-bottom: 21px; + padding-left: 0px; + padding-right: 0px; + border: 0px; + margin: 0px; +} + +h1#titletext +{ + margin: 0px; + border: 0px; + padding: 0px; +} + +div#storypuzzle +{ + text-align: left; + padding: 8px; + margin: 0px; + border: 0px; + font-weight: bold; +} + +div#framespace +{ + background-color: #ffffff; + margin-left: auto; + margin-right: auto; + margin-top: 0px; + margin-bottom: 0px; + padding: 0px; + border: 0px; + width: 656px; +} + +img#frame +{ + border: solid #0057af; + border-width:27px 38px; + padding: 0px; + margin: 0px; +} + +img.intf +{ + border-width: 0px; + padding: 0px; + margin: 0px; +} + +tr.intf +{ + border-width: 0px; + padding: 0px; + margin: 0px; +} + +td.intf +{ + border-width: 0px; + padding: 0px; + margin: 0px; +} + +table#intftable +{ + border: solid #0057af; + border-width:27px 38px; + padding: 0px; + margin: 0px; + background-color: #00ff00; + border-collapse: collapse; + border-spacing: 0px; +} + +img#frame:hover +{ + border-color: #bb6622; +} + +div#undertext +{ + text-align: left; + padding: 8px; + margin: 0px; + border: 0px; +} + +div#chat +{ + text-align: left; + padding: 8px; + margin: 0px; + border: 0px; + font-family: monospace; +} + +div.fq +{ + text-align: left; + border: solid #0057af 4px; + font-family: monospace; +} + +div.fq:hover +{ + border-color: #bb6622; +} + +div.tq +{ + text-align: left; + border: solid #0057af 4px; +} + +div.tq:hover +{ + border-color: #bb6622; +} + +div#command +{ + text-align: left; + margin: 0px; + border: 0px; + padding: 8px; + font-family: monospace; + font-size: 150%; + /* font-weight: bold; */ +} + +div#underlinks +{ + text-align: left; + padding: 8px; + margin: 0px; + border: 0px; + font-family: monospace; +} + +span.inp +{ + animation: inp 2380ms step-start infinite; +} + +@keyframes inp +{ + 50% { opacity: 0.0;} +} + +input.intx +{ + border-color: #0057af; + color: #000000; + background-color: #ffffff; + border-width: 2px; + border-style: solid; + margin: 2px; + font-family: monospace; + /* font-size: 150%; */ +} + +input.intx:focus +{ + border-color: #bb6622; +} + +input.intxc +{ + border-color: #0057af; + color: #000000; + background-color: #ffffff; + border-width: 2px; + border-style: solid; + margin: 2px; + font-family: monospace; + width: 100%; + /* font-size: 150%; */ +} + +input.intxc:focus +{ + border-color: #bb6622; +} + +/* table.intxc +{ + width=100%; +} */ + +textarea.inta +{ + border-color: #0057af; + color: #000000; + background-color: #ffffff; + border-width: 2px; + border-style: solid; + width: 100%; + margin: 2px; + resize: none; +} + +textarea.inta:focus +{ + border-color: #bb6622; +} + +input.inbt +{ + border-color: #0057af; + color: #000000; + background-color: #ffffff; + border-width: 2px; + border-style: solid; + margin: 2px; + font-family: monospace; + /* font-size: 150%; */ +} + +input.inbt:focus +{ + border-color: #bb6622; +} + +.br +{ + border-color: #bb6622!important; + color: #bb6622!important; +} +.po +{ + border-color: #ff8800!important; + color: #ff8800!important; +} +.ni +{ + border-color: #0057af!important; + color: #0057af!important; +} +.bi +{ + border-color: #ffffff!important; + color: #ffffff!important; +} +.cz +{ + border-color: #000000!important; + color: #000000!important; +} + +div.le +{ + float: left; +} +div.pr +{ + float: right; +} \ No newline at end of file diff --git a/bsta_lib.1.pm b/bsta_lib.1.pm new file mode 100644 index 0000000..1105a4b --- /dev/null +++ b/bsta_lib.1.pm @@ -0,0 +1,1293 @@ +# bsta_lib.pm is generated from bsta_lib.1.pm +# 22.09.2022 +# +# Library of functions +# +# Copyright (C) 2016-2017, 2019-2020, 2022 Balthasar Szczepański +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +package bsta_lib; + +use strict; +#use warnings +use Exporter; +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); + +use constant entitycode => { + 'amp' => '&', + 'gt' => '>', + 'lt' => '<', + 'quot' => '"', + 'acute' => '´', + 'cedil' => '¸', + 'circ' => 'ˆ', + 'macr' => '¯', + 'middot' => '·', + 'tilde' => '˜', + 'uml' => '¨', + 'Aacute' => 'Á', + 'aacute' => 'á', + 'Acirc' => 'Â', + 'acirc' => 'â', + 'AElig' => 'Æ', + 'aelig' => 'æ', + 'Agrave' => 'À', + 'agrave' => 'à', + 'Aring' => 'Å', + 'aring' => 'Ã¥', + 'Atilde' => 'Ã', + 'atilde' => 'ã', + 'Auml' => 'Ä', + 'auml' => 'ä', + 'Ccedil' => 'Ç', + 'ccedil' => 'ç', + 'Eacute' => 'É', + 'eacute' => 'é', + 'Ecirc' => 'Ê', + 'ecirc' => 'ê', + 'Egrave' => 'È', + 'egrave' => 'è', + 'ETH' => 'Ð', + 'eth' => 'ð', + 'Euml' => 'Ë', + 'euml' => 'ë', + 'Iacute' => 'Í', + 'iacute' => 'í', + 'Icirc' => 'Î', + 'icirc' => 'î', + 'Igrave' => 'Ì', + 'igrave' => 'ì', + 'Iuml' => 'Ï', + 'iuml' => 'ï', + 'Ntilde' => 'Ñ', + 'ntilde' => 'ñ', + 'Oacute' => 'Ó', + 'oacute' => 'ó', + 'Ocirc' => 'Ô', + 'ocirc' => 'ô', + 'OElig' => 'Œ', + 'oelig' => 'œ', + 'Ograve' => 'Ò', + 'ograve' => 'ò', + 'Oslash' => 'Ø', + 'oslash' => 'ø', + 'Otilde' => 'Õ', + 'otilde' => 'õ', + 'Ouml' => 'Ö', + 'ouml' => 'ö', + 'Scaron' => 'Å ', + 'scaron' => 'Å¡', + 'szlig' => 'ß', + 'THORN' => 'Þ', + 'thorn' => 'þ', + 'Uacute' => 'Ú', + 'uacute' => 'ú', + 'Ucirc' => 'Û', + 'ucirc' => 'û', + 'Ugrave' => 'Ù', + 'ugrave' => 'ù', + 'Uuml' => 'Ü', + 'uuml' => 'ü', + 'Yacute' => 'Ý', + 'yacute' => 'ý', + 'yuml' => 'ÿ', + 'Yuml' => 'Ÿ', + 'cent' => '¢', + 'curren' => '¤', + 'euro' => '€', + 'pound' => '£', + 'yen' => 'Â¥', + 'brvbar' => '¦', + 'bull' => '•', + 'copy' => '©', + 'dagger' => '†', + 'Dagger' => '‡', + 'frasl' => '⁄', + 'hellip' => '…', + 'iexcl' => '¡', + 'image' => 'ℑ', + 'iquest' => '¿', + 'lrm' => '‎', + 'mdash' => '—', + 'ndash' => '–', + 'not' => '¬', + 'oline' => '‾', + 'ordf' => 'ª', + 'ordm' => 'º', + 'para' => '¶', + 'permil' => '‰', + 'prime' => '′', + 'Prime' => '″', + 'real' => 'ℜ', + 'reg' => '®', + 'rlm' => '‏', + 'sect' => '§', + 'shy' => '­', + 'sup1' => '¹', + 'trade' => '™', + 'weierp' => '℘', + 'bdquo' => '„', + 'laquo' => '«', + 'ldquo' => '“', + 'lsaquo' => '‹', + 'lsquo' => '‘', + 'raquo' => '»', + 'rdquo' => '”', + 'rsaquo' => '›', + 'rsquo' => '’', + 'sbquo' => '‚', + 'emsp' => ' ', + 'ensp' => ' ', + 'nbsp' => ' ', + 'thinsp' => ' ', + 'zwj' => '‍', + 'zwnj' => '‌', + 'deg' => '°', + 'divide' => '÷', + 'frac12' => '½', + 'frac14' => '¼', + 'frac34' => '¾', + 'ge' => '≥', + 'le' => '≤', + 'minus' => '−', + 'sup2' => '²', + 'sup3' => '³', + 'times' => '×', + 'alefsym' => 'ℵ', + 'and' => '∧', + 'ang' => '∠', + 'asymp' => '≈', + 'cap' => '∩', + 'cong' => '≅', + 'cup' => '∪', + 'empty' => '∅', + 'equiv' => '≡', + 'exist' => '∃', + 'fnof' => 'ƒ', + 'forall' => '∀', + 'infin' => '∞', + 'int' => '∫', + 'isin' => '∈', + 'lang' => '⟨', + 'lceil' => '⌈', + 'lfloor' => '⌊', + 'lowast' => '∗', + 'micro' => 'µ', + 'nabla' => '∇', + 'ne' => '≠', + 'ni' => '∋', + 'notin' => '∉', + 'nsub' => '⊄', + 'oplus' => '⊕', + 'or' => '∨', + 'otimes' => '⊗', + 'part' => '∂', + 'perp' => '⊥', + 'plusmn' => '±', + 'prod' => '∏', + 'prop' => '∝', + 'radic' => '√', + 'rang' => '⟩', + 'rceil' => '⌉', + 'rfloor' => '⌋', + 'sdot' => '⋅', + 'sim' => '∼', + 'sub' => '⊂', + 'sube' => '⊆', + 'sum' => '∑', + 'sup' => '⊃', + 'supe' => '⊇', + 'there4' => '∴', + 'Alpha' => 'Α', + 'alpha' => 'α', + 'Beta' => 'Β', + 'beta' => 'β', + 'Chi' => 'Χ', + 'chi' => 'χ', + 'Delta' => 'Δ', + 'delta' => 'δ', + 'Epsilon' => 'Ε', + 'epsilon' => 'ε', + 'Eta' => 'Η', + 'eta' => 'η', + 'Gamma' => 'Γ', + 'gamma' => 'γ', + 'Iota' => 'Ι', + 'iota' => 'ι', + 'Kappa' => 'Κ', + 'kappa' => 'κ', + 'Lambda' => 'Λ', + 'lambda' => 'λ', + 'Mu' => 'Μ', + 'mu' => 'μ', + 'Nu' => 'Ν', + 'nu' => 'ν', + 'Omega' => 'Ω', + 'omega' => 'ω', + 'Omicron' => 'Ο', + 'omicron' => 'ο', + 'Phi' => 'Φ', + 'phi' => 'φ', + 'Pi' => 'Π', + 'pi' => 'π', + 'piv' => 'ϖ', + 'Psi' => 'Ψ', + 'psi' => 'ψ', + 'Rho' => 'Ρ', + 'rho' => 'ρ', + 'Sigma' => 'Σ', + 'sigma' => 'σ', + 'sigmaf' => 'ς', + 'Tau' => 'Τ', + 'tau' => 'τ', + 'Theta' => 'Θ', + 'theta' => 'θ', + 'thetasym' => 'ϑ', + 'upsih' => 'ϒ', + 'Upsilon' => 'Î¥', + 'upsilon' => 'υ', + 'Xi' => 'Ξ', + 'xi' => 'ξ', + 'Zeta' => 'Ζ', + 'zeta' => 'ζ', + 'crarr' => '↵', + 'darr' => '↓', + 'dArr' => '⇓', + 'harr' => '↔', + 'hArr' => '⇔', + 'larr' => '←', + 'lArr' => '⇐', + 'rarr' => '→', + 'rArr' => '⇒', + 'uarr' => '↑', + 'uArr' => '⇑', + 'clubs' => '♣', + 'diams' => '♦', + 'hearts' => '♥', + 'spades' => '♠', + 'loz' => '◊', +}; + +use constant tagsbb => { + 'ht' => '', + '/ht' => '', + 'fq' => '[quote]', + '/fq' => '[/quote]', + 'tq' => '[quote]', + '/tq' => '[/quote]', + 'ni' => '[color=#0057AF]', + '/ni' => '[/color]', + 'br' => '[color=#BB6622]', + '/br' => '[/color]', + 'po' => '[color=#FF8800]', + '/po' => '[/color]', + 'url' => '[url]', + 'url=' => '[url=', + 'url/=' => ']', + '/url' => '[/url]', + 'i' => '[i]', + '/i' => '[/i]', + 'list' => '[list]', + 'list=' => '[list=', + 'list/='=> ']', + '/list' => '[/list]', + '*' => '[*]', + '/*' => '[/*]', + '?' => '[unknown!]', + '/?' => '[/unknown!]', +}; +use constant tagsht => { + 'ht' => '', + '/ht' => '', + 'fq' => '
', + '/fq' => '
', + 'tq' => '
', + '/tq' => '
', + 'ni' => '', + '/ni' => '', + 'br' => '', + '/br' => '', + 'po' => '', + '/po' => '', + 'url' => '',#think: how to add selfincluding? + 'url=' => '', + '/url' => '', + 'i' => '', + '/i' => '', + 'list' => '
    ', + 'list=' => '
      ', + '/list' => '
', + '/list=' => '', + '*' => '
  • ', + '/*' => '
  • ', + '?' => '[unknown!]', + '/?' => '[/unknown!]', +}; + +$VERSION = 0.000002; +@ISA = qw(Exporter); +@EXPORT = (); +@EXPORT_OK = qw(entityencode failpage gethttpheader getcgi urldecode readdatafile writedatafile printdatafile printdatafileht urlencode linehtml bb2ht bb2bb); +%EXPORT_TAGS = (); + +# Function to show an error page +# arguments: 1 - header fields, 2 - page title, 3 - error message, 4 method +sub failpage { + (my $header, my $title, my $message, my $method)=@_; + if($header ne ''){ + print $header; + } + if($method eq 'HEAD') { + print "\n"; + return; + } + print "Content-type: text/html\n\n"; + print ''."\n"; + print ''."\n"; + if($title ne ''){ + print ''.entityencode($title).''."\n"; + } + print ''."\n"; + print ''."\n"; + if($title ne ''){ + print '

    '.entityencode($title).'

    '."\n"; + } + if($message ne ''){ + print '

    '.entityencode($message).'

    '."\n"; + } + print ''."\n"; +} + +# function to encode entities, decimal, +sub entityencode { + (my $t, my $all) = @_; + if ($all) { + $t =~ s/(.)/sprintf('\&#%02hu;',ord($1))/eg; + } + else { + $t =~ s/([\"=><\&])/sprintf('&#%02hu;',ord($1))/eg; + } + return $t; +} + +# function to get values of http header fields. Returns a hash. names of header +# fields are lowercase +sub gethttpheader { + (my $env) = @_; + + my %http; + + foreach my $ind (keys %$env) { + my $name = ''; + my $value= ''; + + if ($ind =~ /^HTTP_([A-Z0-9_]+)$/) { + $name=$1; + } + elsif ($ind =~ /^(CONTENT_[A-Z0-9_]+)$/) { + $name=$1; + } + else{ + next; + } + $name =~ s/_/-/g; + $name = lc($name); + if ($$env{$ind} =~ /^([\x20-\x7e]*)$/) { + $value=$1; + } + else { + next; + } + $http{$name}=$value; + } + return %http; +} + +# The function to get CGI parameters from string. +# Format is: name=url_encoded_value&name=url_encoded_value& ... &name=url_encoded_value +sub getcgi { + my $arg; + my $val; + my %cgi; + my $i = $_[0]; + $i =~ s/[\r\n]//g; + my @s = split('&',$i); + foreach my $l ( @s) { + ($arg,$val)=split('=',$l); + $cgi{$arg}=urldecode($val); + } + return %cgi; +} + +# Function for decoding URL-encoded text +sub urldecode { + my $t = $_[0]; + $t =~ s/\+/ /g; + $t =~ s/%([a-fA-F0-9]{2})/chr(hex($1))/eg; + return $t; +} + +# Function to read data from datafiles. +# Very similar to http header file reading. (function readheaderfile() in proxy +# library) +# +# Differences: +# +# 1. After field name and colon there must be exactly one whitespace (space or +# tab). Any other leading or trailing whitespace (but not the newline character +# at the end of the line) is treated as part of the field value. +# +# 2. Instead of colon an equal sign can be used. The number of whitespaces after +# it is then zero and not one. +# +# 3. When header field is split into multiple lines the next lines must start +# with exactly one whitespace (tab or space) Any other leading or trailing +# whitespace (but not the newline character at the end of the line) is treated +# as part of the field value. the lines will be joined with a newline between +# them. +# +# 4. When the same field name appears it replaces the previous one. +# +# 5. Line separator is LF and not CR LF. The CR character is treated as part of +# the field value. +# +# 6. After the end of header (double newline) all next lines are treated as the +# value of the "content" field. +# +# Returns a hash containing the values. +# Names are case sensitive and are converted to lowercase +# +# Argument can be a path or a file handle. In case of a file handle it will just +# read the file. In case of path it opens the file before reading and closes +# after. On failure (file not open) returns empty hash. +# +sub readdatafile { + (my $datapath) = @_; + my $datafile; + my %data; + my $eoh=0; + + # check if $datapath is actually a path or maybe a filehandle + # filehandles are references. + if(ref($datapath)) { + $datafile=$datapath; + unless (seek($datafile, 0, 0)) { + return %data; + } + } + else { + unless (open ($datafile, "<", $datapath)) { + return %data; + } + } + + # The name of header field in previous line. Required for header fields that + # occupy multiple lines. + my $lastname=''; + + while (defined(my $line = <$datafile>)) { + my $name=''; + my $value=''; + + if ($eoh){ + unless($line eq'') { + $data{'content'} = $data{'content'}.$line; + } + next; + } + + $line =~ s/[\n]$//g; + + # Empty line - end of header. + if ($line eq ''){ + $eoh=1; + } + # Line starts with whitespace. It's a continuation of the previous line. + # Concatenate the field value, separated by newline. + elsif($line =~ /^[ \t](.*)$/){ + if($lastname ne '') { + $data{$lastname}.="\n".$1; + } + } + # Line starts with a name followed by colon/equal sign. Save the value + elsif ($line =~ /^([^:=]+)((:[ \t])|=)(.*)$/) { + $name = lc($1); + $value = $4; + + $data{$name}=$value; + + $lastname = $name; + } + } + + # If argument was a path the file must be closed. + unless (ref($datapath)) { + close ($datafile); + } + + return %data; +} + +# the function to write data to datafiles (see readdatafile() description) +# +# First argument can be a path or a file handle. In case of a file handle it +# will just write the file. In case of path it opens the file before writing and +# closes after. +# +# On failure (file not open) returns 0. +# On success returns 1. +# +sub writedatafile { + (my $headerpath, my %header) = @_; + my $headerfile; + + if(ref($headerpath)) { + $headerfile=$headerpath; + unless (seek($headerfile, 0, 0)) { + return 0; + } + } + else { + unless (open ($headerfile, ">", $headerpath)) { + return 0; + } + } + + foreach my $ind (keys %header) { + unless($ind eq 'content') { + my $headname = $ind; + my $headval = $header{$ind}; + $headval =~ s/\r//g; + $headval =~ s/\n/\n /g; + print $headerfile "$headname: $headval\n"; + } + } + print $headerfile "\n".$header{'content'}; + + unless (ref($headerpath)) { + close ($headerfile); + } + else { + truncate ($headerfile , tell($headerfile)); + } + + return 1; +} + +# the function to print data to stdout (see readdatafile() description) +# +# On success returns 1. +# +sub printdatafile { + (my %header) = @_; + + foreach my $ind (keys %header) { + unless($ind eq 'content') { + my $headname = $ind; + my $headval = $header{$ind}; + $headval =~ s/\r//g; + $headval =~ s/\n/\n /g; + print "$headname: $headval\n"; + } + } + print "\n".$header{'content'}; + + return 1; +} + +# the function to print data to stdout as html (see readdatafile() description) +# +# On success returns 1. +# +sub printdatafileht { + (my %header) = @_; + + foreach my $ind (keys %header) { + unless($ind eq 'content') { + my $headname = $ind; + my $headval = $header{$ind}; + $headval =~ s/\r//g; + $headval =~ s/\n/\n /g; + print linehtml("$headname: $headval\n"); + } + } + print linehtml("\n".$header{'content'}); + + return 1; +} + + +sub urlencode { + (my $t, my $all) = @_; + if ($all) { + $t =~ s/(.)/sprintf('%%%02hX',ord($1))/eg; + } + else { + $t =~ s/([^0-9A-Za-z.~\-_])/sprintf('%%%02hX',ord($1))/eg; + } + return $t; +} + +#analyse bbcode text to build tag tree #TODO make [/*] optional! +sub bbtree { + (my $bb, my $printdebug) = @_; + my %bbtree; + my $ind; + my $tag; + my $tagname; + my $tagvalue; + my $tagend; + my $level=0; + my $pretext; + my $debug; + + $ind="_"; + $level=0; + $bbtree{"_.n"}="ht"; + $bbtree{"_.v"}=''; + $bbtree{"_.t"}="tg"; + $bbtree{"_.e"}=0; + $bbtree{"_.c"}=''; + $debug .= debug($printdebug, "\n\n"); + $bbtree{'_.'.$bbtree{"_.e"}.'.t'}="tg"; + $bbtree{'_.'.$bbtree{"_.e"}.'.n'}='/ht'; + $bbtree{"_.e"}+=1; + $bbtree{"_.c"}=1; + + return ($debug, %bbtree); +} + +#convert tag tree to final text +sub convtree { + (my $printdebug, my $debug, my $lang, my %bbtree) = @_; + my $ht; + my $ind; + my $indd; + my $level=0; + my $tagsr = ($lang eq 'html') ? tagsht : tagsbb; + my %tags = %$tagsr; + my $escape = ($lang eq 'html'); + + # $debug .= debug($printdebug, "\n****\n"); + # foreach my $iiii (keys %tags) { + # $debug .= debug($printdebug, $iiii.'='.$tags{$iiii}."\n"); + # } + # $debug .= debug($printdebug, "****\n"); + + $level=0; + $ind='_'; + $ht=''; + $debug .= debug($printdebug, "\n\n"); + return ($debug, $ht); +} + +#bbcode to html, TBD +sub bb2ht { + (my $bb, my $printdebug) = @_; + my $ht; + my %bbtree; + my $debug; + + ($debug, %bbtree) = bbtree($bb,$printdebug); + ($debug, $ht) = convtree ($printdebug, $debug, 'html', %bbtree); + + return $ht; + + # $level=0; + # $ind='_'; + # $ht=''; + # $debug .= debug($printdebug, "\n\n"); + # # print $debug; + + + +} + +#bbcode to bb, TBD +sub bb2bb { + (my $bb, my $printdebug) = @_; + my $ht; + my %bbtree; + my $debug; + + ($debug, %bbtree) = bbtree($bb,$printdebug); + ($debug, $ht) = convtree ($printdebug, $debug, 'bb', %bbtree); + + return $ht; + + # $level=0; + # $ind='_'; + # $ht=''; + # $debug .= debug($printdebug, "\n\n"); + # # print $debug; + + + +} + +sub linehtml { + (my $ht) = @_; + my $esc; + my $ind; + + $ht =~ s/\r\n/\n/g; + $ht =~ s/\r/\n/g; + + while ($ht ne '') { + $ind = index($ht,"\n"); + if($ind>=0){ + $esc = $esc.entityencode(substr($ht,0,$ind))."
    \n"; + $ht=substr($ht,$ind+1); + } + else + { + $esc = $esc.entityencode($ht); + $ht = ''; + } + } + return $esc; +} + +sub debug { + (my $print, my $text) = @_; + + if ($print) { + print $text; + } + + return $text; +} + +1 diff --git a/chat.1.c b/chat.1.c new file mode 100644 index 0000000..2acd182 --- /dev/null +++ b/chat.1.c @@ -0,0 +1,34 @@ +// chat.c is generated from chat.1.c +// 13.11.2016 +// +// This is the wrapper for chat.pl. +// It's run with SETUID to have accesss to some files where the www server +// should not. That's why it has a C wrapper. In modern systems running scripts +// directly with SETUID is considered unsafe and not allowed. +// +// Copyright (C) 2016 Balthasar Szczepański +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include +#include + +###CHAT_PL; +###CHAT_PL_ERRLOG; + +int main(int argc, char *argv[], char *envp[]) +{ + freopen(CHAT_PL_ERRLOG,"at",stderr); + return execve(CHAT_PL,argv,envp); +} diff --git a/chat.1.pl b/chat.1.pl new file mode 100644 index 0000000..ac274ba --- /dev/null +++ b/chat.1.pl @@ -0,0 +1,439 @@ +###PERL; +# +# /bsta/coin +# chat.pl is generated from chat.1.pl. +# 11.01.2017 +# +# The coincidence interface +# +# Copyright (C) 2016-2017 Balthasar Szczepański +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +use strict; +#use warnings; +###LIB; +use bsta_lib qw(failpage gethttpheader getcgi entityencode readdatafile writedatafile urlencode); +use File::Copy; + +###LOGO_PATH; +###FAVICON_PATH; +###COIN_PATH; +###CHAT_PATH; +###WEBSITE_NAME; +###FAVICON_PATH; +###CSS_PATH; +###LOGO_PATH; +###WEBSITE; +###COINCIDENCE_PATH; +###CGI_PATH; + +my %http; +my %cgi; +my %coin; +my %chat; + +my $time = time(); +srand ($time-$$); + +my $method; +my $IP; +my $page; +my $words; +my $username; +my $action; +my $password; +my $chatfile; +my $state; +my $passwordOK; +my @chatlines; +my $chatstate; +my $message; +my $chatid; +my $lastid; + +delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; +###PATH; + +if ($ENV{'REQUEST_METHOD'} =~ /^(HEAD|GET|POST)$/) { + $method=$1; +} +else{ + exit failpage("Status: 405 Method Not Allowed\nAllow: GET, POST, HEAD\n","405 Method Not Allowed","The interface does not support the $ENV{'REQUEST_METHOD'} method.",$method); +} + +%http = gethttpheader (\%ENV); +%cgi = getcgi($ENV{'QUERY_STRING'}); + +if ($method eq 'POST') { + if ($http{'content-type'} eq 'application/x-www-form-urlencoded') { + my %cgipost=getcgi( ); + foreach my $ind (keys %cgipost) { + $cgi{$ind}=$cgipost{$ind}; + } + } + # multipart not supported + else{ + exit failpage("Status: 415 Unsupported Media Type\n","415 Unsupported Media Type","Unsupported Content-type: $http{'content-type'}."); + } +} + +if ($ENV{'PATH_INFO'} =~ /^\/(.+)$/) { + $page=int($1); +} +else { + $page=-1; +} + +if ($ENV{'REMOTE_ADDR'} =~ /^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$/) { + $IP=$1; +} +else { + $IP='0.0.0.0'; +} + +if ($cgi{'words'} ne '') { + $words=$cgi{'words'}; +} +if ($cgi{'username'} ne '') { + $username=$cgi{'username'}; +} +if ($cgi{'join'} ne '') { + $action=1; +} +elsif ($cgi{'leave'} ne '') { + $action=2; +} +elsif ($cgi{'nopost'} ne '') { + $action=3; +} +elsif ($cgi{'file'} ne '') { + $action=4; +} +else { + $action=0; +} +if ($cgi{'p'} ne '') { + $password=$cgi{'p'}; +} + +%coin=readdatafile(COIN_PATH); + +if($password eq $coin{'password'}){ + $passwordOK = 1; +} +else{ + $passwordOK = 0; + $username = ''; +} + +if($page < 0) { + if (open ($chatfile,"+<",CHAT_PATH)){ + if (flock($chatfile,2)) { + %chat=readdatafile($chatfile); + + $chatstate=int($chat{'state'}); + $chatid=int($chat{'id'}); + $lastid=$chatid; + + if($action==0 && $cgi{'words'} ne '') { + if($chatstate < 1 && !$passwordOK) { + $message = 'Not connected.'; + } + else { + if ($cgi{'words'} !~ /[\r\n]/) { + if($username =~ /^[A-Za-z]*$/) { + $chat{'content'}=$chat{'content'}.$username.': '.$cgi{'words'}."\n"; + if($chatstate < 2) { + $chat{'state'} = 2; + $chatstate = 2; + } + writedatafile($chatfile,%chat); + } + else { + $message='Invalid username.'; + } + } + else { + $message='Invalid text.'; + } + } + } + elsif ($action==1) { + if($chatstate > 0 && !$passwordOK) { + $message = 'Already connected.'; + } + else { + if($username =~ /^[A-Za-z]*$/) { + if ($passwordOK || $cgi{'words'} eq $coin{'server'}) { + $chat{'content'}=$chat{'content'}.'join@'.$username.': '.$cgi{'words'}."\n"; + if($chatstate < 1) { + $chat{'state'} = 1; + $chatstate = 1; + } + writedatafile($chatfile,%chat); + } + elsif ($cgi{'words'} eq '') { + $message='Server ID missing.'; + } + elsif ($cgi{'words'} !~ /^[0-9]+$/) { + $message='Invalid server ID.'; + } + else { + $message='No active Coincidence server with this ID.'; + } + } + else { + $message = 'Invalid username.'; + } + } + } + elsif ($action==2) { + if($chatstate < 1 && !$passwordOK) { + $message = 'Already disconnected.'; + } + else { + if($username =~ /^[A-Za-z]*$/) { + $chat{'content'}=$chat{'content'}.'leave@'.$username.': '.$cgi{'words'}."\n"; + if($username ne '') { + writedatafile($chatfile,%chat); + } + elsif ($chatstate > 1) { + writedatafile(CHAT_PATH.$chatid,%chat); + my %newchat; + $newchat{'id'}=$chatid+1; + $newchat{'state'}=0; + $newchat{'content'}=''; + writedatafile($chatfile,%newchat); + } + else { + my %newchat; + $newchat{'id'}=$chatid; + $newchat{'state'}=0; + $newchat{'content'}=''; + writedatafile($chatfile,%newchat); + } + } + else { + $message = 'Invalid username.'; + } + } + } + elsif($action==4 && $cgi{'file'} ne '' && $cgi{'words'} ne '' && $passwordOK) { + if ($cgi{'words'} !~ /[\r\n]/) { + if($username =~ /^[A-Za-z]*$/) { + $chat{'content'}=$chat{'content'}.'file@'.$username.': '.$cgi{'words'}."\n"; + if($chatstate < 2) { + $chat{'state'} = 2; + $chatstate = 2; + } + writedatafile($chatfile,%chat); + } + else { + $message='Invalid username.'; + } + } + else { + $message='Invalid text.'; + } + } + + @chatlines = split(/\r?\n/,$chat{'content'}); + } + else{ + $chatstate=0; + $message='Can\'t lock data file!'; + } + + + close($chatfile); + } + else { + $chatstate=0; + $message='Can\'t open data file!'; + } +} +else { + # %chat=readdatafile(CHAT_PATH); + # $chatid=int($chat{'id'})-$page; + %chat=readdatafile(CHAT_PATH); + $lastid=int($chat{'id'}); + %chat=readdatafile(CHAT_PATH.$page); + $chatid=int($chat{'id'}); + $chatstate=int($chat{'state'}); + @chatlines = split(/\r?\n/,$chat{'content'}); +} + + +# print "Content-type: text/plain\n\n"; +# print CHAT_PATH."\n"; +# print 'state: '.$chat{'state'}."\n"; +# print 'id: '.$chat{'id'}."\n\n"; +# print $chat{'content'}; + +print "Content-type: text/html\n\n"; +if($method eq 'HEAD') { + exit; +} + +print ''."\n"; +print ''."\n"; +print 'Coincidence • '.WEBSITE_NAME.''."\n"; +print ''."\n"; +print ''."\n"; +print ''."\n"; +print ''."\n"; +print ''."\n"; +print '
    '."\n"; + +print '
    '."\n"; + +print '
    '."\n"; +print '

    Coincidence

    '."\n"; +print '
    '."\n"; + +print '
    '."\n"; +if ($page >= 0) { + print 'Before: '.$chatid."\n"; +} +elsif ($chatstate>0){ + print 'Connected to server '.entityencode($coin{'server'}).' as user '.entityencode(($username ne '')?$username:$coin{'name'}).' ('.entityencode(abbrname(($username ne '')?$username:$coin{'name'})).'), public key '.entityencode($coin{'key'}).'.'."\n"; +} +else{ + print 'Not connected.'; +} +print '
    '."\n"; +print '
    '."\n"; +if ($message ne '') { + print ''.entityencode($message).''."\n"; +} +if ($page < 0) { + print '
    '."\n"; + if ($passwordOK) { + print ''."\n"; + print ''."\n"; + print "|\n"; + print ''."\n"; + print ''."\n"; + print ''."\n"; + print ''."\n"; + print ''."\n"; + print ''."\n"; + } + elsif ($chatstate>0) { + print ''."\n"; + print ''."\n"; + print "|\n"; + print ''."\n"; + print ''."\n"; + } + else { + print ''."\n"; + print ''."\n"; + } + print '
    '."\n"; +} +print '
    '."\n"; + +print '
    '."\n"; + +print '
    '."\n"; +if ($page < 0) { + for (my $i = @chatlines-1; $i>=0; --$i) { + print chatline($chatlines[$i])."
    \n"; + } +} +else { + for (my $i = 0; $i<@chatlines; ++$i) { + print chatline($chatlines[$i])."
    \n"; + } +} +print '
    '."\n"; + +print ''."\n"; + +print '
    '."\n"; +print '
    '."\n"; +print ''.WEBSITE.''."\n"; +print ''."\n"; + + + +# print CHAT_PATH."\n"; +# print 'state: '.$chat{'state'}."\n"; +# print 'id: '.$chat{'id'}."\n\n"; +# print $chat{'content'}; + +sub abbrname { + (my $name) = @_; + my $abbr; + + if($name !~ /^[A-Za-z]+$/) { + return '?'; + } + + $abbr = uc(substr($name,0,1)); + $name = substr($name,1); + while($name =~ m/([A-Z])/g) { + $abbr = $abbr.$1; + } + return $abbr; +} + +sub chatline { + (my $line) = @_; + my $action; + my $name; + my $text; + + if ($line =~ /^([a-z]*@)?([A-Za-z]*): (.*)$/) { + $action=$1; + $name=$2; + $text=$3; + + if($action ne ''){ + if ($action eq 'join@') { + return entityencode(($name ne '')?$name:$coin{'name'}).' ('.entityencode(abbrname(($name ne '')?$name:$coin{'name'})).') joined the public chat on server '.entityencode($coin{'server'}).'.'; + } + elsif ($action eq 'leave@') { + return entityencode(($name ne '')?$name:$coin{'name'}).' ('.entityencode(abbrname(($name ne '')?$name:$coin{'name'})).') left the public chat on server '.entityencode($coin{'server'}).'.'; + } + elsif ($action eq 'file@') { + return entityencode(($name ne '')?$name:$coin{'name'}).' ('.entityencode(abbrname(($name ne '')?$name:$coin{'name'})).') sent the file '.entityencode($text).'.'; + } + else { + return 'E:E:E'; + } + } + else { + return ''.entityencode(abbrname(($name ne '')?$name:$coin{'name'})).': '.entityencode($text).''; + } + } + else { + return 'E:E:E'; + } +} diff --git a/config.1.txt b/config.1.txt new file mode 100644 index 0000000..be78fb5 --- /dev/null +++ b/config.1.txt @@ -0,0 +1,29 @@ +# config.txt is generated from config.1.txt +# 20.08.2016 +# +# The file with the autogenerated configurations for Apache2 and crontab +# +# Copyright (C) 2016 Balthasar Szczepański +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# ! the license is for config.1.txt, not config.txt. + +################################################################################ +#copy this to your Apache2 configuration, + +###VIEWER_ALIAS; +###FRAME_ALIAS; + +################################################################################ diff --git a/config.txt b/config.txt new file mode 100644 index 0000000..9f32353 --- /dev/null +++ b/config.txt @@ -0,0 +1,29 @@ +# config.txt is generated from config.1.txt +# 20.08.2016 +# +# The file with the autogenerated configurations for Apache2 and crontab +# +# Copyright (C) 2016 Balthasar Szczepański +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# ! the license is for config.1.txt, not config.txt. + +################################################################################ +#copy this to your Apache2 configuration, + +ScriptAlias /bsta/v /eizm/bin/bsta/viewer +ScriptAlias /bsta/v /eizm/bin/bsta/frame + +################################################################################ diff --git a/configure.pl b/configure.pl new file mode 100644 index 0000000..b397ac5 --- /dev/null +++ b/configure.pl @@ -0,0 +1,137 @@ +#!/usr/bin/perl + +# configure.pl +# 02.07.2017 +# +# This script is called from the makefile. It reads the settings file and +# inserts the information in the source files. +# +# Copyright (C) 2015-2017 Balthasar Szczepański +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +unless ($ARGV[0]) { + print STDERR "Configfile missing.\n"; + exit 1; +} + +unless (open $configfile, "<", $ARGV[0]) { + print STDERR "Cannot open configfile\n"; + exit 2; +} + +# Read the config file, line format: +# some_name = some value # some comment +while (defined(my $line = <$configfile>)) { + $line =~ s/[\r\n]//g; + $line =~ s/#.*$//; #comment + if ($line =~ /^[ \t]*([a-zA-Z0-9_\-\.]+)[ \t]*=[ \t]*([^ \t](.*[^ \t])?)[ \t]*$/){ + my $name=$1; + my $value=$2; + $set{$name}=$value; + } +} +close ($configfile); + +# Now generate things to be inserted. + +$def{'GZIP_PATH'} = "use constant GZIP_PATH => '".$set{'gzip'}."';"; +$def{'LOG_PATH'} = "use constant LOG_PATH => '".$set{'log_path'}."';"; + +$def{'DATA_PATH'} = "use constant DATA_PATH => '".$set{'data_path'}."';"; +$def{'DEFAULT_PATH'} = "use constant DEFAULT_PATH => '".$set{'data_path'}."default';"; +$def{'SETTINGS_PATH'} = "use constant SETTINGS_PATH => '".$set{'data_path'}."settings';"; +$def{'NOACCESS_PATH'} = "use constant NOACCESS_PATH => '".$set{'data_path'}."noaccess';"; +$def{'STATE_PATH'} = "use constant STATE_PATH => '".$set{'data_path'}."state';"; +$def{'STORY_PATH'} = "use constant STORY_PATH => '".$set{'data_path'}."story';"; +$def{'CHAT_PATH'} = "use constant CHAT_PATH => '".$set{'data_path'}."chat';"; +$def{'COIN_PATH'} = "use constant COIN_PATH => '".$set{'data_path'}."coincidence';"; +$def{'LIST_PATH'} = "use constant LIST_PATH => '".$set{'data_path'}."list';"; +$def{'WWW_PATH'} = "use constant WWW_PATH => '".$set{'www_path'}."';"; +$def{'INDEX_PATH'} = "use constant INDEX_PATH => '".$set{'www_path'}."index.htm';"; +$def{'CGI_PATH'} = "use constant CGI_PATH => '".$set{'cgi_path'}."';"; +$def{'CSS_PATH'} = "use constant CSS_PATH => '".$set{'cgi_path'}."bsta.css';"; +$def{'LOGO_PATH'} = "use constant LOGO_PATH => '".$set{'cgi_path'}."botmlogo.png';"; +$def{'FRAME_PATH'} = "use constant FRAME_PATH => '".$set{'cgi_path'}."f';"; +$def{'VIEWER_PATH'} = "use constant VIEWER_PATH => '".$set{'cgi_path'}."v';"; +$def{'ATTACH_PATH'} = "use constant ATTACH_PATH => '".$set{'cgi_path'}."a';"; +$def{'INFO_PATH'} = "use constant INFO_PATH => '".$set{'cgi_path'}."i';"; +$def{'BBCODE_PATH'} = "use constant BBCODE_PATH => '".$set{'cgi_path'}."b';"; +$def{'GOTO_PATH'} = "use constant GOTO_PATH => '".$set{'cgi_path'}."g';"; +$def{'TWOWORDS_PATH'} = "use constant TWOWORDS_PATH => '".$set{'cgi_path'}."2words';"; +$def{'COINCIDENCE_PATH'} = "use constant COINCIDENCE_PATH => '".$set{'cgi_path'}."coin';"; +$def{'TIMER_PATH'} = "use constant TIMER_PATH => '".$set{'cgi_path'}."timer.js';"; +$def{'FAVICON_PATH'} = "use constant FAVICON_PATH => '".$set{'favicon_path'}."';"; +$def{'WEBSITE'} = "use constant WEBSITE => '".$set{'website'}."';"; +$def{'WEBSITE_NAME'} = "use constant WEBSITE_NAME => '".$set{'website_name'}."';"; +$def{'INTF_DATE'} = "use constant INTF_DATE => '".$set{'intf_date'}."';"; +$def{'COIN_DATE'} = "use constant COIN_DATE => '".$set{'coin_date'}."';"; +$def{'LOG_SIZE_LIMIT'} = "use constant LOG_SIZE_LIMIT => ".$set{'log_size_limit'}.";"; +$def{'LOGS_UNCOMPRESSED'} = "use constant LOGS_UNCOMPRESSED => ".$set{'logs_uncompressed'}.";"; +$def{'LOGS_TOTAL'} = "use constant LOGS_TOTAL => ".$set{'logs_total'}.";"; +$def{'STORY_LENGTH'} = "use constant STORY_LENGTH => ".int($set{'story_length'}).";"; +$def{'PAGE_LENGTH'} = "use constant PAGE_LENGTH => ".int($set{'page_length'}).";"; +$def{'FIRSTPAGE_LENGTH'} = "use constant FIRSTPAGE_LENGTH => ".int($set{'firstpage_length'}).";"; + +$def{'VIEWER_ALIAS'} = 'ScriptAlias '.$set{'cgi_path'}.'v '.$set{'bin_path'}.'viewer'; +$def{'FRAME_ALIAS' } = 'ScriptAlias '.$set{'cgi_path'}.'v '.$set{'bin_path'}.'frame'; + +$def{'LIB'} = "use lib '".$set{'lib_path'}."';"; + +$def{'PATH'} = "\$ENV{'PATH'} = '".$set{'path'}."';"; + +$def{'PERL'} = "#!".$set{'perl'}; + +$def{'FRAME_PL'} = '#define FRAME_PL "'.$set{'bin_path'}.'frame.pl"'; +$def{'VIEWER_PL'} = '#define VIEWER_PL "'.$set{'bin_path'}.'viewer.pl"'; +$def{'ATTACH_PL'} = '#define ATTACH_PL "'.$set{'bin_path'}.'attach.pl"'; +$def{'TWOWORDS_PL'} = '#define TWOWORDS_PL "'.$set{'bin_path'}.'2words.pl"'; +$def{'CHAT_PL'} = '#define CHAT_PL "'.$set{'bin_path'}.'chat.pl"'; +$def{'INFO_PL'} = '#define INFO_PL "'.$set{'bin_path'}.'info.pl"'; +$def{'GOTO_PL'} = '#define GOTO_PL "'.$set{'bin_path'}.'goto.pl"'; +$def{'BBCODE_PL'} = '#define BBCODE_PL "'.$set{'bin_path'}.'bbcode.pl"'; +$def{'FRAME_PL_ERRLOG'} = '#define FRAME_PL_ERRLOG "'.$set{'log_path'}.'frame-stderr.log"'; +$def{'VIEWER_PL_ERRLOG'} = '#define VIEWER_PL_ERRLOG "'.$set{'log_path'}.'viewer-stderr.log"'; +$def{'ATTACH_PL_ERRLOG'} = '#define ATTACH_PL_ERRLOG "'.$set{'log_path'}.'attach-stderr.log"'; +$def{'TWOWORDS_PL_ERRLOG'} = '#define TWOWORDS_PL_ERRLOG "'.$set{'log_path'}.'2words-stderr.log"'; +$def{'CHAT_PL_ERRLOG'} = '#define CHAT_PL_ERRLOG "'.$set{'log_path'}.'chat-stderr.log"'; +$def{'INFO_PL_ERRLOG'} = '#define INFO_PL_ERRLOG "'.$set{'log_path'}.'info-stderr.log"'; +$def{'GOTO_PL_ERRLOG'} = '#define GOTO_PL_ERRLOG "'.$set{'log_path'}.'goto-stderr.log"'; +$def{'BBCODE_PL_ERRLOG'} = '#define BBCODE_PL_ERRLOG "'.$set{'log_path'}.'bbcode-stderr.log"'; + +$def{'CC'} = 'CC='.$set{'gcc'}; +$def{'CF'} = 'CF='.$set{'c_flags'}; +$def{'PL'} = 'PL='.$set{'perl'}; +$def{'MV'} = 'MV='.$set{'mv'}; +$def{'CP'} = 'CP='.$set{'cp'}; +$def{'RM'} = 'RM='.$set{'rm'}; +$def{'OD'} = 'OD='.$set{'bin_path'}; +$def{'LD'} = 'LD='.$set{'lib_path'}; +$def{'WD'} = 'WD='.$set{'www_path'}; +$def{'CM'} = 'CM='.$set{'chmod'}; + + +# Now go through input file, find lines to be replaced. Format: +# ###SOME_NAME; +# If found - replace. + +while (defined($line = )) { + $line =~ s/[\r\n]//g; + if ($line =~ /###([a-zA-Z0-9_]+);/) { + print "$def{$1}\n"; + } + else { + print "$line\n"; + } +} diff --git a/frame.1.c b/frame.1.c new file mode 100644 index 0000000..0f74d7e --- /dev/null +++ b/frame.1.c @@ -0,0 +1,34 @@ +// frame.c is generated from frame.1.c +// 21.08.2016 +// +// This is the wrapper for frame.pl. +// It's run with SETUID to have accesss to some files where the www server +// should not. That's why it has a C wrapper. In modern systems running scripts +// directly with SETUID is considered unsafe and not allowed. +// +// Copyright (C) 2016 Balthasar Szczepański +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include +#include + +###FRAME_PL; +###FRAME_PL_ERRLOG; + +int main(int argc, char *argv[], char *envp[]) +{ + freopen(FRAME_PL_ERRLOG,"at",stderr); + return execve(FRAME_PL,argv,envp); +} diff --git a/frame.1.pl b/frame.1.pl new file mode 100644 index 0000000..ad58411 --- /dev/null +++ b/frame.1.pl @@ -0,0 +1,154 @@ +###PERL; +# +# /bsta/f +# viewer.pl is generated from viewer.1.pl. +# 19.10.2016 +# +# The frame interface +# +# Copyright (C) 2016 Balthasar Szczepañski +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +use strict; +#use warnings; +###LIB; + +use bsta_lib qw(failpage gethttpheader getcgi readdatafile); + +###SETTINGS_PATH; +###DEFAULT_PATH; +###DATA_PATH; +###STATE_PATH; +###NOACCESS_PATH; + +my %http; +my %cgi; +my %framedata; +my %default; +my %settings; +my %state; + +my $time = time(); +srand ($time-$$); + +my $method; +my $frame; +my $password; +my $passwordOK; +my $IP; +my $access; +my $framepath; +my $framefile; +my $buffer; +my @fileinfo; + +delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; +###PATH; + +if ($ENV{'REQUEST_METHOD'} =~ /^(HEAD|GET|POST)$/) { + $method=$1; +} +else{ + exit failpage("Status: 405 Method Not Allowed\nAllow: GET, POST, HEAD\n","405 Method Not Allowed","The interface does not support the $ENV{'REQUEST_METHOD'} method.",$method); +} + +%http = gethttpheader (\%ENV); +%cgi = getcgi($ENV{'QUERY_STRING'}); + +if ($method eq 'POST') { + if ($http{'content-type'} eq 'application/x-www-form-urlencoded') { + my %cgipost=getcgi( ); + foreach my $ind (keys %cgipost) { + $cgi{$ind}=$cgipost{$ind}; + } + } + # multipart not supported + else{ + exit failpage("Status: 415 Unsupported Media Type\n","415 Unsupported Media Type","Unsupported Content-type: $http{'content-type'}."); + } +} + +if ($cgi{'f'} =~ /^(.+)$/) { + $frame=int($1); +} +elsif ($ENV{'PATH_INFO'} =~ /^\/(.+)$/) { + $frame=int($1); +} +else { + $frame = 0; +} + +if ($cgi{'p'} =~ /^(.+)$/) { + $password=$1; +} +else { + $password=''; +} + +%settings=readdatafile(SETTINGS_PATH); +%default=readdatafile(DEFAULT_PATH); +%state=readdatafile(STATE_PATH); +if($frame<0) { + $frame = $state{'last'} + $frame +1; +} +%framedata=readdatafile(DATA_PATH.$frame); +foreach my $ind (keys %default) { + unless(defined($framedata{$ind})){ + $framedata{$ind}=$default{$ind}; + } +} +if($password eq $settings{'password'}){ + $passwordOK = 1; +} +else{ + $passwordOK = 0; +} + +if ($passwordOK || (int($state{'state'}) >= 1 && $frame <= int($state{'last'}) && $frame > 0)) { + $access=1; +} +else { + $access=0; + %framedata = readdatafile(NOACCESS_PATH); + foreach my $ind (keys %default) { + unless(defined($framedata{$ind})){ + $framedata{$ind}=$default{$ind}; + } + } +} + +if($access){ + $framepath=DATA_PATH.sprintf($settings{'frame'},$frame,$framedata{'ext'}); +} +else { + $framepath=DATA_PATH.$framedata{'frame'}; +} + +open($framefile,'<',$framepath) or exit failpage("Status: 404 Not Found\n","404 Not Found"," Can't open image file."); +unless(binmode($framefile)) { + close($framefile); + exit failpage("Status: 500 Internal Server Error\n","500 Internal Server Error"," Can't switch to binary mode."); +} +if (my @fileinfo = stat($framepath)){ + print 'Content-length: '.$fileinfo[7]."\n"; +} +print 'Content-type: '.$framedata{'content-type'}."\n"; +print "\n"; +if($method ne 'HEAD'){ + while (read ($framefile,$buffer,1024)) { + print (STDOUT $buffer); + } +} +close($framefile); diff --git a/goto.1.c b/goto.1.c new file mode 100644 index 0000000..110e0b7 --- /dev/null +++ b/goto.1.c @@ -0,0 +1,34 @@ +// info.c is generated from info.1.c +// 29.06.2017 +// +// This is the wrapper for info.pl. +// It's run with SETUID to have accesss to some files where the www server +// should not. That's why it has a C wrapper. In modern systems running scripts +// directly with SETUID is considered unsafe and not allowed. +// +// Copyright (C) 2017 Balthasar Szczepański +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include +#include + +###GOTO_PL; +###GOTO_PL_ERRLOG; + +int main(int argc, char *argv[], char *envp[]) +{ + freopen(GOTO_PL_ERRLOG,"at",stderr); + return execve(GOTO_PL,argv,envp); +} diff --git a/goto.1.pl b/goto.1.pl new file mode 100644 index 0000000..dcc2072 --- /dev/null +++ b/goto.1.pl @@ -0,0 +1,172 @@ +###PERL; +# +# /bsta/g +# goto.pl is generated from goto.1.pl. +# 02.07.2017 +# +# The frame list +# +# Copyright (C) 2017 Balthasar Szczepański +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +use strict; +#use warnings; +###LIB; +use bsta_lib qw(failpage gethttpheader getcgi readdatafile printdatafile entityencode urlencode); +use File::Copy; + +###DATA_PATH; +###SETTINGS_PATH; +###STATE_PATH; +###LIST_PATH; +###WEBSITE_NAME; +###FAVICON_PATH; +###CSS_PATH; +###LOGO_PATH; +###WEBSITE; +###CGI_PATH; +###VIEWER_PATH; + + +my %http; +my %cgi; +my %settings; +my %state; +my %gotolist; + +my @timetab; + +my $time = time(); +srand ($time-$$); + +my $method; +my $password; +my $passwordOK; + +my $frame; +my $last; +my $title; +my $ongtime; +my $ongstate; +my $line; + +delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; +###PATH; + +if ($ENV{'REQUEST_METHOD'} =~ /^(HEAD|GET|POST)$/) { + $method=$1; +} +else{ + exit failpage("Status: 405 Method Not Allowed\nAllow: GET, POST, HEAD\n","405 Method Not Allowed","The interface does not support the $ENV{'REQUEST_METHOD'} method.",$method); +} + +%http = gethttpheader (\%ENV); +%cgi = getcgi($ENV{'QUERY_STRING'}); + +if ($method eq 'POST') { + if ($http{'content-type'} eq 'application/x-www-form-urlencoded') { + my %cgipost=getcgi( ); + foreach my $ind (keys %cgipost) { + $cgi{$ind}=$cgipost{$ind}; + } + } + # multipart not supported + else{ + exit failpage("Status: 415 Unsupported Media Type\n","415 Unsupported Media Type","Unsupported Content-type: $http{'content-type'}."); + } +} + +if ($cgi{'p'} =~ /^(.+)$/) { + $password=$1; +} +else { + $password=''; +} + +%settings=readdatafile(SETTINGS_PATH); +%state=readdatafile(STATE_PATH); +%gotolist=readdatafile(LIST_PATH); + +if($password eq $settings{'password'}){ + $passwordOK = 1; +} +else{ + $passwordOK = 0; +} + +print "Content-type: text/html\n"; +print "\n"; +if($method eq 'HEAD') { + exit; +} + +print ''."\n"; +print ''."\n"; +print 'GOTO • '.entityencode($settings{'story'}).' • '.WEBSITE_NAME.''."\n"; +print ''."\n"; +print ''."\n"; +print ''."\n"; +print ''."\n"; +print ''."\n"; +print '
    '."\n"; + +print '
    '."\n"; + +print '
    '."\n"; +print '

    '.entityencode($settings{'story'}).'

    '."\n"; +print '
    '."\n"; + +print '
    '."\n"; + +print '
    '."\n"; + +$last=int($state{'last'}); +$ongstate=int($state{'state'}); +for ($frame=0; ; ++$frame) { + if((($frame > $last) || $ongstate<1) && !$passwordOK) { + last; + } + + $ongtime=$gotolist{'ongtime-'.$frame}; + $title=$gotolist{'title-'.$frame}; + if ($ongtime eq '') { + last; + } + @timetab=gmtime($ongtime); + + print ''.sprintf('%03d',$frame).' '.sprintf('%02d.%02d.%02d %02d:%02d',$timetab[3],$timetab[4]+1,$timetab[5]-100,$timetab[2],$timetab[1]).' '.entityencode($title).'
    '."\n"; +} + +print '
    '."\n"; + +print ''."\n"; + +print '
    '."\n"; + +print '
    '."\n"; +print ''.WEBSITE.''."\n"; + + +print ''."\n"; + + + + + + diff --git a/info.1.c b/info.1.c new file mode 100644 index 0000000..7f55f1e --- /dev/null +++ b/info.1.c @@ -0,0 +1,34 @@ +// info.c is generated from info.1.c +// 02.02.2017 +// +// This is the wrapper for info.pl. +// It's run with SETUID to have accesss to some files where the www server +// should not. That's why it has a C wrapper. In modern systems running scripts +// directly with SETUID is considered unsafe and not allowed. +// +// Copyright (C) 2017 Balthasar Szczepański +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include +#include + +###INFO_PL; +###INFO_PL_ERRLOG; + +int main(int argc, char *argv[], char *envp[]) +{ + freopen(INFO_PL_ERRLOG,"at",stderr); + return execve(INFO_PL,argv,envp); +} diff --git a/info.1.pl b/info.1.pl new file mode 100644 index 0000000..8e16798 --- /dev/null +++ b/info.1.pl @@ -0,0 +1,181 @@ +###PERL; +# +# /bsta/i +# info.pl is generated from info.1.pl. +# 05.06.2017 +# +# The frame/story info interface +# +# Copyright (C) 2017 Balthasar Szczepański +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +use strict; +#use warnings; +###LIB; +use bsta_lib qw(failpage gethttpheader getcgi readdatafile printdatafile); +use File::Copy; + +###DATA_PATH; +###DEFAULT_PATH; +###SETTINGS_PATH; +###STATE_PATH; +###NOACCESS_PATH; + +my %http; +my %cgi; +my %framedata; +my %nextframedata; +my %default; +my %settings; +my %state; + +my $time = time(); +srand ($time-$$); + +my $method; +my $frame; +my $password; +my $passwordOK; +my $access; +my $showcommand; +my $ongtime; +my $seconds; + +delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; +###PATH; + +if ($ENV{'REQUEST_METHOD'} =~ /^(HEAD|GET|POST)$/) { + $method=$1; +} +else{ + exit failpage("Status: 405 Method Not Allowed\nAllow: GET, POST, HEAD\n","405 Method Not Allowed","The interface does not support the $ENV{'REQUEST_METHOD'} method.",$method); +} + +%http = gethttpheader (\%ENV); +%cgi = getcgi($ENV{'QUERY_STRING'}); + +if ($method eq 'POST') { + if ($http{'content-type'} eq 'application/x-www-form-urlencoded') { + my %cgipost=getcgi( ); + foreach my $ind (keys %cgipost) { + $cgi{$ind}=$cgipost{$ind}; + } + } + # multipart not supported + else{ + exit failpage("Status: 415 Unsupported Media Type\n","415 Unsupported Media Type","Unsupported Content-type: $http{'content-type'}."); + } +} + +if ($cgi{'f'} =~ /^(.+)$/) { + $frame=int($1); +} +elsif ($ENV{'PATH_INFO'} =~ /^\/(.+)$/) { + $frame=int($1); +} +else { + $frame = ''; +} + +if ($cgi{'p'} =~ /^(.+)$/) { + $password=$1; +} +else { + $password=''; +} + +%settings=readdatafile(SETTINGS_PATH); +%default=readdatafile(DEFAULT_PATH); +%framedata=readdatafile(DATA_PATH.$frame); +%state=readdatafile(STATE_PATH); +if($password eq $settings{'password'}){ + $passwordOK = 1; +} +else{ + $passwordOK = 0; +} + +if ($frame eq '') { + unless($passwordOK) { + if ($state{'ip1'} ne '') { + $state{'ip1'}=1; + } + if ($state{'ip2'} ne '') { + $state{'ip2'}=1; + } + if ($state{'ip3'} ne '') { + $state{'ip3'}=1; + } + } + print "Content-type: text/plain\n\n"; + if($method eq 'HEAD') { + exit; + } + printdatafile(%state); +} + +else { + if($frame<0) { + $frame = int($state{'last'}) + $frame +1; + %framedata=readdatafile(DATA_PATH.$frame); + } + + %nextframedata=readdatafile(DATA_PATH.($frame+1)); + + foreach my $ind (keys %default) { + unless(defined($framedata{$ind})){ + $framedata{$ind}=$default{$ind}; + } + unless(defined($nextframedata{$ind})){ + $nextframedata{$ind}=$default{$ind}; + } + } + $seconds=int($state{'nextong'})-$time; + $ongtime=int($state{'ongtime'}); + if($ongtime == 0) { + $ongtime=int($settings{'ongtime'}) + } + $showcommand = ($seconds < ($ongtime*3600/3)); + + if ($passwordOK || (int($state{'state'}) >= 1 && $frame <= int($state{'last'}) && $frame >= 0)) { + $access=1; + + if ($passwordOK || $frame= 2 && $showcommand)) { + $framedata{'command'}=$nextframedata{'title'}; + } + $framedata{'frame'}=sprintf($settings{'frame'},$frame,$framedata{'ext'}); + } + else { + $access=0; + %framedata = readdatafile(NOACCESS_PATH); + foreach my $ind (keys %default) { + unless(defined($framedata{$ind})){ + $framedata{$ind}=$default{$ind}; + } + } + } + + # $framedata{'frame'}=sprintf($settings{'frame'},$frame,$framedata{'ext'}); + + print "Content-type: text/plain\n"; + if(!$access) { + print "Status: 403 Forbidden\n"; + } + print "\n"; + if($method eq 'HEAD') { + exit; + } + printdatafile(%framedata); +} diff --git a/make.sh b/make.sh new file mode 100755 index 0000000..38d663e --- /dev/null +++ b/make.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +# make.sh +# 13.01.2016 +# +# This is the script for making the software. Normally, the makefile is used for +# this purpose. But the makefile has to be generated first. +# This script generates the makefile and then uses it and finally removes it. +# +# Copyright (C) 2015-2016 Balthasar Szczepański +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +set -x +perl configure.pl settings makefile +make +# rm makefile diff --git a/makefile b/makefile new file mode 100644 index 0000000..ad0f7d2 --- /dev/null +++ b/makefile @@ -0,0 +1,151 @@ +# makefile is generated from makefile.1.mak. +# 2.07.2017 +# +# This is the makefile +# +# Copyright (C) 2016 - 2017 Balthasar Szczepañski +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +CC=/usr/bin/gcc +CF=-g -Wall +PL=/usr/bin/perl +CP=/bin/cp +MV=/bin/mv +RM=/bin/rm +CM=/bin/chmod +OD=/eizm/bin/bsta/ +LD=/eizm/lib/bsta/ +WD=/eizm/www/time/bsta/ + +all: moveout copyoutwww moveoutlib remove config.txt + +moveout: viewer viewer.pl frame frame.pl 2words 2words.pl chat chat.pl ong.pl updlist.pl oldlogs.pl attach attach.pl info info.pl goto goto.pl bbcode bbcode.pl setuid exec + $(MV) viewer viewer.pl frame frame.pl 2words 2words.pl chat chat.pl ong.pl updlist.pl oldlogs.pl attach attach.pl info info.pl goto goto.pl bbcode bbcode.pl $(OD) + +copyout: setuid exec +# $(CP) access.pl $(OD) + +copyoutwww: timer.js bsta.css setuid exec + $(CP) timer.js bsta.css $(WD) + + +moveoutlib: bsta_lib.pm setuid exec + $(MV) bsta_lib.pm $(LD) + +setuid: viewer frame 2words attach chat info goto bbcode + $(CM) u+s viewer frame 2words attach chat info goto bbcode + +exec: viewer.pl frame.pl 2words.pl ong.pl updlist.pl oldlogs.pl attach.pl chat.pl info.pl goto.pl bbcode.pl + $(CM) +x viewer.pl frame.pl 2words.pl ong.pl updlist.pl oldlogs.pl attach.pl chat.pl info.pl goto.pl bbcode.pl + +remove: viewer.c frame.c 2words.c chat.c attach.c info.c goto.c bbcode.c copyout moveout setuid exec + $(RM) viewer.c frame.c 2words.c chat.c attach.c info.c goto.c bbcode.c + + +bsta_lib.pm: bsta_lib.1.pm configure.pl settings + $(PL) configure.pl settings bsta_lib.pm + + +viewer: viewer.c + $(CC) $(CF) -o viewer viewer.c + +viewer.c: viewer.1.c configure.pl settings + $(PL) configure.pl settings viewer.c + +viewer.pl: viewer.1.pl configure.pl settings + $(PL) configure.pl settings viewer.pl + + +frame: frame.c + $(CC) $(CF) -o frame frame.c + +frame.c: frame.1.c configure.pl settings + $(PL) configure.pl settings frame.c + +frame.pl: frame.1.pl configure.pl settings + $(PL) configure.pl settings frame.pl + + +2words: 2words.c + $(CC) $(CF) -o 2words 2words.c + +2words.c: 2words.1.c configure.pl settings + $(PL) configure.pl settings <2words.1.c >2words.c + +2words.pl: 2words.1.pl configure.pl settings + $(PL) configure.pl settings <2words.1.pl >2words.pl + + +attach: attach.c + $(CC) $(CF) -o attach attach.c + +attach.c: attach.1.c configure.pl settings + $(PL) configure.pl settings attach.c + +attach.pl: attach.1.pl configure.pl settings + $(PL) configure.pl settings attach.pl + + +chat: chat.c + $(CC) $(CF) -o chat chat.c + +chat.c: chat.1.c configure.pl settings + $(PL) configure.pl settings chat.c + +chat.pl: chat.1.pl configure.pl settings + $(PL) configure.pl settings chat.pl + + +info: info.c + $(CC) $(CF) -o info info.c + +info.c: info.1.c configure.pl settings + $(PL) configure.pl settings info.c + +info.pl: info.1.pl configure.pl settings + $(PL) configure.pl settings info.pl + +goto: goto.c + $(CC) $(CF) -o goto goto.c + +goto.c: goto.1.c configure.pl settings + $(PL) configure.pl settings goto.c + +goto.pl: goto.1.pl configure.pl settings + $(PL) configure.pl settings goto.pl + + +bbcode: bbcode.c + $(CC) $(CF) -o bbcode bbcode.c + +bbcode.c: bbcode.1.c configure.pl settings + $(PL) configure.pl settings bbcode.c + +bbcode.pl: bbcode.1.pl configure.pl settings + $(PL) configure.pl settings bbcode.pl + + +ong.pl: ong.1.pl configure.pl settings + $(PL) configure.pl settings ong.pl + +updlist.pl: updlist.1.pl configure.pl settings + $(PL) configure.pl settings updlist.pl + +oldlogs.pl: oldlogs.1.pl configure.pl settings + $(PL) configure.pl settings oldlogs.pl + + +config.txt: config.1.txt configure.pl settings + $(PL) configure.pl settings config.txt diff --git a/makefile.1.mak b/makefile.1.mak new file mode 100644 index 0000000..cdeb46e --- /dev/null +++ b/makefile.1.mak @@ -0,0 +1,156 @@ +# makefile is generated from makefile.1.mak. +# 2.07.2017 +# +# This is the makefile +# +# Copyright (C) 2016 - 2017 Balthasar Szczepañski +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +###CC; +###CF; +###PL; +###CP; +###MV; +###RM; +###CM; +###OD; +###LD; +###WD; + +all: moveout copyoutwww moveoutlib remove config.txt + +moveout: reset.pl viewer viewer.pl frame frame.pl 2words 2words.pl chat chat.pl ong.pl updlist.pl oldlogs.pl attach attach.pl info info.pl goto goto.pl bbcode bbcode.pl setuid exec + $(MV) reset.pl viewer viewer.pl frame frame.pl 2words 2words.pl chat chat.pl ong.pl updlist.pl oldlogs.pl attach attach.pl info info.pl goto goto.pl bbcode bbcode.pl $(OD) + +copyout: setuid exec +# $(CP) access.pl $(OD) + +copyoutwww: timer.js bsta.css setuid exec + $(CP) timer.js bsta.css $(WD) + + +moveoutlib: bsta_lib.pm setuid exec + $(MV) bsta_lib.pm $(LD) + +setuid: viewer frame 2words attach chat info goto bbcode + $(CM) u+s viewer frame 2words attach chat info goto bbcode + +exec: reset.pl viewer.pl frame.pl 2words.pl ong.pl updlist.pl oldlogs.pl attach.pl chat.pl info.pl goto.pl bbcode.pl + $(CM) +x reset.pl viewer.pl frame.pl 2words.pl ong.pl updlist.pl oldlogs.pl attach.pl chat.pl info.pl goto.pl bbcode.pl + +remove: viewer.c frame.c 2words.c chat.c attach.c info.c goto.c bbcode.c copyout moveout setuid exec + $(RM) viewer.c frame.c 2words.c chat.c attach.c info.c goto.c bbcode.c + + +bsta_lib.pm: bsta_lib.1.pm configure.pl settings + $(PL) configure.pl settings bsta_lib.pm + + +viewer: viewer.c + $(CC) $(CF) -o viewer viewer.c + +viewer.c: viewer.1.c configure.pl settings + $(PL) configure.pl settings viewer.c + +viewer.pl: viewer.1.pl configure.pl settings + $(PL) configure.pl settings viewer.pl + + +frame: frame.c + $(CC) $(CF) -o frame frame.c + +frame.c: frame.1.c configure.pl settings + $(PL) configure.pl settings frame.c + +frame.pl: frame.1.pl configure.pl settings + $(PL) configure.pl settings frame.pl + + +2words: 2words.c + $(CC) $(CF) -o 2words 2words.c + +2words.c: 2words.1.c configure.pl settings + $(PL) configure.pl settings <2words.1.c >2words.c + +2words.pl: 2words.1.pl configure.pl settings + $(PL) configure.pl settings <2words.1.pl >2words.pl + + +attach: attach.c + $(CC) $(CF) -o attach attach.c + +attach.c: attach.1.c configure.pl settings + $(PL) configure.pl settings attach.c + +attach.pl: attach.1.pl configure.pl settings + $(PL) configure.pl settings attach.pl + + +chat: chat.c + $(CC) $(CF) -o chat chat.c + +chat.c: chat.1.c configure.pl settings + $(PL) configure.pl settings chat.c + +chat.pl: chat.1.pl configure.pl settings + $(PL) configure.pl settings chat.pl + + +info: info.c + $(CC) $(CF) -o info info.c + +info.c: info.1.c configure.pl settings + $(PL) configure.pl settings info.c + +info.pl: info.1.pl configure.pl settings + $(PL) configure.pl settings info.pl + + +goto: goto.c + $(CC) $(CF) -o goto goto.c + +goto.c: goto.1.c configure.pl settings + $(PL) configure.pl settings goto.c + +goto.pl: goto.1.pl configure.pl settings + $(PL) configure.pl settings goto.pl + + +bbcode: bbcode.c + $(CC) $(CF) -o bbcode bbcode.c + +bbcode.c: bbcode.1.c configure.pl settings + $(PL) configure.pl settings bbcode.c + +bbcode.pl: bbcode.1.pl configure.pl settings + $(PL) configure.pl settings bbcode.pl + + +ong.pl: ong.1.pl configure.pl settings + $(PL) configure.pl settings ong.pl + +updlist.pl: updlist.1.pl configure.pl settings + $(PL) configure.pl settings updlist.pl + +oldlogs.pl: oldlogs.1.pl configure.pl settings + $(PL) configure.pl settings oldlogs.pl + +reset.pl: reset.1.pl configure.pl settings + $(PL) configure.pl settings reset.pl + + + +config.txt: config.1.txt configure.pl settings + $(PL) configure.pl settings config.txt diff --git a/oldlogs.1.pl b/oldlogs.1.pl new file mode 100644 index 0000000..67fca65 --- /dev/null +++ b/oldlogs.1.pl @@ -0,0 +1,119 @@ +###PERL; + +# oldlogs is generated from oldlogs.1.pl. +# 02.01.2016 +# +# This script renames log files if they are big enough. +# Compresses or removes older log files. +# +# Copyright (C) 2015-2016 Balthasar Szczepański +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + + +###GZIP_PATH; +###LOG_PATH; +###LOG_SIZE_LIMIT; +###LOGS_TOTAL; +###LOGS_UNCOMPRESSED; + +if ($ARGV[0] ne '') { + $log_path = $ARGV[0]; +} +else { + $log_path = LOG_PATH; +} +if ($ARGV[1] =~ /^([0-9]+)$/) { + $log_size_limit = $1; +} +else { + $log_size_limit = LOG_SIZE_LIMIT; +} +if ($ARGV[2] =~ /^([0-9]+)$/) { + $logs_total = $1; +} +else { + $logs_total = LOGS_TOTAL; +} +if ($ARGV[3] =~ /^([0-9]+)$/) { + $logs_uncompressed = $1; +} +else { + $logs_uncompressed = LOGS_UNCOMPRESSED; +} + +if ( opendir ($dir, $log_path)) { + while ($subpath = readdir $dir) { + if ($subpath !~ /\.log$/) { + next; + } + $fullpath=$log_path.$subpath; + unless (-f $fullpath) { + next; + } + unless (@stat = stat($fullpath)) { + next; + } + if ($stat[7] > $log_size_limit) { + movelog($fullpath,0,0); + } + + } + closedir($dir); +} + +sub movelog { + (my $path, my $number, my $gz) = @_; + my $nextgz = 0; + my $thispath; + my $nextpath; + my $nextnumber=$number+1; + my @gzip_arg = (GZIP_PATH, '-q', '-9','-f'); + + $thispath = $path.(($number != 0)?'.'.$number.($gz?'.gz':''):''); + if ($number == $logs_total) { + if (unlink $thispath) { + return 1; + } + else { + return 0; + } + } + if ($number == $logs_uncompressed) { + $nextgz=1; + $nextpath = $path.'.'.$nextnumber.'.gz'; + } + else { + $nextpath = $path.'.'.$nextnumber.($gz?'.gz':''); + } + + if (-e $nextpath) { + unless (movelog($path,$nextnumber,($nextgz or $gz)?1:0)) { + return 0; + } + } + + if ($nextgz) { + push @gzip_arg, $thispath; + unless (! system (@gzip_arg)) { + return 0; + } + $thispath .= '.gz'; + } + + unless (rename ($thispath, $nextpath)) { + return 0; + } + return 1; +} diff --git a/ong.1.pl b/ong.1.pl new file mode 100644 index 0000000..8da5522 --- /dev/null +++ b/ong.1.pl @@ -0,0 +1,155 @@ +###PERL; +# +# ong.pl is generated from ong.1.pl. +# 29.06.2017 +# +# The ONG bot +# +# Copyright (C) 2016-2017 Balthasar Szczepański +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +use strict; +#use warnings; +###LIB; +use bsta_lib qw(entityencode readdatafile writedatafile urlencode); +use File::Copy; + +###DATA_PATH; +###WWW_PATH; +###SETTINGS_PATH; +###STATE_PATH; +###DEFAULT_PATH; +###LIST_PATH; + +my %framedata; +my %nextframedata; +my %default; +my %settings; +my %state; +my %gotolist; + +my $statefile; +my $ongstate; +my $frame; +my $nextong; +my $inpath; +my $outpath; +my $ongtime; +my $static; +my $dynamic; +my $last; + +my $time = time(); +srand ($time-$$); +$ongtime = int($time / 3600) * 3600; + +print $time.' - '.$ongtime."\n"; + +delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; +###PATH; + + +if (open ($statefile,"+<",STATE_PATH)){ + if (flock($statefile,2)) { + %state=readdatafile($statefile); + $ongstate=int($state{'state'}); + print 'state: '.$ongstate."\n"; + if($ongstate > 0) { + $nextong = int($state{'nextong'}); + print 'ongtime: '.$nextong."\n"; + + if($ongtime >= $nextong) { + %settings=readdatafile(SETTINGS_PATH); + $static=int($settings{'ongtime'}); + $dynamic=int($settings{'dynamicongtime'}); + $last=int($settings{'last'}); + $frame=int($state{'last'})+1; + + if($dynamic > 0 && $frame < $last) { + $dynamic = int($dynamic/($last-$frame)); + } + else { + $dynamic=0; + } + + if($static>$dynamic){ + $dynamic=$static; + } + $nextong=$ongtime+($dynamic*3600); + $state{'nextong'}=$nextong; + print 'next ongtime: '.$nextong.' (+'.$dynamic.")\n"; + $state{'ongtime'}=$dynamic; + + if($ongstate == 2) { + print 'next frame: '.$frame."\n"; + %framedata=readdatafile(DATA_PATH.$frame); + %default=readdatafile(DEFAULT_PATH); + %gotolist=readdatafile(LIST_PATH); + + $framedata{'ongtime'}=$time; + $framedata{'timer'}=$dynamic; + $gotolist{'title-'.$frame}=$framedata{'title'}; + $gotolist{'ongtime-'.$frame}=$framedata{'ongtime'}; + writedatafile(DATA_PATH.$frame,%framedata); + writedatafile(LIST_PATH,%gotolist); + + foreach my $ind (keys %default) { + unless(defined($framedata{$ind})){ + $framedata{$ind}=$default{$ind}; + } + unless(defined($nextframedata{$ind})){ + $nextframedata{$ind}=$default{$ind}; + } + } + + $inpath = DATA_PATH.sprintf($settings{'frame'},$frame,$framedata{'ext'}); + $outpath = WWW_PATH.sprintf($settings{'frame'},$frame,$framedata{'ext'}); + + print $inpath.' -> '.$outpath."\n"; + + # ALSO-ATTACHMENTS!!! (here) + + if(copy ($inpath, $outpath)) { + $state{'last'}=$frame; + $state{'state'}=1; + $state{'ip1'}=''; + $state{'ip2'}=''; + $state{'ip3'}=''; + $state{'ongtime'}=$dynamic; + print "ONG\n"; + } + else { + print "NO ONG\n"; + } + } + writedatafile($statefile,%state); + } + else { + print "WAIT\n"; + } + } + else { + print "INACTIVE\n"; + } + } + else { + print "NO STATELOCK\n"; + } + close ($statefile); +} +else { + print "NO STATEFILE\n"; +} +print "\n"; \ No newline at end of file diff --git a/ongtimes.pl b/ongtimes.pl new file mode 100755 index 0000000..e44ff45 --- /dev/null +++ b/ongtimes.pl @@ -0,0 +1,49 @@ +#!/usr/bin/perl +# 01.02.2017 + +use strict; +#use warnings; +use lib '/eizm/lib/bstatest/'; +use bsta_lib qw(readdatafile writedatafile); + +use constant DATA_PATH => '/eizm/data/bsta/'; +use constant SETTINGS_PATH => '/eizm/data/bsta/settings'; +use constant STATE_PATH => '/eizm/data/bsta/state'; + +my %framedata; +my %settings; +my %state; + +my $last; +my $ongtime; +my @timetab; +my $timer; +my $prevongtime; +my $cleantimer; +my $nicetime; + +%state=readdatafile(STATE_PATH); +$last=int($state{'last'}); + +for (my $f=$last; $f>=0; --$f) { + %framedata=readdatafile(DATA_PATH.$f); $ongtime=int($framedata{'ongtime'}); + @timetab=gmtime($ongtime); + $nicetime=sprintf('UTC %04d.%02d.%02d %02d:%02d:%02d',$timetab[5]+1900,$timetab[4]+1,$timetab[3],$timetab[2],$timetab[1],$timetab[0]); + + if($framedata{'timer'}ne'') { + $timer=int($framedata{'timer'}); + $cleantimer=$timer; + } + # elsif($f<$last) { + # $timer=($prevongtime-$ongtime)/3600; + # $cleantimer=0-int($timer+0.5); + # $framedata{'timer'}=0-$cleantimer; + # writedatafile(DATA_PATH.$f,%framedata); + # } + + print "$f\t$nicetime\t$ongtime\t\t$timer\n"; + $prevongtime=$ongtime; +} +for (my $f=0; $f<=$last; ++$f) { + +} \ No newline at end of file diff --git a/reset.1.pl b/reset.1.pl new file mode 100644 index 0000000..353814b --- /dev/null +++ b/reset.1.pl @@ -0,0 +1,116 @@ +###PERL; +# +# /bsta/2words +# reset is generated from reset.1.pl. +# 26.03.2018 +# +# Reset BSTA state +# +# Copyright (C) 2016 - 2018 Balthasar Szczepański +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +use strict; +#use warnings; +###LIB; +use bsta_lib qw(writedatafile); + +###STORY_PATH; +###WEBSITE; +###CGI_PATH; +###INDEX_PATH; +###INTF_DATE; +###STATE_PATH; +###COIN_DATE; +###CHAT_PATH; + +my %story = ( + 'id' => '0', + 'letter' => '', + 'lastip' => '0.0.0.0', + 'content' => '', + 'pass' => '0', + 'state' => '0' +); +my %state = ( + 'state' => '0', + 'last' => '0', + 'ongtime' => '', + 'nextong' => '', + 'ip1' => '', + 'ip2' => '', + 'ip3' => '' +); +my %chat = ( + 'id' => '0', + 'state' => '0', + 'content' => '' +); + +delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; +###PATH; + +writedatafile(STATE_PATH,%state); +writedatafile(STORY_PATH,%story); +writedatafile(CHAT_PATH,%chat); + +writeindex(INDEX_PATH); + +#function borrowed from 2words.pl - keep consistent! +sub writeindex { + (my $indexpath) = @_; + my $indexfile; + my $indexof; + + if(ref($indexpath)) { + $indexfile=$indexpath; + unless (seek($indexfile, 0, 0)) { + return 0; + } + } + else { + unless (open ($indexfile, ">", $indexpath)) { + return 0; + } + } + + $indexof = CGI_PATH; + $indexof =~ s/\/$//g; + + print $indexfile ''."\n"; + print $indexfile ''."\n"; + print $indexfile ' '."\n"; + print $indexfile ' Index of '.$indexof.''."\n"; + print $indexfile ' '."\n"; + print $indexfile ' '."\n"; + print $indexfile '

    Index of '.$indexof.'

    '."\n"; + print $indexfile ''."\n"; + print $indexfile ''."\n"; + print $indexfile ''."\n"; + print $indexfile ''."\n"; + print $indexfile ''."\n"; + print $indexfile '
    [ICO]NameLast modifiedSizeDescription

    [DIR]Parent Directory  -  
    [DIR]2words/'.INTF_DATE.' -  
    [DIR]coin/'.COIN_DATE.' - Coincidence

    '."\n"; + print $indexfile '
    Apache/2.2.22 (Debian) Server at '.WEBSITE.' Port 80
    '."\n"; + print $indexfile ''."\n"; + + + unless (ref($indexpath)) { + close ($indexfile); + } + else { + truncate ($indexfile , tell($indexfile)); + } + + return 1; +} diff --git a/settings b/settings new file mode 100644 index 0000000..65579ae --- /dev/null +++ b/settings @@ -0,0 +1,64 @@ +# settings +# 19.11.2016 +# +# In this file are defined values specific for the user's system +# +# Copyright (C) 2016 Balthasar Szczepański +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +#all directory paths must end with '/' and must already exist. + +bin_path = /eizm/bin/bsta/ #Where the software will be located +data_path = /eizm/data/bsta/ #where the software will remember data; subdir: + #group, groupsettings +log_path = /eizm/log/bsta/ #where the software will remember data +tmp_path = /eizm/tmp/bsta/ #for temporary fies +www_path = /eizm/www/time/bsta/ #for the www server +lib_path = /eizm/lib/bsta/ +cgi_path = /bsta/ + +path = /usr/local/bin:/usr/bin:/bin #The path environment variable. Must be + #overwritten if SETUID. Otherwise + #launching programs may fail. (Perl + #security...) + + +#paths to software +perl = /usr/bin/perl +chmod = /bin/chmod +cp = /bin/cp +mv = /bin/mv +rm = /bin/rm +gcc = /usr/bin/gcc +gzip = /bin/gzip +c_flags = -g -Wall + +log_size_limit = 65536 # How big can a log file be +logs_uncompressed = 2 # How many uncompressed old logs to keep +logs_total = 10 # How many old logs to keep + +rm_access_crontab = 10 2 * * * # How often to remove leftover unlock info. +oldlogs_crontab = 0 2 * * * # How often to deal with old logs +bot_crontab = 20 2 * * * # How often to run the bot + +website = 1190.bicyclesonthemoon.info +website_name = Bicycles on the Moon +favicon_path = /img/favicon.png +intf_date = 28-Sep-2016 20:34 +coin_date = 13-Nov-2016 22:15 + +story_length = 16 +firstpage_length = 4 +page_length = 16 diff --git a/settings-again b/settings-again new file mode 100644 index 0000000..ee732f9 --- /dev/null +++ b/settings-again @@ -0,0 +1,64 @@ +# settings +# 19.11.2016 +# +# In this file are defined values specific for the user's system +# +# Copyright (C) 2016 Balthasar Szczepański +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +#all directory paths must end with '/' and must already exist. + +bin_path = /eizm/bin/bstagain/ #Where the software will be located +data_path = /eizm/data/bstagain/ #where the software will remember data; subdir: + #group, groupsettings +log_path = /eizm/log/bstagain/ #where the software will remember data +tmp_path = /eizm/tmp/bstagain/ #for temporary fies +www_path = /eizm/www/time/bstagain/ #for the www server +lib_path = /eizm/lib/bstagain/ +cgi_path = /bstagain/ + +path = /usr/local/bin:/usr/bin:/bin #The path environment variable. Must be + #overwritten if SETUID. Otherwise + #launching programs may fail. (Perl + #security...) + + +#paths to software +perl = /usr/bin/perl +chmod = /bin/chmod +cp = /bin/cp +mv = /bin/mv +rm = /bin/rm +gcc = /usr/bin/gcc +gzip = /bin/gzip +c_flags = -g -Wall + +log_size_limit = 65536 # How big can a log file be +logs_uncompressed = 2 # How many uncompressed old logs to keep +logs_total = 10 # How many old logs to keep + +rm_access_crontab = 10 2 * * * # How often to remove leftover unlock info. +oldlogs_crontab = 0 2 * * * # How often to deal with old logs +bot_crontab = 20 2 * * * # How often to run the bot + +website = 1190.bicyclesonthemoon.info +website_name = Bicycles on the Moon +favicon_path = /img/favicon.png +intf_date = 28-Sep-2016 20:34 +coin_date = 13-Nov-2016 22:15 + +story_length = 16 +firstpage_length = 4 +page_length = 16 diff --git a/settings-test b/settings-test new file mode 100644 index 0000000..93bb2de --- /dev/null +++ b/settings-test @@ -0,0 +1,64 @@ +# settings +# 19.11.2016 +# +# In this file are defined values specific for the user's system +# +# Copyright (C) 2016 Balthasar Szczepański +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +#all directory paths must end with '/' and must already exist. + +bin_path = /eizm/bin/bstatest/ #Where the software will be located +data_path = /eizm/data/bstatest/ #where the software will remember data; subdir: + #group, groupsettings +log_path = /eizm/log/bstatest/ #where the software will remember data +tmp_path = /eizm/tmp/bstatest/ #for temporary fies +www_path = /eizm/www/time/tbst/ #for the www server +lib_path = /eizm/lib/bstatest/ +cgi_path = /tbst/ + +path = /usr/local/bin:/usr/bin:/bin #The path environment variable. Must be + #overwritten if SETUID. Otherwise + #launching programs may fail. (Perl + #security...) + + +#paths to software +perl = /usr/bin/perl +chmod = /bin/chmod +cp = /bin/cp +mv = /bin/mv +rm = /bin/rm +gcc = /usr/bin/gcc +gzip = /bin/gzip +c_flags = -g -Wall + +log_size_limit = 65536 # How big can a log file be +logs_uncompressed = 2 # How many uncompressed old logs to keep +logs_total = 10 # How many old logs to keep + +rm_access_crontab = 10 2 * * * # How often to remove leftover unlock info. +oldlogs_crontab = 0 2 * * * # How often to deal with old logs +bot_crontab = 20 2 * * * # How often to run the bot + +website = 1190.bicyclesonthemoon.info +website_name = Bicycles on the Moon +favicon_path = /img/favicon.png +intf_date = 28-Sep-2016 20:34 +coin_date = 13-Nov-2016 22:15 + +story_length = 16 +firstpage_length = 4 +page_length = 16 diff --git a/testhtml.pl b/testhtml.pl new file mode 100644 index 0000000..97f72fb --- /dev/null +++ b/testhtml.pl @@ -0,0 +1,15 @@ +#!/usr/bin/perl +use lib '/eizm/lib/bstatest/'; +use strict; +use bsta_lib qw(failpage gethttpheader getcgi entityencode readdatafile writedatafile printdatafileht urlencode bb2ht bb2bb linehtml); + +my $bb; +my $ht; + +$bb="[fq][/fq][tq][/tq][ni][/ni][br]
    [/br][po][/po][url=][/url][i][/i]\n"; +$ht=bb2bb($bb,1); +print $ht."\n"; +$ht=bb2ht($bb,1); +print $ht."\n"; + +# print ht; diff --git a/timer.js b/timer.js new file mode 100644 index 0000000..7ca7807 --- /dev/null +++ b/timer.js @@ -0,0 +1,85 @@ +// timer.js +// 3.07.2017 +// +// The countdown script. +// +// Copyright (C) 2017 Balthasar Szczepański +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +window.onload = function () { + var ongh; + var ongm; + var ongs; + var ongvh; + var ongvm; + var ongvs; + var timer; + + ongh = document.getElementById("ongh"); + ongm = document.getElementById("ongm"); + ongs = document.getElementById("ongs"); + + if(ongh == null || ongm == null || ongs == null) { + // window.alert("NUL"); + } + else { + timer = setInterval (function() { + ongvh = +(ongh.innerHTML); + ongvm = +(ongm.innerHTML); + ongvs = +(ongs.innerHTML); + + if(isNaN(ongvh) || isNaN(ongvm) || isNaN(ongvs)) { + // window.alert("NAN"); + clearInterval(timer); + return; + } + + if(ongvs == 0) { + ongvs = 59; + if(ongvm == 0) { + ongvm == 59; + if(ongvh ==0) { + ongvm = 0; + ongvs = 0; + } + else { + ongvh -= 1; + } + } + else { + ongvm -= 1; + } + } + else { + ongvs -= 1; + } + + if(ongvh == 0 && ongvm == 0 && ongvs == 0) { + ongh.innerHTML = "00"; + ongm.innerHTML = "00"; + ongs.innerHTML = "NG"; + // window.alert("ONG"); + clearInterval(timer); + return; + } + else { + ongh.innerHTML = ((ongvh<10)?"0":"")+ongvh; + ongm.innerHTML = ((ongvm<10)?"0":"")+ongvm; + ongs.innerHTML = ((ongvs<10)?"0":"")+ongvs; + } + + }, 1000); + } +}; diff --git a/updlist.1.pl b/updlist.1.pl new file mode 100644 index 0000000..8e8e6a2 --- /dev/null +++ b/updlist.1.pl @@ -0,0 +1,55 @@ +###PERL; +# +# updlist.pl is generated from updlist.1.pl. +# 29.06.2017 +# +# The framelist update bot +# +# Copyright (C) 2016-2017 Balthasar Szczepański +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +use strict; +#use warnings; +###LIB; +use bsta_lib qw(readdatafile writedatafile); +use File::Copy; + +###DATA_PATH; +###WWW_PATH; +###SETTINGS_PATH; +###STATE_PATH; +###DEFAULT_PATH; +###LIST_PATH; + +my %framedata; +my %gotolist; + +my $frame; + +%gotolist=readdatafile(LIST_PATH); + +for($frame=0; ; ++$frame) { + %framedata=readdatafile(DATA_PATH.$frame); + if($framedata{'ongtime'} eq '') { + last; + } + + print $frame.' '.$framedata{'ongtime'}.' '.$framedata{'title'}."\n"; + + $gotolist{'title-'.$frame}=$framedata{'title'}; + $gotolist{'ongtime-'.$frame}=$framedata{'ongtime'}; +} + +writedatafile (LIST_PATH,%gotolist); diff --git a/viewer.1.c b/viewer.1.c new file mode 100644 index 0000000..2a3f1a6 --- /dev/null +++ b/viewer.1.c @@ -0,0 +1,34 @@ +// viewer.c is generated from viewer.1.c +// 20.08.2016 +// +// This is the wrapper for viewer.pl. +// It's run with SETUID to have accesss to some files where the www server +// should not. That's why it has a C wrapper. In modern systems running scripts +// directly with SETUID is considered unsafe and not allowed. +// +// Copyright (C) 2016 Balthasar Szczepański +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include +#include + +###VIEWER_PL; +###VIEWER_PL_ERRLOG; + +int main(int argc, char *argv[], char *envp[]) +{ + freopen(VIEWER_PL_ERRLOG,"at",stderr); + return execve(VIEWER_PL,argv,envp); +} diff --git a/viewer.1.pl b/viewer.1.pl new file mode 100644 index 0000000..7afe8a1 --- /dev/null +++ b/viewer.1.pl @@ -0,0 +1,627 @@ +###PERL; +# +# /bsta/v +# viewer is generated from viewer.1.pl. +# 09.01.2023 +# +# The viewer interface +# +# Copyright (C) 2016-2017, 2019-2020, 2023 Balthasar Szczepański +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +use strict; +#use warnings; +###LIB; +use bsta_lib qw(failpage gethttpheader getcgi entityencode readdatafile writedatafile printdatafileht urlencode bb2ht bb2bb linehtml); +use File::Copy; + +###DATA_PATH; +###DEFAULT_PATH; +###SETTINGS_PATH; +###STATE_PATH; +###LOGO_PATH; +###FAVICON_PATH; +###WEBSITE; +###WEBSITE_NAME; +###CSS_PATH; +###CGI_PATH; +###FRAME_PATH; +###VIEWER_PATH; +###NOACCESS_PATH; +###STORY_PATH; +###WWW_PATH; +###INDEX_PATH; +###ATTACH_PATH; +###INFO_PATH; +###BBCODE_PATH; +###GOTO_PATH; +###LIST_PATH; +###TIMER_PATH; + +my %http; +my %cgi; +my %framedata; +my %nextframedata; +my %default; +my %settings; +my %state; +my %newstate; +my %gotolist; + +my $time = time(); +srand ($time-$$); + +my $method; +my $frame; +my $password; +my $passwordOK; +my $IP; +my $access; +my $seconds; +my $minutes; +my $hours; +my $statefile; +my $showcommand; +my $ongtime; +my $textmode; + +delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; +###PATH; + +if ($ENV{'REQUEST_METHOD'} =~ /^(HEAD|GET|POST)$/) { + $method=$1; +} +else{ + exit failpage("Status: 405 Method Not Allowed\nAllow: GET, POST, HEAD\n","405 Method Not Allowed","The interface does not support the $ENV{'REQUEST_METHOD'} method.",$method); +} + +%http = gethttpheader (\%ENV); +%cgi = getcgi($ENV{'QUERY_STRING'}); + +if ($method eq 'POST') { + if ($http{'content-type'} eq 'application/x-www-form-urlencoded') { + my %cgipost=getcgi( ); + foreach my $ind (keys %cgipost) { + $cgi{$ind}=$cgipost{$ind}; + } + } + # multipart not supported + else{ + exit failpage("Status: 415 Unsupported Media Type\n","415 Unsupported Media Type","Unsupported Content-type: $http{'content-type'}."); + } +} + +if ($ENV{'HTTP_X_FORWARDED_FOR'} =~ /^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$/) { + $IP=$1; +} +elsif ($ENV{'REMOTE_ADDR'} =~ /^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$/) { + $IP=$1; +} +else { + $IP='0.0.0.0'; +} + +if ($cgi{'f'} =~ /^(.+)$/) { + $frame=int($1); +} +elsif ($ENV{'PATH_INFO'} =~ /^\/(.+)$/) { + $frame=int($1); +} +else { + $frame = 0; +} + +if ($cgi{'p'} =~ /^(.+)$/) { + $password=$1; +} +else { + $password=''; +} +# print "Content-type: text/plain\n\n"; + +%settings=readdatafile(SETTINGS_PATH); +%default=readdatafile(DEFAULT_PATH); +%framedata=readdatafile(DATA_PATH.$frame); +if($password eq $settings{'password'}){ + $passwordOK = 1; +} +else{ + $passwordOK = 0; +} + +if (open ($statefile,"+<",STATE_PATH)){ + if (flock($statefile,2)) { + + %state=readdatafile($statefile); + + if($frame<0) { + $frame = int($state{'last'}) + $frame +1; + %framedata=readdatafile(DATA_PATH.$frame); + } + + if(int($state{'state'})==1 && $frame == int($state{'last'}) && $method ne 'HEAD' && !$passwordOK){ + my %newstate=%state; + if($state{'ip1'} ne $IP) { + if ($state{'ip1'} eq '') { + $newstate{'ip1'} = $IP; + writedatafile($statefile,%newstate); + } + elsif($state{'ip2'} ne $IP) { + if ($state{'ip2'} eq '') { + $newstate{'ip2'} = $IP; + writedatafile($statefile,%newstate); + } + else { + $newstate{'state'}=2; + $newstate{'ip3'} = $IP; + writedatafile($statefile,%newstate); + } + } + } + } + elsif(int($state{'state'})==0 && $frame == 1) { + my %story; + my $inpath; + my $outpath; + + %story = readdatafile(STORY_PATH); + %gotolist=readdatafile(LIST_PATH); + if(int($story{'state'}) == 17 && int($story{'pass'}) == 1) { + #ACTIVATE! + + $framedata{'ongtime'} = $time; + writedatafile(DATA_PATH.$frame,%framedata); + $state{'state'} = 1; + $state{'last'} = 1; + $state {'ip1'} = '0.0.0.0'; + $state {'ip2'} = '0.0.0.0'; + $state {'ip3'} = ''; + $state {'nextong'} = (int($time / 3600) + int($settings{'firstongtime'})) * 3600 ; + $state{'ongtime'} = int($settings{'firstongtime'}); + + unless(defined($framedata{'ext'})){ + $framedata{'ext'}=$default{'ext'}; + } + + $inpath = DATA_PATH.sprintf($settings{'frame'},$frame,$framedata{'ext'}); + $outpath = WWW_PATH.sprintf($settings{'frame'},$frame,$framedata{'ext'}); + + $gotolist{'title-1'}=$framedata{'title'}; + $gotolist{'ongtime-1'}=$framedata{'ongtime'}; + + if(copy ($inpath, $outpath)) { + writeindex(INDEX_PATH); + writedatafile($statefile,%state); + writedatafile(LIST_PATH,%gotolist); + } + else { + $state{'state'} = 0; + } + } + } + } + else { + $state{'state'} = 0; + } + close ($statefile); +} +else { + $state{'state'} = 0; +} + +%nextframedata=readdatafile(DATA_PATH.($frame+1)); +foreach my $ind (keys %default) { + unless(defined($framedata{$ind})){ + $framedata{$ind}=$default{$ind}; + } + unless(defined($nextframedata{$ind})){ + $nextframedata{$ind}=$default{$ind}; + } +} + +$hours=int($state{'nextong'})-$time; +$ongtime=int($state{'ongtime'}); +if($ongtime == 0) { + $ongtime=int($settings{'ongtime'}) +} + +$showcommand = ($hours < ($ongtime*3600/3)); +if($hours>0){ + $seconds = sprintf('%02d',$hours % 60); + $hours = int($hours/60); + $minutes = sprintf('%02d',$hours % 60); + $hours = sprintf('%02d',int($hours/60)); +} +elsif(($hours>=-15) && (int($state{'state'}) == 2)){ + $seconds='NG'; + $minutes='00'; + $hours='00'; +} +else{ + $seconds='EE'; + $minutes='EE'; + $hours='EE'; +} + + +if ($passwordOK || (int($state{'state'}) >= 1 && $frame <= int($state{'last'}) && $frame >= 0)) { + $access=1; +} +else { + $access=0; + %framedata = readdatafile(NOACCESS_PATH); + foreach my $ind (keys %default) { + unless(defined($framedata{$ind})){ + $framedata{$ind}=$default{$ind}; + } + } +} + +$textmode = int($cgi{'b'}); +if($textmode > 2) { + $textmode = 0; +} + +# print "Content-type: text/plain\n\n"; +# print 'frame='.$frame."\n"; +# print 'password='.$password."\n"; +# print 'passwordOK='.$passwordOK."\n"; +# print 'access='.$access."\n"; +# print "\n>>>ENV<<<\n\n"; +# foreach my $ind (keys %ENV) { + # print $ind.'='.$ENV{$ind}."\n"; +# } +# print "\n>>>HTTP<<<\n\n"; +# foreach my $ind (keys %http) { + # print $ind.': '.$http{$ind}."\n"; +# } +# print "\n>>>CGI<<<\n\n"; +# foreach my $ind (keys %cgi) { + # print $ind.'='.$cgi{$ind}."\n"; +# } +# print "\n>>>FRAMEDATA<<<\n\n"; +# foreach my $ind (keys %framedata) { + # print $ind.': '.$framedata{$ind}."\n"; +# } +# print "\n>>>NEXTFRAMEDATA<<<\n\n"; +# foreach my $ind (keys %nextframedata) { + # print $ind.': '.$nextframedata{$ind}."\n"; +# } +# print "\n>>>SETTINGS<<<\n\n"; +# foreach my $ind (keys %settings) { + # print $ind.': '.$settings{$ind}."\n"; +# } +# print "\n>>>STATE<<<\n\n"; +# foreach my $ind (keys %state) { + # print $ind.': '.$state{$ind}."\n"; +# } + +print "Content-type: text/html\n"; +if(!$access) { + print "Status: 403 Forbidden\n"; +} +print "\n"; +if($method eq 'HEAD') { + exit; +} + +print ''."\n"; +print ''."\n"; +print ''."\n"; +print ''; +unless($framedata{'title'} eq '' || $framedata{'title'} eq $settings{'story'}) { + print entityencode($framedata{'title'}).' • '; +} +print entityencode($settings{'story'}).' • '.WEBSITE_NAME.''."\n"; +print ''."\n"; +print ''."\n"; +print ''."\n"; +print ''."\n"; +if($frame>0 && $access) { + print ''."\n"; +} +if ($passwordOK || $frame