]>
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 ################################################################################
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 -> {conf
} -> {template
} -> {messages
} || {},
379 base_uri
=> $self -> {conf
} -> {original
} -> {files
} -> {forum_base
}
382 # set the variables if defined..
385 author
=> 'posterName',
386 email
=> 'posterEmail',
387 category
=> 'posterCategory',
388 subject
=> 'posterSubject',
389 body
=> 'posterBody',
390 homepage
=> 'posterURL',
391 image
=> 'posterImage'
395 $pars -> {$_} = $q -> param
($formdata -> {$may{$_}} -> {name
})
396 if (defined $q -> param
($formdata -> {$may{$_}} -> {name
}));
399 my ($stat, $xml, $mid);
401 # we've got a fup if it's a reply
403 if ($self -> {response
} -> {reply
}) {
404 $pars -> {parentMessage
} = $self -> {fup_mid
};
405 $pars -> {thread
} = $self -> {fup_tid
};
406 ($stat, $xml, $mid) = write_reply_posting
($pars);
409 ($stat, $xml, $mid) = write_new_thread
($pars);
420 $self -> {check_success
} = 1;
421 my $thx = $self -> {conf
} -> {show_posting
} -> {thanx
};
423 # define special response data
425 $self -> {response
} -> {doc
} = $self -> {conf
} -> {assign
} -> {docThx
};
426 $self -> {response
} -> {pars
} = {
427 $thx -> {time} => plain
(hr_time
($time)),
428 $thx -> {body
} => message_as_HTML
(
432 assign
=> $self -> {conf
} -> {assign
},
433 quoteChars
=> $q -> param
($formdata -> {quoteChar
} -> {name
}),
434 quoting
=> $self -> {conf
} -> {admin
} -> {View
} -> {quoting
}
438 # set the variables if defined..
441 author
=> 'posterName',
442 email
=> 'posterEmail',
443 category
=> 'posterCategory',
444 subject
=> 'posterSubject',
445 homepage
=> 'posterURL',
446 image
=> 'posterImage'
450 my $x = $q -> param
($formdata -> {$may{$_}} -> {name
});
451 $x = '' unless (defined $x);
452 $self -> {response
} -> {pars
} -> {$thx -> {$_}} = plain
($x)
453 if (defined $thx -> {$_});
460 # unlock forum main file
462 if ($self -> {forum
} -> {flocked
}) {
463 violent_unlock_file
($self -> {conf
} -> {forum_file_name
}) unless write_unlock_file
($self -> {conf
} -> {forum_file_name
});
464 $self -> {forum
} -> {flocked
} = 0;
467 $self -> handle_error
unless $self -> {check_success
};
472 ### sub parse_cgi ##############################################################
474 # fetch and decode cgi-parameters,
475 # find out the kind of response requested by the user (new message, reply)
482 # create the CGI object
484 $self -> {cgi_object
} = new CGI
;
488 $self -> {check_success
} = $self -> check_cgi
;
493 ### sub load_main_file #########################################################
495 # load and parse the forum main file
497 # Return: Success (true/false)
503 unless ($lock_stat = write_lock_file
($self -> {conf
} -> {forum_file_name
})) {
504 if (defined $lock_stat) {
505 # occupied or no w-bit set for the directory..., hmmm
507 violent_unlock_file
($self -> {conf
} -> {forum_file_name
});
518 spec
=> 'master_lock',
525 $self -> {forum
} -> {flocked
} = 1;
526 ( $self -> {forum
} -> {threads
},
527 $self -> {forum
} -> {last_thread
},
528 $self -> {forum
} -> {last_message
},
529 $self -> {forum
} -> {dtd
},
530 $self -> {forum
} -> {unids
}
531 ) = get_all_threads
($self -> {conf
} -> {forum_file_name
}, KEEP_DELETED
);
538 ### sub check_reply_dupe #######################################################
540 # check whether a reply is legal
541 # (followup posting must exists)
543 # check whether this form request is a dupe
544 # (unique id already exists)
546 # Return: Status Code (Bool)
548 sub check_reply_dupe
{
552 # return true unless it's not a reply
556 $self -> {response
} -> {reply
}
557 or $self -> {response
} -> {new
}
560 if ($self -> {response
} -> {reply
}) {
562 my ($threads, $ftid, $fmid, $i, %msg) = (
563 $self -> {forum
} -> {threads
},
568 # thread doesn't exist
570 unless (exists($threads -> {$ftid})) {
578 # build a reverse lookup hash (mid => number in array)
579 # and ignore invisible messages
580 # (users can't reply to "deleted" msg)
582 for ($i=0; $i < @
{$threads -> {$ftid}}; $i++) {
584 if ($threads -> {$ftid} -> [$i] -> {deleted
}) {
585 $i+=$threads -> {$ftid} -> [$i] -> {answers
};
588 $msg{$threads -> {$ftid} -> [$i] -> {mid
}}=$i;
592 # message doesn't exist
594 unless (exists($msg{$fmid})) {
602 # build a unique id lookup hash
603 # use the unids of parent message's kids
605 %unids = map {$_ => 1} @
{$threads -> {$ftid} -> [$msg{$fmid}] -> {unids
}};
608 # build a unique id lookup hash, too
609 # but use only the level-zero-messages
611 %unids = map {$_ => 1} @
{$self -> {forum
} -> {unids
}};
617 $self -> {cgi_object
} -> param
(
618 $self -> {conf
} -> {form_data
} -> {uniqueID
} -> {name
})})) {
630 ### sub check_cgi ##############################################################
632 # cgi params are like raw eggs...
634 # Return: Status Code (Bool)
635 # creates content for the handle_error method if anything fails
640 # count the submitted keys and get the keys themselves
642 my %got_keys = map {($_ => 1)} $self -> {cgi_object
} -> param
;
643 my $cnt_got_keys = keys %got_keys;
644 my $formdata = $self -> {conf
} -> {form_data
};
645 my $formmust = $self -> {conf
} -> {form_must
};
647 # user requested the 'new thread' page
648 # (no params but perhaps the user-ID have been submitted)
650 if ($cnt_got_keys == 0 or (
651 exists ($formdata -> {userID
})
652 and $cnt_got_keys == 1
653 and $got_keys{$formdata -> {userID
} -> {name
}}
655 $self -> {response
} -> {new_thread
} = 1;
656 $self -> {check_success
} = 1;
660 # now we know, we've got a filled out form
661 # we do the following steps to check it:
663 # 1st: create a reverse Hash (CGI-key - identifier)
664 # 2nd: did we get _all_ must-keys?
665 # check whether reply or new message request
666 # 3rd: did we get too many keys?
667 # 4th: do _all_ submitted values accord to
669 # fetch the "missing" keys
675 exists($formdata -> {$_} -> {name
})
676 ?
($formdata -> {$_} -> {name
} => $_)
682 $self -> {response
} -> {reply
} = $got_keys {$formdata -> {followUp
} -> {name
}}?
1 : 0;
683 $self -> {response
} -> {new
} = not $self -> {response
} -> {reply
};
685 # define the fetch array (values to fetch from parent message)
687 $self -> {fetch
} = [];
689 for ( @
{$formmust -> {$self -> {response
} -> {reply
}?
'reply':'new'}} ) {
691 unless ($got_keys {$formdata -> {$_} -> {name
}}) {
693 # only miss the key unless we're able to fetch it from parent posting
696 not $self -> {response
} -> {reply
}
697 or $formdata -> {$_} -> {errorType
} eq 'fetch') {
700 spec
=> 'missing_key',
707 # keep in mind to fetch the value later
709 push @
{$self -> {fetch
}} => $_;
714 # I'm lazy - I know...
715 my $q = $self -> {cgi_object
};
720 unless (exists ($name {$_})) {
722 spec
=> 'unexpected_key',
732 unless ($self -> decode_param
) {
734 spec
=> 'unknown_encoding',
740 if ($self -> {response
} -> {reply
}) {
742 # get the parent-identifiers if we got a reply request
744 my ($ftid, $fmid) = split /;/ => $q -> param
($formdata -> {followUp
} -> {name
}) => 2;
746 unless ($ftid =~ /\d+/ and $fmid =~ /\d+/) {
748 spec
=> 'unknown_followup',
753 $self -> {fup_tid
} = $ftid;
754 $self -> {fup_mid
} = $fmid;
756 # fetch the missing keys
757 # if it fails, they're too short, too... ;)
760 $got_keys{$formdata -> {$_} -> {name
}} = 1 for (@
{$self -> {fetch
}});
763 # now we can check on length, type etc.
765 for (keys %got_keys) {
767 # we are sure, we've got only one value for one key
769 my $val = $q -> param
($_);
771 $val =~ s/\302\240/ /g; # convert nbsp (UTF-8 encoded) into normal spaces
772 $val =~ s/\015\012|\015|\012/ /g # convert \n into spaces unless it's a multiline field
774 exists ($formdata -> {$name {$_}} -> {type
})
775 and $formdata -> {$name {$_}} -> {type
} eq 'multiline-text'
778 $q -> param
($_ => $val); # write it back
782 if (length $val > $formdata -> {$name {$_}} -> {maxlength
}) {
786 type
=> $formdata -> {$name {$_}} -> {errorType
}
788 $self -> kill_param
or return;
792 # (only check if there's defined a minimum length)
794 if (exists ($formdata -> {$name {$_}} -> {minlength
})) {
796 # kill the whitespaces to get only the visible characters...
798 (my $val_ww = $val) =~ s/\s+//g;
800 if (length $val_ww < $formdata -> {$name {$_}} -> {minlength
}) {
804 type
=> $formdata -> {$name {$_}} -> {errorType
}
806 $self -> kill_param
or return;
810 # check the values on expected kinds of content
811 # (email, http-url, url, option)
813 if (exists ($formdata -> {$name {$_}} -> {type
}) and length $val) {
814 if ($formdata -> {$name {$_}} -> {type
} eq 'email' and not is_email
$val) {
816 spec
=> 'wrong_mail',
818 type
=> $formdata -> {$name {$_}} -> {errorType
}
820 $self -> kill_param
or return;
823 elsif ($formdata -> {$name {$_}} -> {type
} eq 'http-url' and not is_URL
$val => 'http') {
825 spec
=> 'wrong_http_url',
827 type
=> $formdata -> {$name {$_}} -> {errorType
}
829 $self -> kill_param
or return;
832 elsif ($formdata -> {$name {$_}} -> {type
} eq 'url' and not is_URL
$val => ':ALL') {
836 type
=> $formdata -> {$name {$_}} -> {errorType
}
838 $self -> kill_param
or return;
842 if (exists ($formdata -> {$name {$_}} -> {values})
843 and not exists ({map {$_ => undef} @
{$formdata -> {$name {$_}} -> {values}}} -> {$val})) {
847 type
=> $formdata -> {$name {$_}} -> {errorType
}
849 $self -> kill_param
or return;
856 ### sub kill_param #############################################################
858 # kill the param (set it on '') if wrong and declared as 'kill' in config file
860 # Return: true if killed
866 if ($self -> {conf
} -> {form_data
} -> {$self -> {error
} -> {desc
}} -> {errorType
} eq 'kill') {
867 $self -> {cgi_object
} -> param
($self -> {conf
} -> {form_data
} -> {$self -> {error
} -> {desc
}} -> {name
} => '');
868 $self -> {error
} = {};
875 ### sub fetch ##################################################################
877 # fetch "missing" keys from parent posting
881 my $q = $self -> {cgi_object
};
882 my $formdata = $self -> {conf
} -> {form_data
};
884 if (@
{$self -> {fetch
}}) {
885 my $filename = $self -> {conf
} -> {message_path
}.'t'.$self -> {fup_tid
}.'.xml';
887 if (lock_file
($filename)) {
888 my $xml = parse_xml_file
($filename);
889 violent_unlock_file
($filename) unless unlock_file
($filename);
892 my $mnode = get_message_node
($xml, 't'.$self -> {fup_tid
}, 'm'.$self -> {fup_mid
});
894 my $header = get_message_header
($mnode);
896 $q -> param
($formdata -> {$_} -> {name
} => $header -> {$formdata -> {$_} -> {header
}})
897 for (@
{$self -> {fetch
}});
906 # fillout the values with an empty string
908 $q -> param
($formdata -> {$_} -> {name
} => '')
909 for (@
{$self -> {fetch
}});
914 ### sub decode_param ###########################################################
916 # convert submitted form data into UTF-8
917 # unless it's not encoded yet
919 # Return: Status Code (Bool)
920 # false if unknown encoding (like UTF-7 for instance)
925 my $q = $self -> {cgi_object
};
926 my $formdata = $self -> {conf
} -> {form_data
};
928 my $code = $q -> param
($formdata -> {quoteChar
} -> {name
});
931 # Latin 1 (we hope so - there's no real way to find out :-( )
932 if ($code =~ /^\377/) {
933 $q -> param
($_ => map {toUTF8
($_)} $q -> param
($_)) for ($q -> param
);
936 # UTF-8 is (probably) correct,
937 # other encodings we don't know and fail
938 return unless $code =~ /^\303\277/;
941 # remove the ÿ (encoded as UTF-8) from quotechars
942 $q -> param
($formdata -> {quoteChar
} -> {name
}
943 => substr $q -> param
($formdata -> {quoteChar
} -> {name
}),2);
945 # ok, params now should be UTF-8 encoded
950 my $text = $_[1] || 'An error has occurred.';
952 Content-type: text/plain
959 We will fix it as soon as possible. Thank you for your patience.
968 ### end of fo_posting.pl #######################################################
patrick-canterino.de