]>
git.p6c8.net - selfforum.git/blob - selfforum-cgi/user/fo_posting.pl
3 ################################################################################
5 # File: user/fo_posting.pl #
7 # Authors: André Malo <nd@o3media.de>, 2001-04-08 #
9 # Description: Accept new postings, display "Neue Nachricht" page #
11 ################################################################################
25 my $null = $0; $null =~ s/\\/\//g
; # for win :-(
26 $Bin = ($null =~ /^(.*)\/.*$/)?
$1 : '.';
27 $Shared = "$Bin/../shared";
28 $Config = "$Bin/config";
29 $Script = ($null =~ /^.*\/(.*)$/)?
$1 : $null;
32 # $Bin = ($null =~ /^(.*)\/.*$/)? $1 : '.';
33 # $Config = "$Bin/../../daten/forum/config";
34 # $Shared = "$Bin/../../cgi-shared";
35 # $Script = ($null =~ /^.*\/(.*)$/)? $1 : $null;
38 # setting umask, remove or comment it, if you don't need
43 use CGI
::Carp
qw(fatalsToBrowser);
51 $VERSION = do { my @r =(q
$Revision$ =~ /\d+/g); sprintf "%d."."%02d" x
$#r, @r };
53 # load script configuration and admin default conf.
55 my $conf = read_script_conf
($Config, $Shared, $Script);
56 my $adminDefault = read_admin_conf
($conf -> {files
} -> {adminDefault
});
58 # Initialize the request
60 my $request = new Posting
::Request
($conf, $adminDefault);
62 # fetch and parse the cgi-params
64 $request -> parse_cgi
;
66 # handle errors or save the posting
68 $request -> handle_error
or $request -> save
;
74 # shorten the main file?
76 $request -> severance
;
80 ### main end ###################################################################
82 ################################################################################
83 ### Posting::Request ###########################################################
84 package Posting
::Request
;
88 use Encode
::Plain
; $Encode::Plain
::utf8
= 1; # generally convert from UTF-8
102 use Template
::Posting
;
106 ### sub new ####################################################################
108 # initialising the Posting::Request object
109 # check parameters and fill in object properties
112 my ($class, $conf, $adminDefault) = @_;
114 my $sp = $conf -> {show
} -> {Posting
};
119 admin
=> $adminDefault,
121 message_path
=> $conf -> {files
} -> {messagePath
},
122 forum_file_name
=> $conf -> {files
} -> {forum
},
125 assign
=> $sp -> {assign
},
126 template
=> $conf -> {template
},
127 form_must
=> $sp -> {form
} -> {must
},
128 form_data
=> $sp -> {form
} -> {data
},
129 form_action
=> $sp -> {form
} -> {action
},
132 template
=> new Template
$sp -> {templateFile
},
144 start_severance
($self -> {conf
} -> {original
} -> {files
} -> {sev_app
});
147 ### sub response ###############################################################
149 # print the response to STDOUT
155 my $formdata = $self -> {conf
} -> {form_data
};
156 my $formact = $self -> {conf
} -> {form_action
};
157 my $template = $self -> {template
};
158 my $assign = $self -> {conf
} -> {assign
};
159 my $q = $self -> {cgi_object
};
161 # fill out the form field names
164 for (keys %$formdata) {
165 $pars -> {$formdata -> {$_} -> {assign
} -> {name
}} = plain
($formdata -> {$_} -> {name
}) if (
166 exists($formdata -> {$_} -> {name
})
167 and exists ($formdata -> {$_} -> {assign
})
168 and exists ($formdata -> {$_} -> {assign
} -> {name
})
172 # response the 'new message' page
174 if ($self -> {response
} -> {new_thread
}) {
176 # fill in the default form data
180 for (keys %$formdata) {
181 unless (exists ($formdata -> {$_} -> {type
}) and $formdata -> {$_} -> {type
} eq 'internal') {
182 if (exists ($formdata -> {$_} -> {default}) and exists ($formdata -> {$_} -> {assign
} -> {value
})) {
183 $default -> {$formdata -> {$_} -> {assign
} -> {value
}}
184 = $formdata -> {$_} -> {default};
186 elsif (exists($formdata -> {$_} -> {values})) {
187 my ($_name, $val) = $_;
188 $val = exists ($formdata -> {$_} -> {default})
189 ?
$formdata -> {$_} -> {default}
191 $default -> {$formdata -> {$_} -> {assign
} -> {value
}}
192 = $self -> {template
} -> list
(
195 { $assign -> {optval
} => plain
($_),
196 ((defined $val and $_ eq $val)
197 ?
($assign -> {optsel
} => 1)
201 } @
{$formdata -> {$_name} -> {values}}
208 print $q -> header
(-type
=> 'text/html');
209 print ${$template -> scrap
(
211 { $formdata->{uniqueID
} ->{assign
}->{value
} => plain
(unique_id
),
212 $formdata->{quoteChar
} ->{assign
}->{value
} => 'ÿ'.plain
($self -> {conf
} -> {admin
} -> {View
} -> {quoteChars
}),
213 $formact->{post
}->{assign
} => $formact->{post
}->{url
},
221 # check the response -> doc
223 unless ($self -> {response
} -> {doc
}) {
225 spec
=> 'unknown_error',
229 $self -> handle_error
;
231 unless ($self -> {response
} -> {doc
}) {
232 $self -> jerk
('While producing the HTML response an unknown error has occurred.');
237 # ok, print the response document to STDOUT
239 print $q -> header
(-type
=> 'text/html');
240 print ${$template -> scrap
(
241 $self -> {response
} -> {doc
},
243 $self -> {response
} -> {pars
}
250 ### sub handle_error ###########################################################
252 # analyze error data and create content for the response method
254 # Return: true if error detected
260 my $spec = $self -> {error
} -> {spec
};
262 return unless ($spec);
264 my $assign = $self -> {conf
} -> {assign
};
265 my $formdata = $self -> {conf
} -> {form_data
};
267 my $desc = $self -> {error
} -> {desc
} || '';
268 my $type = $self -> {error
} -> {type
};
271 if (exists ($formdata -> {$desc})
272 and exists ($formdata -> {$desc} -> {assign
} -> {$spec})) {
273 $emsg = $formdata -> {$desc} -> {assign
} -> {$spec};
276 $emsg = $assign -> {$spec} || '';
281 if ($type eq 'fatal') {
282 $self -> {response
} -> {doc
} = $assign -> {docFatal
};
283 $self -> {response
} -> {pars
} = {
284 $assign -> {errorMessage
} => $self -> {template
} -> insert
($emsg)
289 # user is able to repair his request
291 elsif ($type eq 'repeat' or $type eq 'fetch') {
292 $self -> {response
} -> {doc
} = $assign -> {docError
};
293 $self -> fillout_form
;
294 $self -> {response
} -> {pars
} -> {$assign -> {errorMessage
}} = $self -> {template
} -> insert
($emsg);
295 my $num = $spec eq 'too_long'
296 ?
$formdata -> {$desc} -> {maxlength
}
297 : ($spec eq 'too_short'
298 ?
$formdata -> {$desc} -> {minlength
}
302 $self -> {response
} -> {pars
} -> {$assign -> {charNum
}} = $num
309 ### sub fillout_form ###########################################################
311 # fill out the form using available form data
318 my $assign = $self -> {conf
} -> {assign
};
319 my $formdata = $self -> {conf
} -> {form_data
};
320 my $formact = $self -> {conf
} -> {form_action
};
321 my $q = $self -> {cgi_object
};
326 $pars -> {$formact -> {post
} -> {assign
}} = $formact -> {post
} -> {url
};
328 for (keys %$formdata) {
329 if ($_ eq 'quoteChar') {
330 $pars -> {$formdata->{$_}->{assign
}->{value
}}
331 = 'ÿ'.plain
($q -> param
($formdata -> {quoteChar
} -> {name
}) or '');
333 elsif (exists ($formdata -> {$_} -> {name
})) {
334 unless (exists ($formdata -> {$_} -> {values})) {
335 $pars -> {$formdata -> {$_} -> {assign
} -> {value
}}
336 = plain
($q -> param
($formdata -> {$_} -> {name
}));
340 $pars -> {$formdata -> {$_} -> {assign
} -> {value
}}
341 = $self -> {template
} -> list
(
344 { $assign -> {optval
} => plain
($_),
345 (( $_ eq $q -> param
($formdata -> {$_name} -> {name
}))
346 ?
($assign -> {optsel
} => 1)
350 } @
{$formdata -> {$_name} -> {values}}
357 $self -> {response
} -> {pars
} = $pars;
361 ### sub save ###################################################################
364 # check on legal reply or dupe is released here
371 # if an empty 'new message' document, there's nothing to save
373 return if ($self -> {response
} -> {new_thread
});
375 $self -> {check_success
} = 0;
377 # lock and load the forum main file
379 if ($self -> load_main_file
) {
381 # if a reply - is it legal?
384 if ($self -> check_reply_dupe
) {
386 unless ($self -> {response
} -> {reply
} or $self -> {response
} -> {new
}) {
387 # don't know, if we any time come to this branch
388 # the script is probably broken
391 spec
=> 'unknown_error',
397 my $formdata = $self -> {conf
} -> {form_data
};
398 my $q = $self -> {cgi_object
};
399 my $f = $self -> {forum
};
401 quoteChars
=> $q -> param
($formdata -> {quoteChar
} -> {name
}),
402 uniqueID
=> $q -> param
($formdata -> {uniqueID
} -> {name
}),
404 ip
=> $q -> remote_addr
,
405 forumFile
=> $self -> {conf
} -> {forum_file_name
},
406 messagePath
=> $self -> {conf
} -> {message_path
},
407 lastThread
=> $f -> {last_thread
},
408 lastMessage
=> $f -> {last_message
},
409 parsedThreads
=> $f -> {threads
},
411 messages
=> $self -> {conf
} -> {template
} -> {messages
} || {},
412 base_uri
=> $self -> {conf
} -> {original
} -> {files
} -> {forum_base
}
415 # set the variables if defined..
418 author
=> 'posterName',
419 email
=> 'posterEmail',
420 category
=> 'posterCategory',
421 subject
=> 'posterSubject',
422 body
=> 'posterBody',
423 homepage
=> 'posterURL',
424 image
=> 'posterImage'
428 $pars -> {$_} = $q -> param
($formdata -> {$may{$_}} -> {name
})
429 if (defined $q -> param
($formdata -> {$may{$_}} -> {name
}));
432 my ($stat, $xml, $mid, $tid);
434 # we've got a fup if it's a reply
436 if ($self -> {response
} -> {reply
}) {
437 $pars -> {parentMessage
} = $self -> {fup_mid
};
438 $pars -> {thread
} = $self -> {fup_tid
};
439 ($stat, $xml, $mid, $tid) = write_reply_posting
($pars);
442 ($stat, $xml, $mid, $tid) = write_new_thread
($pars);
453 my $cache = new Posting
::Cache
($self->{conf
}->{original
}->{files
}->{cachePath
});
454 $cache -> add_posting
(
455 { thread
=> ($tid =~ /(\d+)/)[0],
456 posting
=> ($mid =~ /(\d+)/)[0]
460 $self -> {check_success
} = 1;
461 my $thx = $self -> {conf
} -> {show_posting
} -> {thanx
};
463 # define special response data
465 $self -> {response
} -> {doc
} = $self -> {conf
} -> {assign
} -> {docThx
};
466 $self -> {response
} -> {pars
} = {
467 $thx -> {time} => plain
(hr_time
($time)),
468 $thx -> {body
} => message_as_HTML
(
472 assign
=> $self -> {conf
} -> {assign
},
473 quoteChars
=> $q -> param
($formdata -> {quoteChar
} -> {name
}),
474 quoting
=> $self -> {conf
} -> {admin
} -> {View
} -> {quoting
}
478 # set the variables if defined..
481 author
=> 'posterName',
482 email
=> 'posterEmail',
483 category
=> 'posterCategory',
484 subject
=> 'posterSubject',
485 homepage
=> 'posterURL',
486 image
=> 'posterImage'
490 my $x = $q -> param
($formdata -> {$may{$_}} -> {name
});
491 $x = '' unless (defined $x);
492 $self -> {response
} -> {pars
} -> {$thx -> {$_}} = plain
($x)
493 if (defined $thx -> {$_});
500 # unlock forum main file
502 if ($self -> {forum
} -> {flocked
}) {
503 $self -> {forum
} -> {flocked
} -> unlock
;
504 $self -> {forum
} -> {flocked
} = 0;
507 $self -> handle_error
unless $self -> {check_success
};
512 ### sub parse_cgi ##############################################################
514 # fetch and decode cgi-parameters,
515 # find out the kind of response requested by the user (new message, reply)
522 # create the CGI object
524 $self -> {cgi_object
} = new CGI
;
528 $self -> {check_success
} = $self -> check_cgi
;
533 ### sub load_main_file #########################################################
535 # load and parse the forum main file
537 # Return: Success (true/false)
541 my $forum = new Lock
($self -> {conf
} -> {forum_file_name
});
543 unless ($forum -> lock(LH_EXCL
)) {
544 unless ($forum -> masterlocked
) {
545 # occupied or no w-bit set for the directory..., hmmm
557 spec
=> 'master_lock',
564 $self -> {forum
} -> {flocked
} = $forum;
565 ( $self -> {forum
} -> {threads
},
566 $self -> {forum
} -> {last_thread
},
567 $self -> {forum
} -> {last_message
},
568 $self -> {forum
} -> {dtd
},
569 $self -> {forum
} -> {unids
}
570 ) = get_all_threads
($self -> {conf
} -> {forum_file_name
}, KEEP_DELETED
);
577 ### sub check_reply_dupe #######################################################
579 # check whether a reply is legal
580 # (followup posting must exists)
582 # check whether this form request is a dupe
583 # (unique id already exists)
585 # Return: Status Code (Bool)
587 sub check_reply_dupe
{
591 # return true unless it's not a reply
595 $self -> {response
} -> {reply
}
596 or $self -> {response
} -> {new
}
599 if ($self -> {response
} -> {reply
}) {
601 my ($threads, $ftid, $fmid, $i, %msg) = (
602 $self -> {forum
} -> {threads
},
607 # thread doesn't exist
609 unless (exists($threads -> {$ftid})) {
617 # build a reverse lookup hash (mid => number in array)
618 # and ignore invisible messages
619 # (users can't reply to "deleted" msg)
621 for ($i=0; $i < @
{$threads -> {$ftid}}; $i++) {
623 if ($threads -> {$ftid} -> [$i] -> {deleted
}) {
624 $i+=$threads -> {$ftid} -> [$i] -> {answers
};
627 $msg{$threads -> {$ftid} -> [$i] -> {mid
}}=$i;
631 # message doesn't exist
633 unless (exists($msg{$fmid})) {
641 # build a unique id lookup hash
642 # use the unids of parent message's kids
644 %unids = map {$_ => 1} @
{$threads -> {$ftid} -> [$msg{$fmid}] -> {unids
}};
647 # build a unique id lookup hash, too
648 # but use only the level-zero-messages
650 %unids = map {$_ => 1} @
{$self -> {forum
} -> {unids
}};
656 $self -> {cgi_object
} -> param
(
657 $self -> {conf
} -> {form_data
} -> {uniqueID
} -> {name
})})) {
669 ### sub check_cgi ##############################################################
671 # cgi params are like raw eggs...
673 # Return: Status Code (Bool)
674 # creates content for the handle_error method if anything fails
679 # count the submitted keys and get the keys themselves
681 my %got_keys = map {($_ => 1)} $self -> {cgi_object
} -> param
;
682 my $cnt_got_keys = keys %got_keys;
683 my $formdata = $self -> {conf
} -> {form_data
};
684 my $formmust = $self -> {conf
} -> {form_must
};
686 # user requested the 'new thread' page
687 # (no params but perhaps the user-ID have been submitted)
689 if ($cnt_got_keys == 0 or (
690 exists ($formdata -> {userID
})
691 and $cnt_got_keys == 1
692 and $got_keys{$formdata -> {userID
} -> {name
}}
694 $self -> {response
} -> {new_thread
} = 1;
695 $self -> {check_success
} = 1;
699 # now we know, we've got a filled out form
700 # we do the following steps to check it:
702 # 1st: create a reverse Hash (CGI-key - identifier)
703 # 2nd: did we get _all_ must-keys?
704 # check whether reply or new message request
705 # 3rd: did we get too many keys?
706 # 4th: do _all_ submitted values accord to
708 # fetch the "missing" keys
714 exists($formdata -> {$_} -> {name
})
715 ?
($formdata -> {$_} -> {name
} => $_)
721 $self -> {response
} -> {reply
} = $got_keys {$formdata -> {followUp
} -> {name
}}?
1 : 0;
722 $self -> {response
} -> {new
} = not $self -> {response
} -> {reply
};
724 # define the fetch array (values to fetch from parent message)
726 $self -> {fetch
} = [];
728 for ( @
{$formmust -> {$self -> {response
} -> {reply
}?
'reply':'new'}} ) {
730 unless ($got_keys {$formdata -> {$_} -> {name
}}) {
732 # only miss the key unless we're able to fetch it from parent posting
735 not $self -> {response
} -> {reply
}
736 or $formdata -> {$_} -> {errorType
} eq 'fetch') {
739 spec
=> 'missing_key',
746 # keep in mind to fetch the value later
748 push @
{$self -> {fetch
}} => $_;
753 # I'm lazy - I know...
754 my $q = $self -> {cgi_object
};
759 unless (exists ($name {$_})) {
761 spec
=> 'unexpected_key',
771 unless ($self -> decode_param
) {
773 spec
=> 'unknown_encoding',
779 if ($self -> {response
} -> {reply
}) {
781 # get the parent-identifiers if we got a reply request
783 my ($ftid, $fmid) = split /;/ => $q -> param
($formdata -> {followUp
} -> {name
}) => 2;
785 unless ($ftid =~ /^\d+$/ and $fmid =~ /^\d+$/) {
787 spec
=> 'unknown_followup',
792 $self -> {fup_tid
} = $ftid;
793 $self -> {fup_mid
} = $fmid;
795 # fetch the missing keys
796 # if it fails, they're too short, too... ;)
799 $got_keys{$formdata -> {$_} -> {name
}} = 1 for (@
{$self -> {fetch
}});
802 # now we can check on length, type etc.
804 for (keys %got_keys) {
806 # we are sure, we've got only one value for one key
808 my $val = $q -> param
($_);
810 $val =~ s/\302\240/ /g; # convert nbsp (UTF-8 encoded) into normal spaces
811 $val =~ s/\015\012|\015|\012/ /g # convert \n into spaces unless it's a multiline field
813 exists ($formdata -> {$name {$_}} -> {type
})
814 and $formdata -> {$name {$_}} -> {type
} eq 'multiline-text'
817 $q -> param
($_ => $val); # write it back
821 if (length $val > $formdata -> {$name {$_}} -> {maxlength
}) {
825 type
=> $formdata -> {$name {$_}} -> {errorType
}
827 $self -> kill_param
or return;
831 # (only check if there's defined a minimum length)
833 if (exists ($formdata -> {$name {$_}} -> {minlength
})) {
835 # kill the whitespaces to get only the visible characters...
837 (my $val_ww = $val) =~ s/\s+//g;
839 if (exists ($formdata -> {$name {$_}} -> {type
}) and $formdata -> {$name {$_}} -> {type
} eq 'name') {
840 $val_ww =~ y/a-zA-Z//cd;
843 # my @badlist = map {qr/\Q$_/i} qw (
844 # # insert badmatchlist here
847 # push @badlist => map {qr/\b\Q$_\E\b/i} qw(
848 # # insert badwordlist here
852 if ($val_ww =~ /$_/) {
863 if (length $val_ww < $formdata -> {$name {$_}} -> {minlength
}) {
867 type
=> $formdata -> {$name {$_}} -> {errorType
}
869 $self -> kill_param
or return;
873 # check the values on expected kinds of content
874 # (email, http-url, url, option)
876 if (exists ($formdata -> {$name {$_}} -> {type
}) and length $val) {
877 if ($formdata -> {$name {$_}} -> {type
} eq 'email' and not is_email
$val) {
879 spec
=> 'wrong_mail',
881 type
=> $formdata -> {$name {$_}} -> {errorType
}
883 $self -> kill_param
or return;
886 elsif ($formdata -> {$name {$_}} -> {type
} eq 'http-url' and not is_URL
$val => 'http') {
888 spec
=> 'wrong_http_url',
890 type
=> $formdata -> {$name {$_}} -> {errorType
}
892 $self -> kill_param
or return;
895 elsif ($formdata -> {$name {$_}} -> {type
} eq 'url' and not is_URL
$val => ':ALL') {
899 type
=> $formdata -> {$name {$_}} -> {errorType
}
901 $self -> kill_param
or return;
904 elsif ($formdata -> {$name {$_}} -> {type
} eq 'unique-id' and not may_id
$val) {
906 spec
=> 'wrong_unique_id',
908 type
=> $formdata -> {$name {$_}} -> {errorType
}
910 print STDERR
"Manipuliert!";
911 $self -> kill_param
or return;
915 if (exists ($formdata -> {$name {$_}} -> {values})
916 and not exists ({map {$_ => undef} @
{$formdata -> {$name {$_}} -> {values}}} -> {$val})) {
920 type
=> $formdata -> {$name {$_}} -> {errorType
}
922 $self -> kill_param
or return;
929 ### sub kill_param #############################################################
931 # kill the param (set it on '') if wrong and declared as 'kill' in config file
933 # Return: true if killed
939 if ($self -> {conf
} -> {form_data
} -> {$self -> {error
} -> {desc
}} -> {errorType
} eq 'kill') {
940 $self -> {cgi_object
} -> param
($self -> {conf
} -> {form_data
} -> {$self -> {error
} -> {desc
}} -> {name
} => '');
941 $self -> {error
} = {};
948 ### sub fetch ##################################################################
950 # fetch "missing" keys from parent posting
954 my $q = $self -> {cgi_object
};
955 my $formdata = $self -> {conf
} -> {form_data
};
957 if (@
{$self -> {fetch
}}) {
958 my $thread = new Lock
($self -> {conf
} -> {message_path
}.'t'.$self -> {fup_tid
}.'.xml');
960 if ($thread -> lock (LH_SHARED
)) {
961 my $xml = parse_xml_file
($thread -> filename
);
965 my $mnode = get_message_node
($xml, 't'.$self -> {fup_tid
}, 'm'.$self -> {fup_mid
});
967 my $header = get_message_header
($mnode);
969 $q -> param
($formdata -> {$_} -> {name
} => $header -> {$formdata -> {$_} -> {header
}})
970 for (@
{$self -> {fetch
}});
979 # fillout the values with an empty string
981 $q -> param
($formdata -> {$_} -> {name
} => '')
982 for (@
{$self -> {fetch
}});
987 ### sub decode_param ###########################################################
989 # convert submitted form data into UTF-8
990 # unless it's not encoded yet
992 # Return: Status Code (Bool)
993 # false if unknown encoding (like UTF-7 for instance)
998 my $q = $self -> {cgi_object
};
999 my $formdata = $self -> {conf
} -> {form_data
};
1001 my $code = $q -> param
($formdata -> {quoteChar
} -> {name
});
1004 # Latin 1 (we hope so - there's no real way to find out :-( )
1005 if ($code =~ /^\377/) {
1006 $q -> param
($_ => map {toUTF8
($_)} $q -> param
($_)) for ($q -> param
);
1009 # UTF-8 is (probably) correct,
1010 # other encodings we don't know and fail
1011 return unless $code =~ /^\303\277/;
1014 # remove the ÿ (encoded as UTF-8) from quotechars
1015 $q -> param
($formdata -> {quoteChar
} -> {name
}
1016 => substr $q -> param
($formdata -> {quoteChar
} -> {name
}),2);
1018 # ok, params now should be UTF-8 encoded
1023 my $text = $_[1] || 'An error has occurred.';
1025 Content-type: text/plain
1032 We will fix it as soon as possible. Thank you for your patience.
1041 ### end of fo_posting.pl #######################################################
patrick-canterino.de