1 ###RUN_PERL: #!/usr/bin/perl
4 # opomba is generated from opomba.1.pl.
6 # The comment posting interface
8 # Copyright (C) 2024 Balthasar SzczepaĆski
10 # This program is free software: you can redistribute it and/or modify
11 # it under the terms of the GNU Affero General Public License as
12 # published by the Free Software Foundation, either version 3 of the
13 # License, or (at your option) any later version.
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU Affero General Public License for more details.
20 # You should have received a copy of the GNU Affero General Public License
21 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # use Encode::Locale ('decode_argv');
26 use Encode ('encode', 'decode');
28 ###PERL_LIB: use lib /botm/lib/bsta
30 'read_header_env', 'url_query_decode',
31 'read_data_file', 'write_data_file',
32 'html_entity_encode_dec',
39 'fail_method', 'fail_content_type',
41 'print_html_start', 'print_html_end',
42 'print_html_head_start', 'print_html_head_end',
43 'print_html_body_start', 'print_html_body_end'
46 ###PERL_PATH_SEPARATOR: PATH_SEPARATOR = /
48 ###PERL_CGI_VIEWER_PATH: CGI_VIEWER_PATH = /bsta/v
50 ###PERL_DATA_SETTINGS_PATH: DATA_SETTINGS_PATH = /botm/data/bsta/settings
51 ###PERL_DATA_WORDS_PATH: DATA_WORDS_PATH = /botm/data/bsta/words/
53 ###PERL_WEBSITE_NAME: WEBSITE_NAME = Bicycles on the Moon
55 ###PERL_COMMENT_PAGE_LENGTH:COMMENT_PAGE_LENGTH= 16
57 binmode STDIN, ':encoding(UTF-8)';
58 binmode STDOUT, ':encoding(UTF-8)';
59 binmode STDERR, ':encoding(UTF-8)';
90 delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
91 ###PERL_SET_PATH: $ENV{'PATH'} = /usr/local/bin:/usr/bin:/bin;
93 if ($ENV{'REQUEST_METHOD'} =~ /^(HEAD|GET|POST)$/) {
97 exit fail_method($ENV{'REQUEST_METHOD'}, 'GET, POST, HEAD');
100 %http = read_header_env(\%ENV);
101 %cgi = url_query_decode($ENV{'QUERY_STRING'});
103 if ($method eq 'POST') {
104 if ($http{'content-type'} eq 'application/x-www-form-urlencoded') {
105 my %cgi_post = url_query_decode( <STDIN> );
106 %cgi = merge_settings(\%cgi, \%cgi_post);
108 # multipart not supported
110 exit fail_content_type($method, $http{'content-type'});
114 if ($ENV{'PATH_INFO'} =~ /^\/([0-9]+)$/) {
117 elsif ($ENV{'PATH_INFO'} =~ /^\/(.+)$/) {
120 if ($cgi{'f'} =~ /^.+$/) {
123 if ($cgi{'i'} =~ /^.+$/) {
126 $password = get_password(\%cgi);
128 %settings = read_data_file(DATA_SETTINGS_PATH());
130 $password_ok = ($password eq $settings{'password'});
132 if ($cgi{'post'} ne '') {
135 elsif ($cgi{'edit'} ne '') {
141 elsif ($cgi{'remove'} ne '') {
144 $ID = $cgi{'remove'};
149 if ($cgi{'quote'} ne '') {
150 $quote = $cgi{'quote'};
155 $post_data_path = join_path(PATH_SEPARATOR(), DATA_WORDS_PATH(), $ID);
156 %post_data = read_data_file($post_data_path);
157 if ($post_data{'frame'} ne '') {
158 $frame = int($post_data{'frame'});
163 exit output(0, '', 'Frame ID not specified.');
166 $words_data_path = join_path(PATH_SEPARATOR(), DATA_WORDS_PATH(), $frame);
168 unless (open_encoded($fh, "+<:encoding(UTF-8)", $words_data_path)) {
169 exit output(0, '500 Internal Server Error', 'Failed opening data file.');
171 unless (flock($fh, 2)) {
172 exit output(0, '500 Internal Server Error', 'Failed locking data file.');
175 %words_data = read_data_file(
183 @post_list = @{$words_data{'content'}};
185 for (my $i=0; $i< scalar(@post_list); $i +=1) {
186 if ($post_list[$i] eq $ID) {
188 $page = int($index / COMMENT_PAGE_LENGTH());
194 unless ($index ne '') {
196 exit output(0, '404 Not Found', 'Nothing to remove.');
198 unless ($cgi{'key'} eq $post_data{'key'}) {
200 exit output(0, '403 Forbidden', 'Invalid request.');
203 if (($cgi{'username'} eq '') && ($cgi{'password'} eq ''))
206 exit output(0, '', '', 0, 1);
209 unless ($cgi{'username'} ne '') {
211 exit output(0, '403 Forbidden', 'Missing user name.', 0, 1);
213 unless ($cgi{'username'} eq $post_data{'name'}) {
215 exit output(0, '403 Forbidden', 'Wrong user name.', 0, 1);
217 unless ($cgi{'password'} ne '') {
219 exit output(0, '403 Forbidden', 'Missing password.', 0, 1);
222 ($cgi{'password'} eq $post_data{'password'}) || (
223 ($cgi{'password'} eq $settings{'password'}) &&
228 exit output(0, '403 Forbidden', 'Wrong password.', 0, 1);
231 # all conditions fulfilled
233 splice @post_list, $index, 1;
234 $words_data{'posts'} = scalar(@post_list);
235 $words_data{'content'} = \@post_list;
237 $r = write_data_file($fh, '', '', \%words_data);
240 exit output(0, '500 Internal Server Error', 'Failed writing data file.');
248 exit output(0, '', '???');
252 my $posted, my $status, my $message,
253 my $edit, my $remove,
257 my $redirect_url = merge_url(
258 {'path' => CGI_VIEWER_PATH()},
262 'b' => TEXT_MODE->{'words'},
264 'p' => ($password_ok ? $settings{'password'} : '')
269 return redirect ($method, $redirect_url, 303);
272 print "Content-type: text/html\n";
274 print 'Status: '.$status."\n";
277 if ($method eq 'HEAD') {
281 my $_message = html_entity_encode_dec($message, 1);
282 my $_story = html_entity_encode_dec($settings{'story'}, 1);
283 my $_website_name = html_entity_encode_dec(WEBSITE_NAME(), 1);
285 print_html_start(\*STDOUT);
286 print_html_head_start(\*STDOUT);
288 print ' <title>Words • '.$_story.' • '.$_website_name.'</title>';
290 print_html_head_end(\*STDOUT);
291 print_html_body_start(\*STDOUT);
293 print ' <div id="inst" class="ins">'."\n";
295 print ' <div id="title">'."\n";
296 print ' <h1 id="titletext">Words</h1>'."\n";
297 print ' </div>'."\n";
299 print ' </div>'."\n";
301 if ($message ne '') {
302 print ' <div id="insb" class="ins">'."\n";
304 print ' <div id="command">'."\n";
305 print ' <span class="br">'.$_message.'</span>'."\n";
306 print ' </div>'."\n";
308 print ' </div>'."\n";
318 print_html_body_end(\*STDOUT); # , $ong_state == STATE->{'inactive'}
319 print_html_end(\*STDOUT);