]> git.p6c8.net - selfforum.git/blobdiff - selfforum-cgi/user/fo_posting.pl
several style changes
[selfforum.git] / selfforum-cgi / user / fo_posting.pl
index e59d2c4340a72081392d51c5608927b4f45d63aa..41099deef32aa1fe2f146c48d2508cc03125f06f 100644 (file)
@@ -4,7 +4,7 @@
 #                                                                              #
 # File:        user/fo_posting.pl                                              #
 #                                                                              #
-# Authors:     AndrĂ© Malo <nd@o3media.de>, 2001-03-30                          #
+# Authors:     AndrĂ© Malo <nd@o3media.de>, 2001-03-31                          #
 #                                                                              #
 # Description: Accept new postings, display "Neue Nachricht" page              #
 #                                                                              #
@@ -24,40 +24,59 @@ BEGIN {
 }
 
 use lib "$Shared";
-#use CGI::Carp qw(fatalsToBrowser);
-
-use Conf;
-use Encode::Plain; $Encode::Plain::utf8 = 1;
-use Encode::Posting;
-use Id;
-use Lock qw(:ALL);
-use CheckRFC;
-use Posting::_lib qw(get_all_threads get_message_node get_message_header hr_time parse_xml_file);
-use Posting::Write;
-use Template;
-use Template::Posting;
+use CGI::Carp qw(fatalsToBrowser);
 
-use CGI;
-use XML::DOM;
+#use Conf;
+#use Encode::Plain; $Encode::Plain::utf8 = 1; # generally convert from UTF-8
+#use Id;
+#use Posting::Write;
+#use Template;
+#use Template::Posting;
+
+#use autouse 'Encode::Posting' => qw();
 
 # load script configuration and admin default conf.
 my $conf         = read_script_conf ($Bin, $Shared, $Script);
 my $adminDefault = read_admin_conf ($conf -> {files} -> {adminDefault});
 
-# Initializing the request
-my $response = new Posting::Response ($conf, $adminDefault);
+# Initialize the request
+#
+my $request = new Posting::Request ($conf, $adminDefault);
 
 # fetch and parse the cgi-params
-$response -> parse_cgi;
+#
+$request -> parse_cgi;
 
+# handle errors or save the posting
+#
+$request -> handle_error or $request -> save;
+
+# show response
+#
+$request -> response;
+
+#
+#
+### main end ###################################################################
 
 ################################################################################
-### Posting::Response ##########################################################
-package Posting::Response;
+### Posting::Request ###########################################################
+package Posting::Request;
+
+use Lock          qw(:ALL);
+use Posting::_lib qw(
+      hr_time
+      parse_xml_file
+      get_all_threads get_message_node get_message_header
+      KEEP_DELETED
+    );
+
+use autouse 'CheckRFC' => qw[ is_email($) is_URL($@) ];
+use CGI;
 
 ### sub new ####################################################################
 #
-# initialising the Posting::Response object
+# initialising the Posting::Request object
 # check parameters and fill in object properties
 #
 sub new {
@@ -80,29 +99,231 @@ sub new {
          form_action     => $sp -> {form} -> {action},
        },
 
-       template => new Template $sp -> {templateFile}
+       template => new Template $sp -> {templateFile},
+       response => {},
+       forum    => {},
+       error    => {}
      };
 
   bless $self, $class;
 }
 
+### sub save ###################################################################
+#
+# save posting
+# check on legal reply or dupe is released here
+#
+# Return: -none-
+#
+sub save {
+  my $self = shift;
+
+  # if an empty 'new message' document, there's nothing to save
+  #
+  return if ($self -> {response} -> {new_thread});
+
+  # lock and load the forum main file
+  #
+  if ($self -> load_main_file) {
+
+    # if a reply - is it legal?
+    # is it a dupe?
+    #
+    if ($self -> check_reply_dupe) {
+
+      # we've got an opening
+      #
+      if ($self -> {response} -> {new}) {
+        $self -> save_new;
+      }
+
+      # we've got a reply
+      #
+      elsif ($self -> {response} -> {reply}) {
+        $self -> save_reply;
+      }
+
+      # don't know, if we any time come to this branch
+      # the script is probably broken
+      #
+      else {
+        $self -> {error} = {
+          spec => 'unknown_error',
+          type => 'fatal'
+        };
+      }
+    }
+  }
+
+  # unlock forum main file
+  #
+  if ($self -> {forum} -> {flocked}) {
+    violent_unlock_file($self -> {forum_file_name}) unless unlock_file ($self -> {forum_file_name});
+    $self -> {forum} -> {flocked} = 0;
+  }
+
+  $self -> handle_error unless $self -> {check_success};
+
+  return;
+}
+
 ### sub parse_cgi ##############################################################
 #
 # fetch and decode cgi-parameters,
 # find out the kind of response requested by the user (new message, reply)
 #
-# Return: Status Code (Bool)
-#         try out the error method, if false
+# Return: -none-
 #
 sub parse_cgi {
   my $self = shift;
 
   # create the CGI object
+  #
   my $q = new CGI;
   $self -> {cgi_object} = $q;
 
   # check the params
-  return unless $self -> check_cgi;
+  #
+  $self -> {check_success} = $self -> check_cgi;
+
+  return;
+}
+
+### sub load_main_file #########################################################
+#
+# load and parse the forum main file
+#
+# Return: Success (true/false)
+#
+sub load_main_file {
+  my $self = shift;
+  my $lock_stat;
+
+  unless ($lock_stat = write_lock_file ($self ->{forum_file_name})) {
+    if ($lock_stat == 0) {
+      # occupied or no w-bit set for the directory..., hmmm
+      #
+      violent_unlock_file ($self -> {forum_file_name});
+      $self -> {error} = {
+        spec => 'occupied',
+        type => 'fatal'
+      };
+      return;
+    }
+    else {
+      # master lock is set
+      #
+      $self -> {error} = {
+        spec => 'master_lock',
+        type => 'fatal'
+      };
+      return;
+    }
+  }
+  else {
+    $self -> {forum} -> {flocked} = 1;
+    ( $self -> {forum} -> {threads},
+      $self -> {forum} -> {last_thread},
+      $self -> {forum} -> {last_message},
+      undef,
+      $self -> {forum} -> {unids}
+    ) = get_all_threads ($self -> {forum_file_name}, KEEP_DELETED);
+  }
+
+  # ok, looks good
+  1;
+}
+
+### sub check_reply_dupe #######################################################
+#
+# check whether a reply is legal
+# (followup posting must exists)
+#
+# check whether this form request is a dupe
+# (unique id already exists)
+#
+# Return: Status Code (Bool)
+#
+sub check_reply_dupe {
+  my $self = shift;
+
+  # return true unless it's not a reply
+  #
+  return 1 unless (
+    $self -> {response} -> {reply}
+    and $self -> {response} -> {new}
+  );
+
+  my %unids;
+
+  if ($self -> {response} -> {reply}) {
+
+    my ($threads, $ftid, $fmid, $i, %msg, %unids) = (
+          $self -> {forum} -> {threads},
+          $self -> {fup_tid},
+          $self -> {fup_mid}
+       );
+
+    # thread doesn't exist
+    #
+    unless (exists($threads -> {$ftid})) {
+      $self -> {error} = {
+        spec => 'no_reply',
+        type => 'fatal'
+      };
+      return;
+    }
+
+    # build a reverse lookup hash (mid => number in array)
+    # and ignore invisible messages
+    # (users can't reply to "deleted" msg)
+    #
+    for ($i=0; $i < @{$threads -> {$ftid}}; $i++) {
+
+      if ($threads -> {$ftid} -> [$i] -> {deleted}) {
+        $i+=$threads -> {$ftid} -> [$i] -> {answers};
+      }
+      else {
+        $msg{$threads -> {$ftid} -> [$i] -> {mid}}=$i;
+      }
+    }
+
+    # message doesn't exist
+    #
+    unless (exists($msg{$fmid})) {
+      $self -> {error} = {
+        spec => 'no_reply',
+        type => 'fatal'
+      };
+      return;
+    }
+
+    # build a unique id lookup hash
+    # use the unids of parent message's kids
+    #
+    %unids = map {$_ => 1} @{$threads -> {$ftid} -> [$msg{$fmid}] -> {unids}};
+  }
+  else {
+    # build a unique id lookup hash, too
+    # but use only the level-zero-messages
+    #
+    %unids = map {$_ => 1} @{$self -> {unids}};
+  }
+
+  # now check on dupe
+  #
+  if (exists ($unids{
+                $self -> {cgi_object} -> param (
+                  $self -> {conf} -> {form_data} -> {uniqueID} -> {name})})) {
+    $self -> {error} = {
+      spec => 'dupe',
+      type => 'fatal'
+    };
+    return;
+  }
+
+  # ok, looks fine
+  1;
 }
 
 ### sub check_cgi ##############################################################
@@ -110,32 +331,31 @@ sub parse_cgi {
 # cgi params are like raw eggs...
 #
 # Return: Status Code (Bool)
-#         creates content for the error method if anything fails
+#         creates content for the handle_error method if anything fails
 #
 sub check_cgi {
   my $self = shift;
 
-  # find out the count of the submitted keys and the keys themselves
+  # count the submitted keys and get the keys themselves
   #
   my %got_keys     = map {($_ => 1)} $self -> {cgi_object} -> param;
   my $cnt_got_keys = keys %got_keys;
-  my $formdata = $self -> {conf} -> {form_data};
-  my $formmust = $self -> {conf} -> {form_must};
+  my $formdata     = $self -> {conf} -> {form_data};
+  my $formmust     = $self -> {conf} -> {form_must};
 
   # user requested the 'new thread' page
-  # (no params or only the user-ID has been submitted)
+  # (no params but perhaps the user-ID have been submitted)
   #
   if ($cnt_got_keys == 0 or (
         exists ($formdata -> {userID})
         and $cnt_got_keys == 1
         and $got_keys{$formdata -> {userID} -> {name}}
-        )
-     ) {
-    $self -> {response} = {new_thread => 1};
+        )) {
+    $self -> {response} -> {new_thread} = 1;
+    $self -> {check_success} = 1;
     return 1;
   }
 
-  ###################################################
   # now we know, we've got a filled out form
   # we do the following steps to check it:
   #
@@ -143,8 +363,8 @@ sub check_cgi {
   # 2nd: did we get _all_ must-keys?
   #      check whether reply or new message request
   # 3rd: did we get too many keys?
-  # 4th: do _all_ requested values accord to
-  #      expectations?
+  # 4th: do _all_ submitted values accord to
+  #      our expectations?
   #      fetch the "missing" keys
   #
 
@@ -171,7 +391,10 @@ sub check_cgi {
            $self -> {response} -> {new}
         or $formdata -> {$name {$_}} -> {errorType} eq 'fetch') {
 
-        $self -> {error} = {spec => 'missing_key'};
+        $self -> {error} = {
+          spec => 'missing_key',
+          type => 'fatal'
+        };
         return;
       }
       else {
@@ -188,7 +411,8 @@ sub check_cgi {
     unless (exists ($name {$_})) {
       $self -> {error} = {
         spec => 'unexpected_key',
-        desc => $name{$_}
+        desc => $name{$_},
+        type => 'fatal'
       };
       return;
     }
@@ -197,7 +421,10 @@ sub check_cgi {
   # 4
   #
   unless ($self -> decode_param) {
-    $self -> {error} = {spec => 'unknown_encoding'};
+    $self -> {error} = {
+      spec => 'unknown_encoding',
+      type => 'fatal'
+    };
     return;
   };
 
@@ -206,30 +433,42 @@ sub check_cgi {
 
   if ($self -> {response} -> {reply}) {
 
-    # get the parent-identifiers if we got a reply
+    # get the parent-identifiers if we got a reply request
     #
     my ($ftid, $fmid) = split /;/ => $q -> param ($formdata -> {followUp} -> {name}) => 2;
 
     unless ($ftid =~ /\d+/ and $fmid =~ /\d+/) {
-      $self -> {error} = {spec => 'unknown_followup'};
+      $self -> {error} = {
+        spec => 'unknown_followup',
+        type => 'fatal'
+      };
       return;
     }
     $self -> {fup_tid} = $ftid;
     $self -> {fup_mid} = $fmid;
 
-    # now fetching the missing keys
+    # fetch the missing keys
     # if it fails, they're too short, too... ;)
     #
     $self -> fetch;
+    $got_keys{$_}=1 for (@{$self -> {fetch}});
   }
 
   # now we can check on length, type etc.
   #
   for (keys %got_keys) {
 
+    # we are sure, we've got only one value for one key
+    #
     my $val = $q -> param ($_);
 
-    $val =~ s/\302\240/ /g;    # convert nbsp to normal spaces
+    $val =~ s/\302\240/ /g;           # convert nbsp (UTF-8 encoded) into normal spaces
+    $val =~ s/\015\012|\015|\012/ /g  # convert \n into spaces unless it's a multiline field
+      unless (
+        exists ($formdata -> {$name {$_}} -> {type})
+        and $formdata -> {$name {$_}} -> {type} eq 'multiline-text'
+      );
+
     $q -> param ($_ => $val);  # write it back
 
     # too long?
@@ -237,7 +476,8 @@ sub check_cgi {
     if (length $val > $formdata -> {$name {$_}} -> {maxlength}) {
       $self -> {error} = {
         spec => 'too_long',
-        desc => $name{$_}
+        desc => $name{$_},
+        type => $formdata -> {$name {$_}} -> {errorType}
       };
       return;
     }
@@ -254,25 +494,50 @@ sub check_cgi {
       if (length $val_ww < $formdata -> {$name {$_}} -> {minlength}) {
         $self -> {error} = {
           spec => 'too_short',
-          desc => $name{$_}
+          desc => $name{$_},
+          type => $formdata -> {$name {$_}} -> {errorType}
         };
         return;
       }
     }
 
-#    return 'wrongMail' if ($formdata -> {$name{$_}} -> {type} eq 'email' and length ($dparam{$_}) and not is_mail_address ($dparam{$_}));
+    # check the values on expected kinds of content
+    # (email, http-url, url)
+    #
+    if (exists ($formdata -> {$name {$_}} -> {type}) and length $val) {
+      if ($formdata -> {$name {$_}} -> {type} eq 'email' and not is_email $val) {
+        $self -> {error} = {
+          spec => 'wrong_mail',
+          desc => $name{$_},
+          type => $formdata -> {$name {$_}} -> {errorType}
+        };
+        return;
+      }
+
+      elsif ($formdata -> {$name {$_}} -> {type} eq 'http-url' and not is_URL $val => 'http') {
+        $self -> {error} = {
+          spec => 'wrong_http_url',
+          desc => $name{$_},
+          type => $formdata -> {$name {$_}} -> {errorType}
+        };
+        return;
+      }
+
+      elsif ($formdata -> {$name {$_}} -> {type} eq 'url' and not is_URL $val => ':ALL') {
+        $self -> {error} = {
+          spec => 'wrong_url',
+          desc => $name{$_},
+          type => $formdata -> {$name {$_}} -> {errorType}
+        };
+        return;
+      }
+    }
   }
 
   # ok, looks good.
   1;
 }
 
-#  delete $dparam {$formdata -> {posterURL} -> {name}}
-#    unless ($dparam {$formdata -> {posterURL} -> {name}} =~ /$httpurl/);
-#
-#  delete $dparam {$formdata -> {posterImage} -> {name}}
-#    unless ($dparam {$formdata -> {posterImage} -> {name}} =~ /$httpurl/);
-
 ### sub fetch ##################################################################
 #
 # fetch "missing" keys from parent posting
@@ -296,6 +561,8 @@ sub fetch {
 
           $q -> param ($formdata -> {$_} -> {name} => $header -> {$formdata -> {$_} -> {header}})
             for (@{$self -> {fetch}});
+
+          return;
         }
       }
     }
@@ -306,6 +573,8 @@ sub fetch {
   #
   $q -> param ($formdata -> {$_} -> {name} => '')
     for (@{$self -> {fetch}});
+
+  return;
 }
 
 ### sub decode_param ###########################################################

patrick-canterino.de