]> bicyclesonthemoon.info Git - ott/bsta/blobdiff - 2words.1.pl
input validation; goto form; show version; 2 words password
[ott/bsta] / 2words.1.pl
index 3b8bdfa3b17d3c7ed5fa5384d30c6da881b03b51..1bc2d74be5eef2d5c7ea48caa6b2960aaa8061d2 100644 (file)
@@ -29,11 +29,10 @@ use Encode ('encode', 'decode');
 use botm_common (
        'HTTP_STATUS',
        'http_header_status', 'http_header_allow',
-       'read_data_file', 'write_data_file',
        'merge_url',
        'read_header_env',
        'html_entity_encode_dec',
-       'url_query_decode',
+       'url_query_decode', 'url_query_encode',
        'open_encoded'
 );
 use bsta_lib (
@@ -44,16 +43,16 @@ use bsta_lib (
        'print_html_head_start', 'print_html_head_end',
        'print_html_body_start', 'print_html_body_end',
        'write_index',
-       'get_remote_addr',
+       'get_remote_addr', 'get_password',
        'merge_settings',
-       'ong'
+       'ong',
+       'read_story', 'write_story',
+       'read_settings', 'read_state'
 );
 
 ###PERL_CGI_PATH:           CGI_PATH           = /bsta/
 ###PERL_CGI_2WORDS_PATH:    CGI_2WORDS_PATH    = /bsta/2words
 
-###PERL_DATA_SETTINGS_PATH: DATA_SETTINGS_PATH = /botm/data/bsta/settings
-###PERL_DATA_STATE_PATH:    DATA_STATE_PATH    = /botm/data/bsta/state
 ###PERL_DATA_STORY_PATH:    DATA_STORY_PATH    = /botm/data/bsta/story
 
 ###PERL_WEBSITE_NAME:       WEBSITE_NAME       = Bicycles on the Moon
@@ -94,14 +93,13 @@ my $intf_state;
 my $intf_pass;
 my $intf_pause;
 my $intf_mode;
-my $story_i_path;
 my $fh;
 my $story_lock;
 my @story_lines;
 my $ong_state;
 my $page;
-my $cmd_clear;
-my $cmd_clear_all;
+my $password;
+my $password_ok;
 
 
 delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
@@ -130,15 +128,19 @@ if ($method eq 'POST') {
 
 $IP = get_remote_addr();
 $page = get_id(\%cgi);
+$password = get_password(\%cgi);
 if ($cgi{'words'} ne '') {
        $words = $cgi{'words'};
 }
 
-%settings = read_data_file(DATA_SETTINGS_PATH());
-%state    = read_data_file(DATA_STATE_PATH());
+%settings = read_settings();
+%state    = read_state();
 $ong_state = int($state{'state'});
-$cmd_clear     = $settings{'password'}.' clear';
-$cmd_clear_all = $settings{'password'}.' clearall';
+
+$password_ok = ($password eq $settings{'password'});
+if ($password_ok) {
+       $IP .= ' OK';
+}
 
 $story_lock=0;
 if (open_encoded($fh, "+<:encoding(UTF-8)", DATA_STORY_PATH())) {
@@ -146,7 +148,7 @@ if (open_encoded($fh, "+<:encoding(UTF-8)", DATA_STORY_PATH())) {
        if (flock($fh,2)) {
                $story_lock=2;
        }
-       %story = read_data_file($fh);
+       %story = read_story($fh);
        
        if ($story{'lastip'} =~ /^.+$/) {
                $last_IP=$&;
@@ -172,13 +174,13 @@ if (open_encoded($fh, "+<:encoding(UTF-8)", DATA_STORY_PATH())) {
        if (
                ($intf_state < 0) || (
                        ($method eq 'POST') && (
-                               ($words eq $cmd_clear) ||
-                               ($words eq $cmd_clear_all)
+                               ($cgi{'clear'} ne '') || 
+                               ($cgi{'clear_all'} ne '')
                        )
                )
        ) {
                if (
-                       ($words eq $cmd_clear_all) ||
+                       ($cgi{'clear_all'} ne '') ||
                        ($intf_state < -1)
                ) {
                        $story{'id'} = 0;
@@ -198,28 +200,38 @@ if (open_encoded($fh, "+<:encoding(UTF-8)", DATA_STORY_PATH())) {
                                0 # pause
                        );
                }
-               write_data_file($fh, \%story);
+               write_story($fh, \%story);
        }
        
        if (($words ne '') && ($method eq 'POST')) {
-               if (!$turn) {
+               if (
+                       (!$turn) &&
+                       (!$password_ok)
+               ) {
                        $status = HTTP_STATUS->{'forbidden'};
                        $message = "It's not your turn.";
                }
                # TODO: consider allowing non-ASCII letters in words.
                # (not very important in English language)
-               elsif ($words =~ /^([!"\(\),\.:;\?][ \t]*)?([A-Za-z][A-Za-z'\-]*[A-Za-z']?)([!"\(\),\.:;\? \t][ \t]*)([A-Za-z][A-Za-z'\-]*[A-Za-z']?)([!"\(\),\.:;\?]?[ \t]*)$/) {
+               elsif (
+                       ($words =~ /^([!"\(\),\.:;\?][ \t]*)?([A-Za-z][A-Za-z'\-]*[A-Za-z']?)([!"\(\),\.:;\? \t][ \t]*)([A-Za-z][A-Za-z'\-]*[A-Za-z']?)([!"\(\),\.:;\?]?[ \t]*)$/) ||
+                       ($password_ok && ($words ne ''))
+               ) {
                        # we have 2 words
                        $first_letter  = lc(substr($2, 0, 1));
                        $second_letter = lc(substr($4, 0, 1));
                        if (
                                ($first_letter ne $last_letter) &&
-                               ($last_letter ne '')
+                               ($last_letter ne '') && 
+                               (!$password_ok)
                        ) {
                                $status = HTTP_STATUS->{'bad_request'};
                                $message = 'The first word must start with '.uc($last_letter).'.';
                        }
-                       elsif ($first_letter eq $second_letter) {
+                       elsif (
+                               ($first_letter eq $second_letter) &&
+                               (!$password_ok)
+                       ) {
                                $status = HTTP_STATUS->{'bad_request'};
                                $message = 'The second word can\'t also start with '.uc($first_letter).'.';
                        }
@@ -233,10 +245,12 @@ if (open_encoded($fh, "+<:encoding(UTF-8)", DATA_STORY_PATH())) {
                                
                                if ($cgi{'next'} ne '') {
                                        # start next game
-                                       if (split(/\r?\n/,$story{'content'}) >= (STORY_LENGTH-1)) {
+                                       if (
+                                               $password_ok ||
+                                               (split(/\r?\n/,$story{'content'}) >= (STORY_LENGTH-1))
+                                       ) {
                                                # store finished game
-                                               $story_i_path = DATA_STORY_PATH.$story_id;
-                                               write_data_file($story_i_path, \%story);
+                                               write_story($story_id, \%story);
                                                # init new game
                                                $new_story{'id'     } = $story_id + 1;
                                                $new_story{'letter' } = '';
@@ -260,11 +274,11 @@ if (open_encoded($fh, "+<:encoding(UTF-8)", DATA_STORY_PATH())) {
                                                        );
                                                }
                                                # save new game
-                                               write_data_file($fh, \%new_story);
+                                               write_story($fh, \%new_story);
                                        }
                                        else {
                                                $message = 'To early to finish this wordgame.';
-                                               write_data_file($fh, \%story);
+                                               write_story($fh, \%story);
                                        }
                                }
                                else {
@@ -354,7 +368,7 @@ if (open_encoded($fh, "+<:encoding(UTF-8)", DATA_STORY_PATH())) {
                                                        }
                                                }
                                        }
-                                       write_data_file($fh, \%story);
+                                       write_story($fh, \%story);
                                }
                        }
                }
@@ -379,7 +393,7 @@ if (open_encoded($fh, "+<:encoding(UTF-8)", DATA_STORY_PATH())) {
                        $intf_mode,
                        $intf_pause
                );
-               write_data_file($fh, \%story);
+               write_story($fh, \%story);
        }
        @story_lines = split(/\r?\n/, $story{'content'});
        if(@story_lines & 1) {
@@ -401,7 +415,7 @@ if($method eq 'HEAD') {
        exit;
 }
 
-my $max_page = int(($story_id - FIRSTPAGE_LENGTH - 1) / PAGE_LENGTH) + 1;
+my $max_page = int(($story_id + PAGE_LENGTH - FIRSTPAGE_LENGTH - 1) / PAGE_LENGTH);
 my $newer_available = ($page > 0);
 my $older_available = ($page < $max_page);
 my $show_intf = ($intf_pass == 1) && ($ong_state == STATE->{'inactive'});
@@ -500,6 +514,20 @@ my $intf_img = merge_url(
        {'path' => 'intf-00'.$intf_img_id.'.gif'}
 );
 
+if ($password_ok) {
+       my $password_query = url_query_encode({'p', $settings{'password'}});
+       $twowords_url = merge_url($twowords_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
+       $newest_url   = merge_url($newest_url   , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
+       $newer_url    = merge_url($newer_url    , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
+       $older_url    = merge_url($older_url    , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
+       $button_4_url = merge_url($button_4_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
+       $button_3_url = merge_url($button_3_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
+       $button_2_url = merge_url($button_2_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
+       $button_1_url = merge_url($button_1_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
+       $button_0_url = merge_url($button_0_url , {'query' => $password_query, 'append_query' => 1, 'preserve_fragment' => 1});
+}
+
+my $_password = $password_ok ? html_entity_encode_dec($settings{'password'}, 1): '';
 my $_bsta_url     = html_entity_encode_dec($bsta_url     , 1);
 my $_twowords_url = html_entity_encode_dec($twowords_url , 1);
 my $_newest_url   = html_entity_encode_dec($newest_url   , 1);
@@ -556,7 +584,7 @@ if ($page == 0) {
                print '     <span class="br">'.$_message.'</span>'."\n";
        }
        
-       if ($turn) {
+       if ($turn || $password_ok) {
                print '     <form method="post" action="'.$_twowords_url.'">'."\n";
                if ($message eq '') {
                        if ($story{"content"} eq '') {
@@ -568,9 +596,14 @@ if ($page == 0) {
                }
                print '      <input class="intx" type="text" name="words">'."\n";
                print '      <input class="inbt" type="submit" value="enter">'."\n";
-               if (@story_lines >= (STORY_LENGTH-1)) {
+               if ((@story_lines >= (STORY_LENGTH-1)) || $password_ok ) {
                        print '      <input class="inbt" type="submit" name="next" value="enter and then start a new one">'."\n";
                }
+               if ($password_ok) {
+                       print '      <input class="inbt" type="submit" name="clear" value="clear">'."\n";
+                       print '      <input class="inbt" type="submit" name="clear_all" value="clear all">'."\n";
+                       print '      <input type="hidden" name="p" value="'.$_password.'">'."\n";
+               }
                print '     </form>'."\n";
        }
        else {
@@ -610,8 +643,7 @@ print '   <div id="insb" class="ins">'."\n";
 
 print '    <div id="undertext">'."\n";
 for (my $i = $id_start; $i > $id_stop; --$i) {
-       $story_i_path = DATA_STORY_PATH.$i;
-       %new_story = read_data_file($story_i_path);
+       %new_story = read_story($i);
        print '     <p class="'.(($i&1)?'br':'ni').'" id="s'.$i.'">'.html_entity_encode_dec($new_story{'content'}).'</p>'."\n";
 }
 print '    </div>'."\n";