###PERL_LIB: use lib /botm/lib/bsta
use botm_common (
+ 'HTTP_STATUS',
'read_header_env', 'url_query_decode',
'read_data_file', 'write_data_file',
'html_entity_encode_dec',
'open_encoded',
- 'join_path'
+ 'join_path',
+ 'merge_url',
+ 'make_id',
+ 'http_header_status'
);
use bsta_lib (
- 'TEXT_MODE',
+ 'TEXT_MODE', 'STATE',
'get_password',
'fail_method', 'fail_content_type',
'redirect',
'print_html_start', 'print_html_end',
'print_html_head_start', 'print_html_head_end',
- 'print_html_body_start', 'print_html_body_end'
+ 'print_html_body_start', 'print_html_body_end',
+ 'bb_to_html', 'eval_bb',
+ 'merge_settings',
+ 'write_index', 'write_static_viewer_page'
);
###PERL_PATH_SEPARATOR: PATH_SEPARATOR = /
###PERL_CGI_VIEWER_PATH: CGI_VIEWER_PATH = /bsta/v
+###PERL_CGI_WORDS_PATH: CGI_WORDS_PATH = /bsta/w
###PERL_DATA_SETTINGS_PATH: DATA_SETTINGS_PATH = /botm/data/bsta/settings
+###PERL_DATA_STATE_PATH: DATA_STATE_PATH = /botm/data/bsta/state
###PERL_DATA_WORDS_PATH: DATA_WORDS_PATH = /botm/data/bsta/words/
+###PERL_LOG_SPAM_PATH: LOG_SPAM_PATH = /botm/log/bsta/words_spam.log
+###PERL_LOG_WORDS_PATH: LOG_WORDS_PATH = /botm/log/bsta/words.log
+
###PERL_WEBSITE_NAME: WEBSITE_NAME = Bicycles on the Moon
###PERL_COMMENT_PAGE_LENGTH:COMMENT_PAGE_LENGTH= 16
my %http;
my %cgi;
+my %state;
my %settings;
my %words_data;
my %post_data;
+my %last_post_data;
my @post_list;
my $method;
my $frame;
my $ID;
+my $last_ID;
my $password;
my $password_ok;
+my $access;
my $edit = 0;
my $remove = 0;
my $post = 0;
my $quote;
my $words_data_path;
my $post_data_path;
+my $last_post_data_path;
my $index;
my $page;
+my $ong_state;
+my $last_frame;
my $fh;
my $r;
if ($ENV{'REQUEST_METHOD'} =~ /^(HEAD|GET|POST)$/) {
$method = $1;
}
-else{
- exit fail_method($ENV{'REQUEST_METHOD'}, 'GET, POST, HEAD');
+else {
+ exit fail_method($ENV{'REQUEST_METHOD'}, ['GET', 'POST', 'HEAD']);
}
%http = read_header_env(\%ENV);
%cgi = merge_settings(\%cgi, \%cgi_post);
}
# multipart not supported
- else{
+ else {
exit fail_content_type($method, $http{'content-type'});
}
}
}
$password = get_password(\%cgi);
-%settings = read_data_file(DATA_SETTINGS_PATH());
+%settings = read_data_file(DATA_SETTINGS_PATH());
+%state = read_data_file(DATA_STATE_PATH());
+$ong_state = int($state{'state'});
+$last_frame = int($state{'last'});
$password_ok = ($password eq $settings{'password'});
}
}
-if ($frame eq '') {
- exit output(0, '', 'Frame ID not specified.');
+unless ($frame ne '') {
+ exit output(0, HTTP_STATUS->{'bad_request'}, 'Frame ID not specified.');
+}
+$access = (
+ $password_ok || (
+ ($ong_state >= STATE->{'waiting'}) &&
+ ($frame <= $last_frame)
+ )
+);
+unless ($access) {
+ exit output(0, HTTP_STATUS->{'forbidden'}, 'Not allowed to post this here now');
}
$words_data_path = join_path(PATH_SEPARATOR(), DATA_WORDS_PATH(), $frame);
unless (open_encoded($fh, "+<:encoding(UTF-8)", $words_data_path)) {
- exit output(0, '500 Internal Server Error', 'Failed opening data file.');
+ unless (open_encoded($fh, "+>:encoding(UTF-8)", $words_data_path)) {
+ exit output(0, '500 Internal Server Error', 'Failed opening data file.', 1);
+ }
}
unless (flock($fh, 2)) {
- exit output(0, '500 Internal Server Error', 'Failed locking data file.');
+ exit output(0, HTTP_STATUS->{'internal_server_error'}, 'Failed locking data file.', 1);
}
%words_data = read_data_file(
}
}
-if ($remove) {
+if ($remove || ($ID ne '')) {
unless ($index ne '') {
close($fh);
- exit output(0, '404 Not Found', 'Nothing to remove.');
+ exit output(0, HTTP_STATUS->{'not_found'}, $remove ? 'Nothing to remove.' : 'No such message.');
}
unless ($cgi{'key'} eq $post_data{'key'}) {
close($fh);
- exit output(0, '403 Forbidden', 'Invalid request.');
+ exit output(0, HTTP_STATUS->{'bad_request'}, 'Invalid request.');
}
-
- if (($cgi{'username'} eq '') && ($cgi{'password'} eq ''))
- {
+}
+if ($remove) {
+ unless (($method eq 'POST') && ($cgi{'i'} ne '')) { # followed a link, not confirmed yet
close($fh);
- exit output(0, '', '', 0, 1);
+ exit output(0, '', '', 1);
}
-
- unless ($cgi{'username'} ne '') {
+}
+else {
+ unless (($method eq 'POST') && $post) { # followed a link, not confirmed yet
close($fh);
- exit output(0, '403 Forbidden', 'Missing user name.', 0, 1);
+ exit output(0, '', '', 1);
+ }
+}
+if (!$remove) {
+ unless ($cgi{'words'} ne '') {
+ exit output(0, HTTP_STATUS->{'bad_request'}, 'Where are your words?', 1);
}
+}
+unless ($cgi{'username'} ne '') {
+ close($fh);
+ exit output(0, HTTP_STATUS->{'bad_request'}, 'Missing user name.', 1);
+}
+if ($remove || ($ID ne '')) {
unless ($cgi{'username'} eq $post_data{'name'}) {
close($fh);
- exit output(0, '403 Forbidden', 'Wrong user name.', 0, 1);
+ exit output(0, HTTP_STATUS->{'forbidden'}, 'Wrong user name.', 1);
}
- unless ($cgi{'password'} ne '') {
+}
+if ($remove || ($ID ne '')) {
+ unless ($cgi{'password'} ne '') {
close($fh);
- exit output(0, '403 Forbidden', 'Missing password.', 0, 1);
+ exit output(0, HTTP_STATUS->{'bad_request'}, 'Missing password.', 1);
}
unless (
($cgi{'password'} eq $post_data{'password'}) || (
)
) {
close($fh);
- exit output(0, '403 Forbidden', 'Wrong password.', 0, 1);
+ exit output(0, HTTP_STATUS->{'forbidden'}, 'Wrong password.', 1);
}
-
- # all conditions fulfilled
-
+ if ($password_ok) {
+ $cgi{'password'} = $post_data{'password'};
+ }
+}
+unless ($cgi{'password2'} eq '') {
+ close($fh);
+ # no error code to confuse spambot :)
+ output(0, '', 'Please don\'t write anything in the place which should remain empty.', 1);
+ if (open_encoded($fh, ">>:encoding(UTF-8)", LOG_SPAM_PATH())) {
+ $cgi{'content'} = $cgi{'words'};
+ $cgi{'empty'} = $cgi{'password2'};
+ delete($cgi{'words'});
+ delete ($cgi{'password'});
+ delete ($cgi{'password2'});
+ print $fh "$time SPAM $ID\n";
+ write_data_file(
+ $fh, \%cgi, '',
+ 0, 0, 0,
+ '>>', 1
+ );
+ print $fh "\n\n";
+ close ($fh);
+ }
+ exit;
+}
+
+# all conditions fulfilled
+
+if ($remove) {
splice @post_list, $index, 1;
$words_data{'posts'} = scalar(@post_list);
$words_data{'content'} = \@post_list;
- $r = write_data_file($fh, '', '', \%words_data);
- unless($r) {
+ $r = write_data_file(
+ $fh, # file
+ \%words_data,
+ '', # encoding
+ 0, # no header
+ 0, # header only
+ 1 # as list
+ );
+ unless ($r) {
close($fh);
- exit output(0, '500 Internal Server Error', 'Failed writing data file.');
+ exit output(0, HTTP_STATUS->{'internal_server_error'}, 'Failed writing data file.');
}
+
+ close ($fh);
+
+ if (open_encoded($fh, ">>:encoding(UTF-8)", LOG_WORDS_PATH())) {
+ delete ($post_data{'password'});
+ print $fh "$time REMOVE $ID\n";
+ write_data_file(
+ $fh, \%post_data, '',
+ 0, 0, 0,
+ '>>', 1
+ );
+ print $fh "\n\n";
+ close ($fh);
+ }
+
$ID = 'insw';
exit output(1);
}
+if ($ID eq '') {
+ $ID = make_id($frame, 1);
+}
+if ($index eq '') {
+ $index = scalar(@post_list);
+ $page = int($index / COMMENT_PAGE_LENGTH());
+ if ($index > 0) {
+ $last_ID = $post_list[-1];
+ $last_post_data_path = join_path(PATH_SEPARATOR(), DATA_WORDS_PATH(), $last_ID);
+ %last_post_data = read_data_file($last_post_data_path);
+ if (
+ ($cgi{'username'} eq $last_post_data{'name' }) &&
+ ($cgi{'words' } eq $last_post_data{'content'})
+ ) { # duplicate post
+ $index -= 1;
+ $page = int($index / COMMENT_PAGE_LENGTH());
+ $ID = $last_ID;
+ close ($fh);
+ exit output(1);
+ }
+ }
+ push @post_list, $ID;
+}
+$words_data{'posts'} = scalar(@post_list);
+$words_data{'content'} = \@post_list;
+
+$post_data_path = join_path(PATH_SEPARATOR(), DATA_WORDS_PATH(), $ID);
+
+$post_data{'frame'} = $frame;
+$post_data{'name'} = $cgi{'username'};
+$post_data{'password'} = $cgi{'password'};
+if ($post_data{'posttime'} eq '') {
+ $post_data{'posttime'} = $time;
+}
+else {
+ $post_data{'edittime'} = $time;
+}
+if ($post_data{'key'} eq '') {
+ my $new_key = '';
+ for (my $i=1; $i<16; $i+=1) {
+ $new_key .= sprintf('%02X', int(rand(0x100)));
+ }
+ $post_data{'key'} = $new_key;
+}
+$post_data{'content'} = $cgi{'words'};
+
+$r = write_data_file($post_data_path, \%post_data);
+unless ($r) {
+ close($fh);
+ exit output(0, HTTP_STATUS->{'internal_server_error'}, 'Failed writing post file.', 1, 0);
+}
+
+$r = write_data_file(
+ $fh, # file
+ \%words_data,
+ '', # encoding
+ 0, # no header
+ 0, # header only
+ 1 # as list
+);
+unless ($r) {
+ close($fh);
+ exit output(0, HTTP_STATUS->{'internal_server_error'}, 'Failed writing data file.', 1, 0);
+}
+
close($fh);
-exit output(0, '', '???');
+if (($frame == 0) && ($ong_state > STATE->{'inactive'})) {
+ write_index(\%state, \%settings);
+}
+elsif ($frame >= 1) {
+ write_static_viewer_page(
+ $frame,
+ \%state,
+ \%settings,
+ '', # default
+ '', # frame data
+ '', # prev frame data
+ '', # next frame data
+ \%words_data
+ );
+}
+
+if (open_encoded($fh, ">>:encoding(UTF-8)", LOG_WORDS_PATH())) {
+ delete ($post_data{'password'});
+ print $fh "$time POST $ID\n";
+ write_data_file(
+ $fh, \%post_data, '',
+ 0, 0, 0,
+ '>>', 1
+ );
+ print $fh "\n\n";
+ close ($fh);
+}
+
+exit output(1);
+
sub output {
- (
- my $posted, my $status, my $message,
- my $edit, my $remove,
- ) = @_;
+ (my $done, my $status, my $message, my $show_content) = @_;
- if ($posted) {
- my $redirect_url = merge_url(
- {'path' => CGI_VIEWER_PATH()},
- {
- 'path' => $frame,
- 'query' => {
- 'b' => TEXT_MODE->{'words'},
- 'i' => $page,
- 'p' => ($password_ok ? $settings{'password'} : '')
- },
- 'fragment' => $ID
- }
- );
- return redirect ($method, $redirect_url, 303);
+ my $return_url = merge_url(
+ {'path' => CGI_VIEWER_PATH()},
+ {
+ 'path' => $frame,
+ 'query' => {
+ 'b' => TEXT_MODE->{'words'},
+ 'i' => $page,
+ 'p' => ($password_ok ? $settings{'password'} : '')
+ },
+ 'fragment' => $ID
+ }
+ );
+ if ($done) {
+ return redirect($method, $return_url, HTTP_STATUS->{'see_other'});
}
- print "Content-type: text/html\n";
if ($status ne '') {
- print 'Status: '.$status."\n";
+ print http_header_status($status);
}
- print "\n";
+ print "Content-type: text/html; charset=UTF-8\n\n";
if ($method eq 'HEAD') {
return;
}
+ my $title;
+ my $name;
+ my $content;
+
+ if ($remove) {
+ $title = 'Remove message "'.$ID.'"';
+ }
+ elsif ($ID ne '') {
+ $title = 'Edit message "'.$ID.'"';
+ }
+ else {
+ $title = 'Words';
+ }
+ if ($frame ne '') {
+ $title = $frame.'. '.$title;
+ }
+
+ if ($cgi{'username'} ne '') {
+ $name = $cgi{'username'}
+ }
+ elsif ($post_data{'name'} ne '') {
+ $name = $post_data{'name'}
+ }
+ else {
+ $name = '';
+ }
+
+ if ($cgi{'words'} ne '') {
+ $content = $cgi{'words'};
+ }
+ elsif ($quote ne '') {
+ my $quote_data_path = join_path(PATH_SEPARATOR(), DATA_WORDS_PATH(), $quote);
+ my %quote_data = read_data_file($quote_data_path);
+ $content = '[quote="'.$quote_data{'name'}.'"]'.$quote_data{'content'}.'[/quote]';
+ }
+ elsif (($cgi{'edit'} ne '') || $remove) {
+ $content = $post_data{'content'};
+ }
+ else {
+ $content = '';
+ }
+
+ my $_key = html_entity_encode_dec($post_data{'key'}, 1);
+ my $_ID = html_entity_encode_dec($ID, 1);
+ my $_title = html_entity_encode_dec($title, 1);
my $_message = html_entity_encode_dec($message, 1);
+ my $_password = html_entity_encode_dec($settings{'password'}, 1);
my $_story = html_entity_encode_dec($settings{'story'}, 1);
+ my $_name = html_entity_encode_dec($name, 1);
+ my $_content = html_entity_encode_dec($content, 1);
+ my $_empty = html_entity_encode_dec($cgi{'password2'}, 1);
my $_website_name = html_entity_encode_dec(WEBSITE_NAME(), 1);
+ my $_post_url = html_entity_encode_dec(CGI_WORDS_PATH(), 1);
+ my $_return_url = html_entity_encode_dec($return_url, 1);
print_html_start(\*STDOUT);
print_html_head_start(\*STDOUT);
- print ' <title>Words • '.$_story.' • '.$_website_name.'</title>';
+ print ' <title>'.$_title.' • '.$_story.' • '.$_website_name.'</title>';
print_html_head_end(\*STDOUT);
print_html_body_start(\*STDOUT);
print ' <div id="inst" class="ins">'."\n";
print ' <div id="title">'."\n";
- print ' <h1 id="titletext">Words</h1>'."\n";
+ print ' <h1 id="titletext">'.$_title.'</h1>'."\n";
print ' </div>'."\n";
print ' </div>'."\n";
print ' </div>'."\n";
}
- if ($edit) {
- print "EDIT\n";
- }
- elsif ($remove) {
- print "REMOVE\n";
+ print ' <div id="insw" class="ins">'."\n";
+
+ if ($show_content) {
+ print ' <div class="undertext" id="words">'."\n";
+ print ' <form method="post" action="'.$_post_url.'">'."\n";
+ unless ($remove) {
+ print ' <b>Your words:</b>'."\n";
+ print ' <textarea class="inta" name="words" rows="4">'.$_content.'</textarea>'."\n";
+ }
+ print ' <table cellpadding="0" cellspacing="0" border="0"><tr>'."\n";
+ print ' <td><b>Your name: </b></td>'."\n";
+ print ' <td><input class="intx" type="text" name="username" value="'.$_name.'"></td>'."\n";
+ print ' <td></td>'."\n";
+ print ' </tr><tr>'."\n";
+ print ' <td><b>'.(($ID ne '') ? 'Password' : 'Optional password').': </b></td>'."\n";
+ print ' <td><input class="intx" type="password" name="password" value=""></td>'."\n";
+ print ' <td>'.(($ID ne '') ? '' : '(if you want to edit later)').'</td>'."\n";
+ print ' </tr><tr>'."\n";
+ print ' <td><b>Leave this empty: </b></td>'."\n";
+ print ' <td><input class="intx" type="text" name="password2" value="'.$_empty.'"></td>'."\n";
+ if ($remove) {
+ print ' <td><input class="inbt" type="submit" name="remove" value="Remove"></td>'."\n";
+ }
+ else {
+ print ' <td>'."\n";
+ print ' <input class="inbt" type="submit" name="post" value="'.(($ID ne '') ? 'Update' : 'Send').'">'."\n";
+ print ' <input class="inbt" type="submit" name="preview" value="Preview">'."\n";
+ print ' </td>'."\n";
+ }
+ print ' </tr></table>'."\n";
+ print ' <input type="hidden" name="f" value="'.$frame.'">'."\n";
+ if ($ID ne '') {
+ print ' <input type="hidden" name="i" value="'.$_ID.'">'."\n";
+ }
+ print ' <input type="hidden" name="key" value="'.$_key.'">'."\n";
+ if ($password_ok) {
+ print ' <input type="hidden" name="p" value="'.$_password.'">'."\n";
+ }
+ print ' </form>'."\n";
+ if ($content ne ''){
+ print ' <br>'."\n";
+ print ' <div id="preview"class="opomba">'."\n";
+ print ' <div class="opomba_info">'."\n";
+ print ' Preview:'."\n";
+ print ' </div>'."\n";
+ print ' <div class="opomba_text">'."\n";
+ print bb_to_html(
+ eval_bb(
+ $content,
+ 0,
+ $password_ok ? $settings{'password'} : ''
+ )
+ )."\n";
+ print ' </div>'."\n";
+ print ' </div>'."\n";
+ }
+ print ' </div>'."\n";
}
+ print ' <div id="underlinks">'."\n";
+ print ' <a href="'.$_return_url.'">Return</a>'."\n";
+ print ' </div>'."\n";
+
+ print ' </div>'."\n";
- print_html_body_end(\*STDOUT); # , $ong_state == STATE->{'inactive'}
+ print_html_body_end(\*STDOUT, $ong_state == STATE->{'inactive'});
print_html_end(\*STDOUT);
}
-
-
-
-
-
-
-