]>
git.p6c8.net - selfforum.git/blob - selfforum-cgi/user/fo_posting.pl
dad9b8f2ff525ae09a31428099812959da5a688e
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 ################################################################################
14 use vars
qw($Bin $Shared $Script);
19 my $null = $0; $null =~ s/\\/\//g; # for win :-(
20 ($Bin) = ($null =~ /^(.*)\/.*$/)? $1 : '.';
21 $Shared = "$Bin/../shared";
22 ($Script) = ($null =~ /^.*\/(.*)$/)? $1 : $null;
26 use CGI::Carp qw(fatalsToBrowser);
31 # load script configuration and admin default conf.
33 my $conf = read_script_conf
($Bin, $Shared, $Script);
34 my $adminDefault = read_admin_conf
($conf -> {files
} -> {adminDefault
});
36 # Initialize the request
38 my $request = new Posting
::Request
($conf, $adminDefault);
40 # fetch and parse the cgi-params
42 $request -> parse_cgi
;
44 # handle errors or save the posting
46 $request -> handle_error
or $request -> save
;
54 ### main end ###################################################################
56 ################################################################################
57 ### Posting::Request ###########################################################
58 package Posting
::Request
;
61 use Encode
::Plain
; $Encode::Plain
::utf8
= 1; # generally convert from UTF-8
75 use Template
::Posting
;
79 ### sub new ####################################################################
81 # initialising the Posting::Request object
82 # check parameters and fill in object properties
85 my ($class, $conf, $adminDefault) = @_;
87 my $sp = $conf -> {show
} -> {Posting
};
92 admin
=> $adminDefault,
94 message_path
=> $conf -> {files
} -> {messagePath
},
95 forum_file_name
=> $conf -> {files
} -> {forum
},
98 assign
=> $sp -> {assign
},
99 template
=> $conf -> {template
},
100 form_must
=> $sp -> {form
} -> {must
},
101 form_data
=> $sp -> {form
} -> {data
},
102 form_action
=> $sp -> {form
} -> {action
},
105 template
=> new Template
$sp -> {templateFile
},
114 ### sub response ###############################################################
116 # print the response to STDOUT
122 my $formdata = $self -> {conf
} -> {form_data
};
123 my $formact = $self -> {conf
} -> {form_action
};
124 my $template = $self -> {template
};
125 my $assign = $self -> {conf
} -> {assign
};
126 my $q = $self -> {cgi_object
};
128 # fill out the form field names
131 for (keys %$formdata) {
132 $pars -> {$formdata -> {$_} -> {assign
} -> {name
}} = plain
($formdata -> {$_} -> {name
}) if (
133 exists($formdata -> {$_} -> {name
})
134 and exists ($formdata -> {$_} -> {assign
})
135 and exists ($formdata -> {$_} -> {assign
} -> {name
})
139 # response the 'new message' page
141 if ($self -> {response
} -> {new_thread
}) {
143 # fill in the default form data
147 for (keys %$formdata) {
148 unless (exists ($formdata -> {$_} -> {type
}) and $formdata -> {$_} -> {type
} eq 'internal') {
149 if (exists ($formdata -> {$_} -> {default}) and exists ($formdata -> {$_} -> {assign
} -> {value
})) {
150 $default -> {$formdata -> {$_} -> {assign
} -> {value
}}
151 = $formdata -> {$_} -> {default};
153 elsif (exists($formdata -> {$_} -> {values})) {
154 my ($_name, $val) = $_;
155 $val = exists ($formdata -> {$_} -> {default})
156 ?
$formdata -> {$_} -> {default}
158 $default -> {$formdata -> {$_} -> {assign
} -> {value
}}
159 = $self -> {template
} -> list
(
162 { $assign -> {optval
} => plain
($_),
163 ((defined $val and $_ eq $val)
164 ?
($assign -> {optsel
} => 1)
168 } @
{$formdata -> {$_name} -> {values}}
175 print $q -> header
(-type
=> 'text/html');
176 print ${$template -> scrap
(
178 { $formdata->{uniqueID
} ->{assign
}->{value
} => plain
(unique_id
),
179 $formdata->{quoteChar
} ->{assign
}->{value
} => 'ÿ'.plain
($self -> {conf
} -> {admin
} -> {View
} -> {quoteChars
}),
180 $formact->{post
}->{assign
} => $formact->{post
}->{url
},
188 # check the response -> doc
190 unless ($self -> {response
} -> {doc
}) {
192 spec
=> 'unknown_error',
196 $self -> handle_error
;
198 unless ($self -> {response
} -> {doc
}) {
199 $self -> jerk
('While producing the HTML response an unknown error has occurred.');
204 # ok, print the response document to STDOUT
206 print $q -> header
(-type
=> 'text/html');
207 print ${$template -> scrap
(
208 $self -> {response
} -> {doc
},
210 $self -> {response
} -> {pars
}
217 ### sub handle_error ###########################################################
219 # analyze error data and create content for the response method
221 # Return: true if error detected
227 my $spec = $self -> {error
} -> {spec
};
229 return unless ($spec);
231 my $assign = $self -> {conf
} -> {assign
};
232 my $formdata = $self -> {conf
} -> {form_data
};
234 my $desc = $self -> {error
} -> {desc
} || '';
235 my $type = $self -> {error
} -> {type
};
238 if (exists ($formdata -> {$desc})
239 and exists ($formdata -> {$desc} -> {assign
} -> {$spec})) {
240 $emsg = $formdata -> {$desc} -> {assign
} -> {$spec};
243 $emsg = $assign -> {$spec} || '';
248 if ($type eq 'fatal') {
249 $self -> {response
} -> {doc
} = $assign -> {docFatal
};
250 $self -> {response
} -> {pars
} = {
251 $assign -> {errorMessage
} => $self -> {template
} -> insert
($emsg)
256 # user is able to repair his request
258 elsif ($type eq 'repeat' or $type eq 'fetch') {
259 $self -> {response
} -> {doc
} = $assign -> {docError
};
260 $self -> fillout_form
;
261 $self -> {response
} -> {pars
} -> {$assign -> {errorMessage
}} = $self -> {template
} -> insert
($emsg);
262 my $num = $spec eq 'too_long'
263 ?
$formdata -> {$desc} -> {maxlength
}
264 : ($spec eq 'too_short'
265 ?
$formdata -> {$desc} -> {minlength
}
269 $self -> {response
} -> {pars
} -> {$assign -> {charNum
}} = $num
276 ### sub fillout_form ###########################################################
278 # fill out the form using available form data
285 my $assign = $self -> {conf
} -> {assign
};
286 my $formdata = $self -> {conf
} -> {form_data
};
287 my $formact = $self -> {conf
} -> {form_action
};
288 my $q = $self -> {cgi_object
};
293 $pars -> {$formact -> {post
} -> {assign
}} = $formact -> {post
} -> {url
};
295 for (keys %$formdata) {
296 if ($_ eq 'quoteChar') {
297 $pars -> {$formdata->{$_}->{assign
}->{value
}}
298 = 'ÿ'.plain
($q -> param
($formdata -> {quoteChar
} -> {name
}) or '');
300 elsif (exists ($formdata -> {$_} -> {name
})) {
301 unless (exists ($formdata -> {$_} -> {values})) {
302 $pars -> {$formdata -> {$_} -> {assign
} -> {value
}}
303 = plain
($q -> param
($formdata -> {$_} -> {name
}));
307 $pars -> {$formdata -> {$_} -> {assign
} -> {value
}}
308 = $self -> {template
} -> list
(
311 { $assign -> {optval
} => plain
($_),
312 (( $_ eq $q -> param
($formdata -> {$_name} -> {name
}))
313 ?
($assign -> {optsel
} => 1)
317 } @
{$formdata -> {$_name} -> {values}}
324 $self -> {response
} -> {pars
} = $pars;
328 ### sub save ###################################################################
331 # check on legal reply or dupe is released here
338 # if an empty 'new message' document, there's nothing to save
340 return if ($self -> {response
} -> {new_thread
});
342 $self -> {check_success
} = 0;
344 # lock and load the forum main file
346 if ($self -> load_main_file
) {
348 # if a reply - is it legal?
351 if ($self -> check_reply_dupe
) {
353 unless ($self -> {response
} -> {reply
} or $self -> {response
} -> {new
}) {
354 # don't know, if we any time come to this branch
355 # the script is probably broken
358 spec
=> 'unknown_error',
364 my $formdata = $self -> {conf
} -> {form_data
};
365 my $q = $self -> {cgi_object
};
366 my $f = $self -> {forum
};
368 quoteChars
=> $q -> param
($formdata -> {quoteChar
} -> {name
}),
369 uniqueID
=> $q -> param
($formdata -> {uniqueID
} -> {name
}),
371 ip
=> $q -> remote_addr
,
372 forumFile
=> $self -> {conf
} -> {forum_file_name
},
373 messagePath
=> $self -> {conf
} -> {message_path
},
374 lastThread
=> $f -> {last_thread
},
375 lastMessage
=> $f -> {last_message
},
376 parsedThreads
=> $f -> {threads
},
378 messages
=> $self -> {template
} -> {messages
} || {},
381 # set the variables if defined..
384 author
=> 'posterName',
385 email
=> 'posterEmail',
386 category
=> 'posterCategory',
387 subject
=> 'posterSubject',
388 body
=> 'posterBody',
389 homepage
=> 'posterURL',
390 image
=> 'posterImage'
394 $pars -> {$_} = $q -> param
($formdata -> {$may{$_}} -> {name
})
395 if (defined $q -> param
($formdata -> {$may{$_}} -> {name
}));
398 my ($stat, $xml, $mid);
400 # we've got a fup if it's a reply
402 if ($self -> {response
} -> {reply
}) {
403 $pars -> {parentMessage
} = $self -> {fup_mid
};
404 $pars -> {thread
} = $self -> {fup_tid
};
405 ($stat, $xml, $mid) = write_reply_posting
($pars);
408 ($stat, $xml, $mid) = write_new_thread
($pars);
419 $self -> {check_success
} = 1;
420 my $thx = $self -> {conf
} -> {show_posting
} -> {thanx
};
422 # define special response data
424 $self -> {response
} -> {doc
} = $self -> {conf
} -> {assign
} -> {docThx
};
425 $self -> {response
} -> {pars
} = {
426 $thx -> {time} => plain
(hr_time
($time)),
427 $thx -> {body
} => message_as_HTML
(
431 assign
=> $self -> {conf
} -> {assign
},
432 quoteChars
=> $q -> param
($formdata -> {quoteChar
} -> {name
}),
433 quoting
=> $self -> {conf
} -> {admin
} -> {View
} -> {quoting
}
437 # set the variables if defined..
440 author
=> 'posterName',
441 email
=> 'posterEmail',
442 category
=> 'posterCategory',
443 subject
=> 'posterSubject',
444 homepage
=> 'posterURL',
445 image
=> 'posterImage'
449 my $x = $q -> param
($formdata -> {$may{$_}} -> {name
});
450 $x = '' unless (defined $x);
451 $self -> {response
} -> {pars
} -> {$thx -> {$_}} = plain
($x)
452 if (defined $thx -> {$_});
459 # unlock forum main file
461 if ($self -> {forum
} -> {flocked
}) {
462 violent_unlock_file
($self -> {conf
} -> {forum_file_name
}) unless write_unlock_file
($self -> {conf
} -> {forum_file_name
});
463 $self -> {forum
} -> {flocked
} = 0;
466 $self -> handle_error
unless $self -> {check_success
};
471 ### sub parse_cgi ##############################################################
473 # fetch and decode cgi-parameters,
474 # find out the kind of response requested by the user (new message, reply)
481 # create the CGI object
483 $self -> {cgi_object
} = new CGI
;
487 $self -> {check_success
} = $self -> check_cgi
;
492 ### sub load_main_file #########################################################
494 # load and parse the forum main file
496 # Return: Success (true/false)
502 unless ($lock_stat = write_lock_file
($self -> {conf
} -> {forum_file_name
})) {
503 if (defined $lock_stat and $lock_stat == 0) {
504 # occupied or no w-bit set for the directory..., hmmm
506 violent_unlock_file
($self -> {conf
} -> {forum_file_name
});
517 spec
=> 'master_lock',
524 $self -> {forum
} -> {flocked
} = 1;
525 ( $self -> {forum
} -> {threads
},
526 $self -> {forum
} -> {last_thread
},
527 $self -> {forum
} -> {last_message
},
528 $self -> {forum
} -> {dtd
},
529 $self -> {forum
} -> {unids
}
530 ) = get_all_threads
($self -> {conf
} -> {forum_file_name
}, KEEP_DELETED
);
537 ### sub check_reply_dupe #######################################################
539 # check whether a reply is legal
540 # (followup posting must exists)
542 # check whether this form request is a dupe
543 # (unique id already exists)
545 # Return: Status Code (Bool)
547 sub check_reply_dupe
{
551 # return true unless it's not a reply
555 $self -> {response
} -> {reply
}
556 or $self -> {response
} -> {new
}
559 if ($self -> {response
} -> {reply
}) {
561 my ($threads, $ftid, $fmid, $i, %msg) = (
562 $self -> {forum
} -> {threads
},
567 # thread doesn't exist
569 unless (exists($threads -> {$ftid})) {
577 # build a reverse lookup hash (mid => number in array)
578 # and ignore invisible messages
579 # (users can't reply to "deleted" msg)
581 for ($i=0; $i < @
{$threads -> {$ftid}}; $i++) {
583 if ($threads -> {$ftid} -> [$i] -> {deleted
}) {
584 $i+=$threads -> {$ftid} -> [$i] -> {answers
};
587 $msg{$threads -> {$ftid} -> [$i] -> {mid
}}=$i;
591 # message doesn't exist
593 unless (exists($msg{$fmid})) {
601 # build a unique id lookup hash
602 # use the unids of parent message's kids
604 %unids = map {$_ => 1} @
{$threads -> {$ftid} -> [$msg{$fmid}] -> {unids
}};
607 # build a unique id lookup hash, too
608 # but use only the level-zero-messages
610 %unids = map {$_ => 1} @
{$self -> {forum
} -> {unids
}};
616 $self -> {cgi_object
} -> param
(
617 $self -> {conf
} -> {form_data
} -> {uniqueID
} -> {name
})})) {
629 ### sub check_cgi ##############################################################
631 # cgi params are like raw eggs...
633 # Return: Status Code (Bool)
634 # creates content for the handle_error method if anything fails
639 # count the submitted keys and get the keys themselves
641 my %got_keys = map {($_ => 1)} $self -> {cgi_object
} -> param
;
642 my $cnt_got_keys = keys %got_keys;
643 my $formdata = $self -> {conf
} -> {form_data
};
644 my $formmust = $self -> {conf
} -> {form_must
};
646 # user requested the 'new thread' page
647 # (no params but perhaps the user-ID have been submitted)
649 if ($cnt_got_keys == 0 or (
650 exists ($formdata -> {userID
})
651 and $cnt_got_keys == 1
652 and $got_keys{$formdata -> {userID
} -> {name
}}
654 $self -> {response
} -> {new_thread
} = 1;
655 $self -> {check_success
} = 1;
659 # now we know, we've got a filled out form
660 # we do the following steps to check it:
662 # 1st: create a reverse Hash (CGI-key - identifier)
663 # 2nd: did we get _all_ must-keys?
664 # check whether reply or new message request
665 # 3rd: did we get too many keys?
666 # 4th: do _all_ submitted values accord to
668 # fetch the "missing" keys
674 exists($formdata -> {$_} -> {name
})
675 ?
($formdata -> {$_} -> {name
} => $_)
681 $self -> {response
} -> {reply
} = $got_keys {$formdata -> {followUp
} -> {name
}}?
1 : 0;
682 $self -> {response
} -> {new
} = not $self -> {response
} -> {reply
};
684 # define the fetch array (values to fetch from parent message)
686 $self -> {fetch
} = [];
688 for ( @
{$formmust -> {$self -> {response
} -> {reply
}?
'reply':'new'}} ) {
690 unless ($got_keys {$formdata -> {$_} -> {name
}}) {
692 # only miss the key unless we're able to fetch it from parent posting
695 not $self -> {response
} -> {reply
}
696 or $formdata -> {$_} -> {errorType
} eq 'fetch') {
699 spec
=> 'missing_key',
706 # keep in mind to fetch the value later
708 push @
{$self -> {fetch
}} => $_;
713 # I'm lazy - I know...
714 my $q = $self -> {cgi_object
};
719 unless (exists ($name {$_})) {
721 spec
=> 'unexpected_key',
731 unless ($self -> decode_param
) {
733 spec
=> 'unknown_encoding',
739 if ($self -> {response
} -> {reply
}) {
741 # get the parent-identifiers if we got a reply request
743 my ($ftid, $fmid) = split /;/ => $q -> param
($formdata -> {followUp
} -> {name
}) => 2;
745 unless ($ftid =~ /\d+/ and $fmid =~ /\d+/) {
747 spec
=> 'unknown_followup',
752 $self -> {fup_tid
} = $ftid;
753 $self -> {fup_mid
} = $fmid;
755 # fetch the missing keys
756 # if it fails, they're too short, too... ;)
759 $got_keys{$formdata -> {$_} -> {name
}} = 1 for (@
{$self -> {fetch
}});
762 # now we can check on length, type etc.
764 for (keys %got_keys) {
766 # we are sure, we've got only one value for one key
768 my $val = $q -> param
($_);
770 $val =~ s/\302\240/ /g; # convert nbsp (UTF-8 encoded) into normal spaces
771 $val =~ s/\015\012|\015|\012/ /g # convert \n into spaces unless it's a multiline field
773 exists ($formdata -> {$name {$_}} -> {type
})
774 and $formdata -> {$name {$_}} -> {type
} eq 'multiline-text'
777 $q -> param
($_ => $val); # write it back
781 if (length $val > $formdata -> {$name {$_}} -> {maxlength
}) {
785 type
=> $formdata -> {$name {$_}} -> {errorType
}
787 $self -> kill_param
or return;
791 # (only check if there's defined a minimum length)
793 if (exists ($formdata -> {$name {$_}} -> {minlength
})) {
795 # kill the whitespaces to get only the visible characters...
797 (my $val_ww = $val) =~ s/\s+//g;
799 if (length $val_ww < $formdata -> {$name {$_}} -> {minlength
}) {
803 type
=> $formdata -> {$name {$_}} -> {errorType
}
805 $self -> kill_param
or return;
809 # check the values on expected kinds of content
810 # (email, http-url, url, option)
812 if (exists ($formdata -> {$name {$_}} -> {type
}) and length $val) {
813 if ($formdata -> {$name {$_}} -> {type
} eq 'email' and not is_email
$val) {
815 spec
=> 'wrong_mail',
817 type
=> $formdata -> {$name {$_}} -> {errorType
}
819 $self -> kill_param
or return;
822 elsif ($formdata -> {$name {$_}} -> {type
} eq 'http-url' and not is_URL
$val => 'http') {
824 spec
=> 'wrong_http_url',
826 type
=> $formdata -> {$name {$_}} -> {errorType
}
828 $self -> kill_param
or return;
831 elsif ($formdata -> {$name {$_}} -> {type
} eq 'url' and not is_URL
$val => ':ALL') {
835 type
=> $formdata -> {$name {$_}} -> {errorType
}
837 $self -> kill_param
or return;
841 if (exists ($formdata -> {$name {$_}} -> {values})
842 and not exists ({map {$_ => undef} @
{$formdata -> {$name {$_}} -> {values}}} -> {$val})) {
846 type
=> $formdata -> {$name {$_}} -> {errorType
}
848 $self -> kill_param
or return;
855 ### sub kill_param #############################################################
857 # kill the param (set it on '') if wrong and declared as 'kill' in config file
859 # Return: true if killed
865 if ($self -> {conf
} -> {form_data
} -> {$self -> {error
} -> {desc
}} -> {errorType
} eq 'kill') {
866 $self -> {cgi_object
} -> param
($self -> {conf
} -> {form_data
} -> {$self -> {error
} -> {desc
}} -> {name
} => '');
867 $self -> {error
} = {};
874 ### sub fetch ##################################################################
876 # fetch "missing" keys from parent posting
880 my $q = $self -> {cgi_object
};
881 my $formdata = $self -> {conf
} -> {form_data
};
883 if (@
{$self -> {fetch
}}) {
884 my $filename = $self -> {conf
} -> {message_path
}.'t'.$self -> {fup_tid
}.'.xml';
886 if (lock_file
($filename)) {
887 my $xml = parse_xml_file
($filename);
888 violent_unlock_file
($filename) unless unlock_file
($filename);
891 my $mnode = get_message_node
($xml, 't'.$self -> {fup_tid
}, 'm'.$self -> {fup_mid
});
893 my $header = get_message_header
($mnode);
895 $q -> param
($formdata -> {$_} -> {name
} => $header -> {$formdata -> {$_} -> {header
}})
896 for (@
{$self -> {fetch
}});
905 # fillout the values with an empty string
907 $q -> param
($formdata -> {$_} -> {name
} => '')
908 for (@
{$self -> {fetch
}});
913 ### sub decode_param ###########################################################
915 # convert submitted form data into UTF-8
916 # unless it's not encoded yet
918 # Return: Status Code (Bool)
919 # false if unknown encoding (like UTF-7 for instance)
924 my $q = $self -> {cgi_object
};
925 my $formdata = $self -> {conf
} -> {form_data
};
927 my $code = $q -> param
($formdata -> {quoteChar
} -> {name
});
930 # Latin 1 (we hope so - there's no real way to find out :-( )
931 if ($code =~ /^\377/) {
932 $q -> param
($_ => map {toUTF8
($_)} $q -> param
($_)) for ($q -> param
);
935 # UTF-8 is (probably) correct,
936 # other encodings we don't know and fail
937 return unless $code =~ /^\303\277/;
940 # remove the ÿ (encoded as UTF-8) from quotechars
941 $q -> param
($formdata -> {quoteChar
} -> {name
}
942 => substr $q -> param
($formdata -> {quoteChar
} -> {name
}),2);
944 # ok, params now should be UTF-8 encoded
949 my $text = $_[1] || 'An error has occurred.';
951 Content-type: text/plain
958 We will fix it as soon as possible. Thank you for your patience.
967 ### end of fo_posting.pl #######################################################
patrick-canterino.de