]>
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 my $stat = cut_tail
({
145 forumFile
=> $self -> {conf
} -> {forum_file_name
},
146 messagePath
=> $self -> {conf
} -> {message_path
},
147 archivePath
=> $self -> {conf
} -> {original
} -> {files
} -> {archivePath
},
148 lockFile
=> $self -> {conf
} -> {original
} -> {files
} -> {sev_lock
},
149 adminDefault
=> $self -> {conf
} -> {admin
},
150 cachePath
=> $self -> {conf
} -> {original
} -> {files
} -> {cachePath
}
152 # die $stat->{(keys %$stat)[0]} if (%$stat);
156 ### sub response ###############################################################
158 # print the response to STDOUT
164 my $formdata = $self -> {conf
} -> {form_data
};
165 my $formact = $self -> {conf
} -> {form_action
};
166 my $template = $self -> {template
};
167 my $assign = $self -> {conf
} -> {assign
};
168 my $q = $self -> {cgi_object
};
170 # fill out the form field names
173 for (keys %$formdata) {
174 $pars -> {$formdata -> {$_} -> {assign
} -> {name
}} = plain
($formdata -> {$_} -> {name
}) if (
175 exists($formdata -> {$_} -> {name
})
176 and exists ($formdata -> {$_} -> {assign
})
177 and exists ($formdata -> {$_} -> {assign
} -> {name
})
181 # response the 'new message' page
183 if ($self -> {response
} -> {new_thread
}) {
185 # fill in the default form data
189 for (keys %$formdata) {
190 unless (exists ($formdata -> {$_} -> {type
}) and $formdata -> {$_} -> {type
} eq 'internal') {
191 if (exists ($formdata -> {$_} -> {default}) and exists ($formdata -> {$_} -> {assign
} -> {value
})) {
192 $default -> {$formdata -> {$_} -> {assign
} -> {value
}}
193 = $formdata -> {$_} -> {default};
195 elsif (exists($formdata -> {$_} -> {values})) {
196 my ($_name, $val) = $_;
197 $val = exists ($formdata -> {$_} -> {default})
198 ?
$formdata -> {$_} -> {default}
200 $default -> {$formdata -> {$_} -> {assign
} -> {value
}}
201 = $self -> {template
} -> list
(
204 { $assign -> {optval
} => plain
($_),
205 ((defined $val and $_ eq $val)
206 ?
($assign -> {optsel
} => 1)
210 } @
{$formdata -> {$_name} -> {values}}
217 print $q -> header
(-type
=> 'text/html');
218 print ${$template -> scrap
(
220 { $formdata->{uniqueID
} ->{assign
}->{value
} => plain
(unique_id
),
221 $formdata->{quoteChar
} ->{assign
}->{value
} => 'ÿ'.plain
($self -> {conf
} -> {admin
} -> {View
} -> {quoteChars
}),
222 $formact->{post
}->{assign
} => $formact->{post
}->{url
},
230 # check the response -> doc
232 unless ($self -> {response
} -> {doc
}) {
234 spec
=> 'unknown_error',
238 $self -> handle_error
;
240 unless ($self -> {response
} -> {doc
}) {
241 $self -> jerk
('While producing the HTML response an unknown error has occurred.');
246 # ok, print the response document to STDOUT
248 print $q -> header
(-type
=> 'text/html');
249 print ${$template -> scrap
(
250 $self -> {response
} -> {doc
},
252 $self -> {response
} -> {pars
}
259 ### sub handle_error ###########################################################
261 # analyze error data and create content for the response method
263 # Return: true if error detected
269 my $spec = $self -> {error
} -> {spec
};
271 return unless ($spec);
273 my $assign = $self -> {conf
} -> {assign
};
274 my $formdata = $self -> {conf
} -> {form_data
};
276 my $desc = $self -> {error
} -> {desc
} || '';
277 my $type = $self -> {error
} -> {type
};
280 if (exists ($formdata -> {$desc})
281 and exists ($formdata -> {$desc} -> {assign
} -> {$spec})) {
282 $emsg = $formdata -> {$desc} -> {assign
} -> {$spec};
285 $emsg = $assign -> {$spec} || '';
290 if ($type eq 'fatal') {
291 $self -> {response
} -> {doc
} = $assign -> {docFatal
};
292 $self -> {response
} -> {pars
} = {
293 $assign -> {errorMessage
} => $self -> {template
} -> insert
($emsg)
298 # user is able to repair his request
300 elsif ($type eq 'repeat' or $type eq 'fetch') {
301 $self -> {response
} -> {doc
} = $assign -> {docError
};
302 $self -> fillout_form
;
303 $self -> {response
} -> {pars
} -> {$assign -> {errorMessage
}} = $self -> {template
} -> insert
($emsg);
304 my $num = $spec eq 'too_long'
305 ?
$formdata -> {$desc} -> {maxlength
}
306 : ($spec eq 'too_short'
307 ?
$formdata -> {$desc} -> {minlength
}
311 $self -> {response
} -> {pars
} -> {$assign -> {charNum
}} = $num
318 ### sub fillout_form ###########################################################
320 # fill out the form using available form data
327 my $assign = $self -> {conf
} -> {assign
};
328 my $formdata = $self -> {conf
} -> {form_data
};
329 my $formact = $self -> {conf
} -> {form_action
};
330 my $q = $self -> {cgi_object
};
335 $pars -> {$formact -> {post
} -> {assign
}} = $formact -> {post
} -> {url
};
337 for (keys %$formdata) {
338 if ($_ eq 'quoteChar') {
339 $pars -> {$formdata->{$_}->{assign
}->{value
}}
340 = 'ÿ'.plain
($q -> param
($formdata -> {quoteChar
} -> {name
}) or '');
342 elsif (exists ($formdata -> {$_} -> {name
})) {
343 unless (exists ($formdata -> {$_} -> {values})) {
344 $pars -> {$formdata -> {$_} -> {assign
} -> {value
}}
345 = plain
($q -> param
($formdata -> {$_} -> {name
}));
349 $pars -> {$formdata -> {$_} -> {assign
} -> {value
}}
350 = $self -> {template
} -> list
(
353 { $assign -> {optval
} => plain
($_),
354 (( $_ eq $q -> param
($formdata -> {$_name} -> {name
}))
355 ?
($assign -> {optsel
} => 1)
359 } @
{$formdata -> {$_name} -> {values}}
366 $self -> {response
} -> {pars
} = $pars;
370 ### sub save ###################################################################
373 # check on legal reply or dupe is released here
380 # if an empty 'new message' document, there's nothing to save
382 return if ($self -> {response
} -> {new_thread
});
384 $self -> {check_success
} = 0;
386 # lock and load the forum main file
388 if ($self -> load_main_file
) {
390 # if a reply - is it legal?
393 if ($self -> check_reply_dupe
) {
395 unless ($self -> {response
} -> {reply
} or $self -> {response
} -> {new
}) {
396 # don't know, if we any time come to this branch
397 # the script is probably broken
400 spec
=> 'unknown_error',
406 my $formdata = $self -> {conf
} -> {form_data
};
407 my $q = $self -> {cgi_object
};
408 my $f = $self -> {forum
};
410 quoteChars
=> $q -> param
($formdata -> {quoteChar
} -> {name
}),
411 uniqueID
=> $q -> param
($formdata -> {uniqueID
} -> {name
}),
413 ip
=> $q -> remote_addr
,
414 forumFile
=> $self -> {conf
} -> {forum_file_name
},
415 messagePath
=> $self -> {conf
} -> {message_path
},
416 lastThread
=> $f -> {last_thread
},
417 lastMessage
=> $f -> {last_message
},
418 parsedThreads
=> $f -> {threads
},
420 messages
=> $self -> {conf
} -> {template
} -> {messages
} || {},
421 base_uri
=> $self -> {conf
} -> {original
} -> {files
} -> {forum_base
}
424 # set the variables if defined..
427 author
=> 'posterName',
428 email
=> 'posterEmail',
429 category
=> 'posterCategory',
430 subject
=> 'posterSubject',
431 body
=> 'posterBody',
432 homepage
=> 'posterURL',
433 image
=> 'posterImage'
437 $pars -> {$_} = $q -> param
($formdata -> {$may{$_}} -> {name
})
438 if (defined $q -> param
($formdata -> {$may{$_}} -> {name
}));
441 my ($stat, $xml, $mid, $tid);
443 # we've got a fup if it's a reply
445 if ($self -> {response
} -> {reply
}) {
446 $pars -> {parentMessage
} = $self -> {fup_mid
};
447 $pars -> {thread
} = $self -> {fup_tid
};
448 ($stat, $xml, $mid, $tid) = write_reply_posting
($pars);
451 ($stat, $xml, $mid, $tid) = write_new_thread
($pars);
462 my $cache = new Posting
::Cache
($self->{conf
}->{original
}->{files
}->{cachePath
});
463 $cache -> add_posting
(
464 { thread
=> ($tid =~ /(\d+)/)[0],
465 posting
=> ($mid =~ /(\d+)/)[0]
469 $self -> {check_success
} = 1;
470 my $thx = $self -> {conf
} -> {show_posting
} -> {thanx
};
472 # define special response data
474 $self -> {response
} -> {doc
} = $self -> {conf
} -> {assign
} -> {docThx
};
475 $self -> {response
} -> {pars
} = {
476 $thx -> {time} => plain
(hr_time
($time)),
477 $thx -> {body
} => message_as_HTML
(
481 assign
=> $self -> {conf
} -> {assign
},
482 quoteChars
=> $q -> param
($formdata -> {quoteChar
} -> {name
}),
483 quoting
=> $self -> {conf
} -> {admin
} -> {View
} -> {quoting
}
487 # set the variables if defined..
490 author
=> 'posterName',
491 email
=> 'posterEmail',
492 category
=> 'posterCategory',
493 subject
=> 'posterSubject',
494 homepage
=> 'posterURL',
495 image
=> 'posterImage'
499 my $x = $q -> param
($formdata -> {$may{$_}} -> {name
});
500 $x = '' unless (defined $x);
501 $self -> {response
} -> {pars
} -> {$thx -> {$_}} = plain
($x)
502 if (defined $thx -> {$_});
509 # unlock forum main file
511 if ($self -> {forum
} -> {flocked
}) {
512 violent_unlock_file
($self -> {conf
} -> {forum_file_name
}) unless write_unlock_file
($self -> {conf
} -> {forum_file_name
});
513 $self -> {forum
} -> {flocked
} = 0;
516 $self -> handle_error
unless $self -> {check_success
};
521 ### sub parse_cgi ##############################################################
523 # fetch and decode cgi-parameters,
524 # find out the kind of response requested by the user (new message, reply)
531 # create the CGI object
533 $self -> {cgi_object
} = new CGI
;
537 $self -> {check_success
} = $self -> check_cgi
;
542 ### sub load_main_file #########################################################
544 # load and parse the forum main file
546 # Return: Success (true/false)
552 unless ($lock_stat = write_lock_file
($self -> {conf
} -> {forum_file_name
})) {
553 if (defined $lock_stat) {
554 # occupied or no w-bit set for the directory..., hmmm
556 violent_unlock_file
($self -> {conf
} -> {forum_file_name
});
567 spec
=> 'master_lock',
574 $self -> {forum
} -> {flocked
} = 1;
575 ( $self -> {forum
} -> {threads
},
576 $self -> {forum
} -> {last_thread
},
577 $self -> {forum
} -> {last_message
},
578 $self -> {forum
} -> {dtd
},
579 $self -> {forum
} -> {unids
}
580 ) = get_all_threads
($self -> {conf
} -> {forum_file_name
}, KEEP_DELETED
);
587 ### sub check_reply_dupe #######################################################
589 # check whether a reply is legal
590 # (followup posting must exists)
592 # check whether this form request is a dupe
593 # (unique id already exists)
595 # Return: Status Code (Bool)
597 sub check_reply_dupe
{
601 # return true unless it's not a reply
605 $self -> {response
} -> {reply
}
606 or $self -> {response
} -> {new
}
609 if ($self -> {response
} -> {reply
}) {
611 my ($threads, $ftid, $fmid, $i, %msg) = (
612 $self -> {forum
} -> {threads
},
617 # thread doesn't exist
619 unless (exists($threads -> {$ftid})) {
627 # build a reverse lookup hash (mid => number in array)
628 # and ignore invisible messages
629 # (users can't reply to "deleted" msg)
631 for ($i=0; $i < @
{$threads -> {$ftid}}; $i++) {
633 if ($threads -> {$ftid} -> [$i] -> {deleted
}) {
634 $i+=$threads -> {$ftid} -> [$i] -> {answers
};
637 $msg{$threads -> {$ftid} -> [$i] -> {mid
}}=$i;
641 # message doesn't exist
643 unless (exists($msg{$fmid})) {
651 # build a unique id lookup hash
652 # use the unids of parent message's kids
654 %unids = map {$_ => 1} @
{$threads -> {$ftid} -> [$msg{$fmid}] -> {unids
}};
657 # build a unique id lookup hash, too
658 # but use only the level-zero-messages
660 %unids = map {$_ => 1} @
{$self -> {forum
} -> {unids
}};
666 $self -> {cgi_object
} -> param
(
667 $self -> {conf
} -> {form_data
} -> {uniqueID
} -> {name
})})) {
679 ### sub check_cgi ##############################################################
681 # cgi params are like raw eggs...
683 # Return: Status Code (Bool)
684 # creates content for the handle_error method if anything fails
689 # count the submitted keys and get the keys themselves
691 my %got_keys = map {($_ => 1)} $self -> {cgi_object
} -> param
;
692 my $cnt_got_keys = keys %got_keys;
693 my $formdata = $self -> {conf
} -> {form_data
};
694 my $formmust = $self -> {conf
} -> {form_must
};
696 # user requested the 'new thread' page
697 # (no params but perhaps the user-ID have been submitted)
699 if ($cnt_got_keys == 0 or (
700 exists ($formdata -> {userID
})
701 and $cnt_got_keys == 1
702 and $got_keys{$formdata -> {userID
} -> {name
}}
704 $self -> {response
} -> {new_thread
} = 1;
705 $self -> {check_success
} = 1;
709 # now we know, we've got a filled out form
710 # we do the following steps to check it:
712 # 1st: create a reverse Hash (CGI-key - identifier)
713 # 2nd: did we get _all_ must-keys?
714 # check whether reply or new message request
715 # 3rd: did we get too many keys?
716 # 4th: do _all_ submitted values accord to
718 # fetch the "missing" keys
724 exists($formdata -> {$_} -> {name
})
725 ?
($formdata -> {$_} -> {name
} => $_)
731 $self -> {response
} -> {reply
} = $got_keys {$formdata -> {followUp
} -> {name
}}?
1 : 0;
732 $self -> {response
} -> {new
} = not $self -> {response
} -> {reply
};
734 # define the fetch array (values to fetch from parent message)
736 $self -> {fetch
} = [];
738 for ( @
{$formmust -> {$self -> {response
} -> {reply
}?
'reply':'new'}} ) {
740 unless ($got_keys {$formdata -> {$_} -> {name
}}) {
742 # only miss the key unless we're able to fetch it from parent posting
745 not $self -> {response
} -> {reply
}
746 or $formdata -> {$_} -> {errorType
} eq 'fetch') {
749 spec
=> 'missing_key',
756 # keep in mind to fetch the value later
758 push @
{$self -> {fetch
}} => $_;
763 # I'm lazy - I know...
764 my $q = $self -> {cgi_object
};
769 unless (exists ($name {$_})) {
771 spec
=> 'unexpected_key',
781 unless ($self -> decode_param
) {
783 spec
=> 'unknown_encoding',
789 if ($self -> {response
} -> {reply
}) {
791 # get the parent-identifiers if we got a reply request
793 my ($ftid, $fmid) = split /;/ => $q -> param
($formdata -> {followUp
} -> {name
}) => 2;
795 unless ($ftid =~ /^\d+$/ and $fmid =~ /^\d+$/) {
797 spec
=> 'unknown_followup',
802 $self -> {fup_tid
} = $ftid;
803 $self -> {fup_mid
} = $fmid;
805 # fetch the missing keys
806 # if it fails, they're too short, too... ;)
809 $got_keys{$formdata -> {$_} -> {name
}} = 1 for (@
{$self -> {fetch
}});
812 # now we can check on length, type etc.
814 for (keys %got_keys) {
816 # we are sure, we've got only one value for one key
818 my $val = $q -> param
($_);
820 $val =~ s/\302\240/ /g; # convert nbsp (UTF-8 encoded) into normal spaces
821 $val =~ s/\015\012|\015|\012/ /g # convert \n into spaces unless it's a multiline field
823 exists ($formdata -> {$name {$_}} -> {type
})
824 and $formdata -> {$name {$_}} -> {type
} eq 'multiline-text'
827 $q -> param
($_ => $val); # write it back
831 if (length $val > $formdata -> {$name {$_}} -> {maxlength
}) {
835 type
=> $formdata -> {$name {$_}} -> {errorType
}
837 $self -> kill_param
or return;
841 # (only check if there's defined a minimum length)
843 if (exists ($formdata -> {$name {$_}} -> {minlength
})) {
845 # kill the whitespaces to get only the visible characters...
847 (my $val_ww = $val) =~ s/\s+//g;
849 if (exists ($formdata -> {$name {$_}} -> {type
}) and $formdata -> {$name {$_}} -> {type
} eq 'name') {
850 $val_ww =~ y/a-zA-Z//cd;
852 my @badlist = map {qr/\Q$_/i} qw
(
853 # insert badmatchlist here
856 push @badlist => map {qr/\b\Q$_\E\b/i} qw(
857 # insert badwordlist here
861 if ($val_ww =~ /$_/) {
872 if (length $val_ww < $formdata -> {$name {$_}} -> {minlength
}) {
876 type
=> $formdata -> {$name {$_}} -> {errorType
}
878 $self -> kill_param
or return;
882 # check the values on expected kinds of content
883 # (email, http-url, url, option)
885 if (exists ($formdata -> {$name {$_}} -> {type
}) and length $val) {
886 if ($formdata -> {$name {$_}} -> {type
} eq 'email' and not is_email
$val) {
888 spec
=> 'wrong_mail',
890 type
=> $formdata -> {$name {$_}} -> {errorType
}
892 $self -> kill_param
or return;
895 elsif ($formdata -> {$name {$_}} -> {type
} eq 'http-url' and not is_URL
$val => 'http') {
897 spec
=> 'wrong_http_url',
899 type
=> $formdata -> {$name {$_}} -> {errorType
}
901 $self -> kill_param
or return;
904 elsif ($formdata -> {$name {$_}} -> {type
} eq 'url' and not is_URL
$val => ':ALL') {
908 type
=> $formdata -> {$name {$_}} -> {errorType
}
910 $self -> kill_param
or return;
913 elsif ($formdata -> {$name {$_}} -> {type
} eq 'unique-id' and not may_id
$val) {
915 spec
=> 'wrong_unique_id',
917 type
=> $formdata -> {$name {$_}} -> {errorType
}
919 print STDERR
"Manipuliert!";
920 $self -> kill_param
or return;
924 if (exists ($formdata -> {$name {$_}} -> {values})
925 and not exists ({map {$_ => undef} @
{$formdata -> {$name {$_}} -> {values}}} -> {$val})) {
929 type
=> $formdata -> {$name {$_}} -> {errorType
}
931 $self -> kill_param
or return;
938 ### sub kill_param #############################################################
940 # kill the param (set it on '') if wrong and declared as 'kill' in config file
942 # Return: true if killed
948 if ($self -> {conf
} -> {form_data
} -> {$self -> {error
} -> {desc
}} -> {errorType
} eq 'kill') {
949 $self -> {cgi_object
} -> param
($self -> {conf
} -> {form_data
} -> {$self -> {error
} -> {desc
}} -> {name
} => '');
950 $self -> {error
} = {};
957 ### sub fetch ##################################################################
959 # fetch "missing" keys from parent posting
963 my $q = $self -> {cgi_object
};
964 my $formdata = $self -> {conf
} -> {form_data
};
966 if (@
{$self -> {fetch
}}) {
967 my $filename = $self -> {conf
} -> {message_path
}.'t'.$self -> {fup_tid
}.'.xml';
969 if (lock_file
($filename)) {
970 my $xml = parse_xml_file
($filename);
971 violent_unlock_file
($filename) unless unlock_file
($filename);
974 my $mnode = get_message_node
($xml, 't'.$self -> {fup_tid
}, 'm'.$self -> {fup_mid
});
976 my $header = get_message_header
($mnode);
978 $q -> param
($formdata -> {$_} -> {name
} => $header -> {$formdata -> {$_} -> {header
}})
979 for (@
{$self -> {fetch
}});
988 # fillout the values with an empty string
990 $q -> param
($formdata -> {$_} -> {name
} => '')
991 for (@
{$self -> {fetch
}});
996 ### sub decode_param ###########################################################
998 # convert submitted form data into UTF-8
999 # unless it's not encoded yet
1001 # Return: Status Code (Bool)
1002 # false if unknown encoding (like UTF-7 for instance)
1007 my $q = $self -> {cgi_object
};
1008 my $formdata = $self -> {conf
} -> {form_data
};
1010 my $code = $q -> param
($formdata -> {quoteChar
} -> {name
});
1013 # Latin 1 (we hope so - there's no real way to find out :-( )
1014 if ($code =~ /^\377/) {
1015 $q -> param
($_ => map {toUTF8
($_)} $q -> param
($_)) for ($q -> param
);
1018 # UTF-8 is (probably) correct,
1019 # other encodings we don't know and fail
1020 return unless $code =~ /^\303\277/;
1023 # remove the ÿ (encoded as UTF-8) from quotechars
1024 $q -> param
($formdata -> {quoteChar
} -> {name
}
1025 => substr $q -> param
($formdata -> {quoteChar
} -> {name
}),2);
1027 # ok, params now should be UTF-8 encoded
1032 my $text = $_[1] || 'An error has occurred.';
1034 Content-type: text/plain
1041 We will fix it as soon as possible. Thank you for your patience.
1050 ### end of fo_posting.pl #######################################################
patrick-canterino.de