]>
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> #
9 # Description: Accept new postings, display "Neue Nachricht" page #
11 ################################################################################
24 # my $null = $0; $null =~ s/\\/\//g; # for win :-(
25 # $Bin = ($null =~ /^(.*)\/.*$/)? $1 : '.';
26 # $Shared = "$Bin/../shared";
27 # $Config = "$Bin/config";
28 # $Script = ($null =~ /^.*\/(.*)$/)? $1 : $null;
31 $Bin = ($null =~ /^(.*)\/.*$/)?
$1 : '.';
32 $Config = "$Bin/../../cgi-config/forum";
33 $Shared = "$Bin/../../cgi-shared";
34 $Script = ($null =~ /^.*\/(.*)$/)?
$1 : $null;
37 # setting umask, remove or comment it, if you don't need
42 use CGI
::Carp
qw(fatalsToBrowser);
48 ################################################################################
56 sub VERSION
{(q
$Revision$ =~ /([\d.]+)\s*$/)[0] or '0.0'}
58 # load script configuration and admin default conf.
60 my $conf = read_script_conf
($Config, $Shared, $Script);
61 my $adminDefault = read_admin_conf
($conf -> {files
} -> {adminDefault
});
63 # Initialize the request
65 my $request = new Posting
::Request
($conf, $adminDefault);
67 # fetch and parse the cgi-params
69 $request -> parse_cgi
;
71 # handle errors or save the posting
73 $request -> handle_error
or $request -> save
;
79 # shorten the main file?
81 $request -> severance
;
85 ### main end ###################################################################
87 ################################################################################
88 ### Posting::Request ###########################################################
89 package Posting
::Request
;
93 use Encode
::Plain
; $Encode::Plain
::utf8
= 1;
107 use Template
::Posting
;
111 ### sub new ####################################################################
113 # initialising the Posting::Request object
114 # check parameters and fill in object properties
117 my ($class, $conf, $adminDefault) = @_;
119 my $sp = $conf -> {show
} -> {Posting
};
124 admin
=> $adminDefault,
126 message_path
=> $conf -> {files
} -> {messagePath
},
127 forum_file_name
=> $conf -> {files
} -> {forum
},
130 assign
=> $sp -> {assign
},
131 template
=> $conf -> {template
},
132 form_must
=> $sp -> {form
} -> {must
},
133 form_data
=> $sp -> {form
} -> {data
},
134 form_action
=> $sp -> {form
} -> {action
},
137 template
=> new Template
$sp -> {templateFile
},
149 start_severance
($self -> {conf
} -> {original
} -> {files
} -> {sev_app
});
152 ### sub response ###############################################################
154 # print the response to STDOUT
160 my $formdata = $self -> {conf
} -> {form_data
};
161 my $formact = $self -> {conf
} -> {form_action
};
162 my $template = $self -> {template
};
163 my $assign = $self -> {conf
} -> {assign
};
164 my $q = $self -> {cgi_object
};
166 # fill out the form field names
169 for (keys %$formdata) {
170 $pars -> {$formdata -> {$_} -> {assign
} -> {name
}} = plain
($formdata -> {$_} -> {name
}) if (
171 exists($formdata -> {$_} -> {name
})
172 and exists ($formdata -> {$_} -> {assign
})
173 and exists ($formdata -> {$_} -> {assign
} -> {name
})
177 # response the 'new message' page
179 if ($self -> {response
} -> {new_thread
}) {
181 # fill in the default form data
185 for (keys %$formdata) {
186 unless (exists ($formdata -> {$_} -> {type
}) and $formdata -> {$_} -> {type
} eq 'internal') {
187 if (exists ($formdata -> {$_} -> {default}) and exists ($formdata -> {$_} -> {assign
} -> {value
})) {
188 $default -> {$formdata -> {$_} -> {assign
} -> {value
}}
189 = $formdata -> {$_} -> {default};
191 elsif (exists($formdata -> {$_} -> {values})) {
192 my ($_name, $val) = $_;
193 $val = exists ($formdata -> {$_} -> {default})
194 ?
$formdata -> {$_} -> {default}
196 $default -> {$formdata -> {$_} -> {assign
} -> {value
}}
197 = $self -> {template
} -> list
(
200 { $assign -> {optval
} => plain
($_),
201 ((defined $val and $_ eq $val)
202 ?
($assign -> {optsel
} => 1)
206 } @
{$formdata -> {$_name} -> {values}}
213 print $q -> header
(-type
=> 'text/html');
214 print ${$template -> scrap
(
216 { $formdata->{uniqueID
} ->{assign
}->{value
} => plain
(unique_id
),
217 $formdata->{quoteChar
} ->{assign
}->{value
} => 'ÿ'.plain
($self -> {conf
} -> {admin
} -> {View
} -> {quoteChars
}),
218 $formact->{post
}->{assign
} => $formact->{post
}->{url
},
226 # check the response -> doc
228 unless ($self -> {response
} -> {doc
}) {
230 spec
=> 'unknown_error',
234 $self -> handle_error
;
236 unless ($self -> {response
} -> {doc
}) {
237 $self -> jerk
('While producing the HTML response an unknown error has occurred.');
242 # ok, print the response document to STDOUT
244 print $q -> header
(-type
=> 'text/html');
245 print ${$template -> scrap
(
246 $self -> {response
} -> {doc
},
248 $self -> {response
} -> {pars
}
255 ### sub handle_error ###########################################################
257 # analyze error data and create content for the response method
259 # Return: true if error detected
265 my $spec = $self -> {error
} -> {spec
};
267 return unless ($spec);
269 my $assign = $self -> {conf
} -> {assign
};
270 my $formdata = $self -> {conf
} -> {form_data
};
272 my $desc = $self -> {error
} -> {desc
} || '';
273 my $type = $self -> {error
} -> {type
};
276 if (exists ($formdata -> {$desc})
277 and exists ($formdata -> {$desc} -> {assign
} -> {$spec})) {
278 $emsg = $formdata -> {$desc} -> {assign
} -> {$spec};
281 $emsg = $assign -> {$spec} || '';
286 if ($type eq 'fatal') {
287 $self -> {response
} -> {doc
} = $assign -> {docFatal
};
288 $self -> {response
} -> {pars
} = {
289 $assign -> {errorMessage
} => $self -> {template
} -> insert
($emsg)
294 # user is able to repair his request
296 elsif ($type eq 'repeat' or $type eq 'fetch') {
297 $self -> {response
} -> {doc
} = $assign -> {docError
};
298 $self -> fillout_form
;
299 $self -> {response
} -> {pars
} -> {$assign -> {errorMessage
}} = $self -> {template
} -> insert
($emsg);
300 my $num = $spec eq 'too_long'
301 ?
$formdata -> {$desc} -> {maxlength
}
302 : ($spec eq 'too_short'
303 ?
$formdata -> {$desc} -> {minlength
}
307 $self -> {response
} -> {pars
} -> {$assign -> {charNum
}} = $num
314 ### sub fillout_form ###########################################################
316 # fill out the form using available form data
323 my $assign = $self -> {conf
} -> {assign
};
324 my $formdata = $self -> {conf
} -> {form_data
};
325 my $formact = $self -> {conf
} -> {form_action
};
326 my $q = $self -> {cgi_object
};
331 $pars -> {$formact -> {post
} -> {assign
}} = $formact -> {post
} -> {url
};
333 for (keys %$formdata) {
334 if ($_ eq 'quoteChar') {
335 $pars -> {$formdata->{$_}->{assign
}->{value
}}
336 = 'ÿ'.plain
($q -> param
($formdata -> {quoteChar
} -> {name
}) or '');
338 elsif (exists ($formdata -> {$_} -> {name
})) {
339 unless (exists ($formdata -> {$_} -> {values})) {
340 $pars -> {$formdata -> {$_} -> {assign
} -> {value
}}
341 = plain
($q -> param
($formdata -> {$_} -> {name
}));
345 $pars -> {$formdata -> {$_} -> {assign
} -> {value
}}
346 = $self -> {template
} -> list
(
349 { $assign -> {optval
} => plain
($_),
350 (( $_ eq $q -> param
($formdata -> {$_name} -> {name
}))
351 ?
($assign -> {optsel
} => 1)
355 } @
{$formdata -> {$_name} -> {values}}
362 $self -> {response
} -> {pars
} = $pars;
366 ### sub save ###################################################################
369 # check on legal reply or dupe is released here
376 # if an empty 'new message' document, there's nothing to save
378 return if ($self -> {response
} -> {new_thread
});
380 $self -> {check_success
} = 0;
382 # lock and load the forum main file
384 if ($self -> load_main_file
) {
386 # if a reply - is it legal?
389 if ($self -> check_reply_dupe
) {
391 unless ($self -> {response
} -> {reply
} or $self -> {response
} -> {new
}) {
392 # don't know, if we any time come to this branch
393 # the script is probably broken
396 spec
=> 'unknown_error',
402 my $formdata = $self -> {conf
} -> {form_data
};
403 my $q = $self -> {cgi_object
};
404 my $f = $self -> {forum
};
406 quoteChars
=> $q -> param
($formdata -> {quoteChar
} -> {name
}),
407 uniqueID
=> $q -> param
($formdata -> {uniqueID
} -> {name
}),
409 ip
=> $q -> remote_addr
,
410 forumFile
=> $self -> {conf
} -> {forum_file_name
},
411 messagePath
=> $self -> {conf
} -> {message_path
},
412 lastThread
=> $f -> {last_thread
},
413 lastMessage
=> $f -> {last_message
},
414 parsedThreads
=> $f -> {threads
},
416 messages
=> $self -> {conf
} -> {template
} -> {messages
} || {},
417 base_uri
=> $self -> {conf
} -> {original
} -> {files
} -> {forum_base
}
420 # set the variables if defined..
423 author
=> 'posterName',
424 email
=> 'posterEmail',
425 category
=> 'posterCategory',
426 subject
=> 'posterSubject',
427 body
=> 'posterBody',
428 homepage
=> 'posterURL',
429 image
=> 'posterImage'
433 $pars -> {$_} = $q -> param
($formdata -> {$may{$_}} -> {name
})
434 if (defined $q -> param
($formdata -> {$may{$_}} -> {name
}));
437 my ($stat, $xml, $mid, $tid);
439 # we've got a fup if it's a reply
441 if ($self -> {response
} -> {reply
}) {
442 $pars -> {parentMessage
} = $self -> {fup_mid
};
443 $pars -> {thread
} = $self -> {fup_tid
};
444 ($stat, $xml, $mid, $tid) = write_reply_posting
($pars);
447 ($stat, $xml, $mid, $tid) = write_new_thread
($pars);
458 my $cache = new Posting
::Cache
($self->{conf
}->{original
}->{files
}->{cachePath
});
459 $cache -> add_posting
(
460 { thread
=> ($tid =~ /(\d+)/)[0],
461 posting
=> ($mid =~ /(\d+)/)[0]
465 $self -> {check_success
} = 1;
466 my $thx = $self -> {conf
} -> {show_posting
} -> {thanx
};
468 # define special response data
470 $self -> {response
} -> {doc
} = $self -> {conf
} -> {assign
} -> {docThx
};
471 $self -> {response
} -> {pars
} = {
472 $thx -> {time} => plain
(hr_time
($time)),
473 $thx -> {body
} => message_as_HTML
(
477 assign
=> $self -> {conf
} -> {assign
},
478 quoteChars
=> $q -> param
($formdata -> {quoteChar
} -> {name
}),
479 quoting
=> $self -> {conf
} -> {admin
} -> {View
} -> {quoting
}
483 # set the variables if defined..
486 author
=> 'posterName',
487 email
=> 'posterEmail',
488 category
=> 'posterCategory',
489 subject
=> 'posterSubject',
490 homepage
=> 'posterURL',
491 image
=> 'posterImage'
495 my $x = $q -> param
($formdata -> {$may{$_}} -> {name
});
496 $x = '' unless (defined $x);
497 $self -> {response
} -> {pars
} -> {$thx -> {$_}} = plain
($x)
498 if (defined $thx -> {$_});
505 # unlock forum main file
507 if ($self -> {forum
} -> {flocked
}) {
508 $self -> {forum
} -> {flocked
} -> unlock
;
509 $self -> {forum
} -> {flocked
} = 0;
512 $self -> handle_error
unless $self -> {check_success
};
517 ### sub parse_cgi ##############################################################
519 # fetch and decode cgi-parameters,
520 # find out the kind of response requested by the user (new message, reply)
527 # create the CGI object
529 $self -> {cgi_object
} = new CGI
;
533 $self -> {check_success
} = $self -> check_cgi
;
538 ### sub load_main_file #########################################################
540 # load and parse the forum main file
542 # Return: Success (true/false)
546 my $forum = new Lock
($self -> {conf
} -> {forum_file_name
});
548 unless ($forum -> lock(LH_EXCL
)) {
549 unless ($forum -> masterlocked
) {
550 # occupied or no w-bit set for the directory..., hmmm
562 spec
=> 'master_lock',
569 $self -> {forum
} -> {flocked
} = $forum;
570 ( $self -> {forum
} -> {threads
},
571 $self -> {forum
} -> {last_thread
},
572 $self -> {forum
} -> {last_message
},
573 $self -> {forum
} -> {dtd
},
574 $self -> {forum
} -> {unids
}
575 ) = get_all_threads
($self -> {conf
} -> {forum_file_name
}, KEEP_DELETED
);
582 ### sub check_reply_dupe #######################################################
584 # check whether a reply is legal
585 # (followup posting must exists)
587 # check whether this form request is a dupe
588 # (unique id already exists)
590 # Return: Status Code (Bool)
592 sub check_reply_dupe
{
596 # return true unless it's not a reply
600 $self -> {response
} -> {reply
}
601 or $self -> {response
} -> {new
}
604 if ($self -> {response
} -> {reply
}) {
606 my ($threads, $ftid, $fmid, $i, %msg) = (
607 $self -> {forum
} -> {threads
},
612 # thread doesn't exist
614 unless (exists($threads -> {$ftid})) {
622 # build a reverse lookup hash (mid => number in array)
623 # and ignore invisible messages
624 # (users can't reply to "deleted" msg)
626 for ($i=0; $i < @
{$threads -> {$ftid}}; $i++) {
628 if ($threads -> {$ftid} -> [$i] -> {deleted
}) {
629 $i+=$threads -> {$ftid} -> [$i] -> {answers
};
632 $msg{$threads -> {$ftid} -> [$i] -> {mid
}}=$i;
636 # message doesn't exist
638 unless (exists($msg{$fmid})) {
646 # build a unique id lookup hash
647 # use the unids of parent message's kids
649 %unids = map {$_ => 1} @
{$threads -> {$ftid} -> [$msg{$fmid}] -> {unids
}};
652 # build a unique id lookup hash, too
653 # but use only the level-zero-messages
655 %unids = map {$_ => 1} @
{$self -> {forum
} -> {unids
}};
661 $self -> {cgi_object
} -> param
(
662 $self -> {conf
} -> {form_data
} -> {uniqueID
} -> {name
})})) {
674 ### sub check_cgi ##############################################################
676 # cgi params are like raw eggs...
678 # Return: Status Code (Bool)
679 # creates content for the handle_error method if anything fails
684 # count the submitted keys and get the keys themselves
686 my %got_keys = map {($_ => 1)} $self -> {cgi_object
} -> param
;
687 my $cnt_got_keys = keys %got_keys;
688 my $formdata = $self -> {conf
} -> {form_data
};
689 my $formmust = $self -> {conf
} -> {form_must
};
691 # user requested the 'new thread' page
692 # (no params but perhaps the user-ID have been submitted)
694 if ($cnt_got_keys == 0 or (
695 exists ($formdata -> {userID
})
696 and $cnt_got_keys == 1
697 and $got_keys{$formdata -> {userID
} -> {name
}}
699 $self -> {response
} -> {new_thread
} = 1;
700 $self -> {check_success
} = 1;
704 # now we know, we've got a filled out form
705 # we do the following steps to check it:
707 # 1st: create a reverse Hash (CGI-key - identifier)
708 # 2nd: did we get _all_ must-keys?
709 # check whether reply or new message request
710 # 3rd: did we get too many keys?
711 # 4th: do _all_ submitted values accord to
713 # fetch the "missing" keys
719 exists($formdata -> {$_} -> {name
})
720 ?
($formdata -> {$_} -> {name
} => $_)
726 $self -> {response
} -> {reply
} = $got_keys {$formdata -> {followUp
} -> {name
}}?
1 : 0;
727 $self -> {response
} -> {new
} = not $self -> {response
} -> {reply
};
729 # define the fetch array (values to fetch from parent message)
731 $self -> {fetch
} = [];
733 for ( @
{$formmust -> {$self -> {response
} -> {reply
}?
'reply':'new'}} ) {
735 unless ($got_keys {$formdata -> {$_} -> {name
}}) {
737 # only miss the key unless we're able to fetch it from parent posting
740 not $self -> {response
} -> {reply
}
741 or $formdata -> {$_} -> {errorType
} eq 'fetch') {
744 spec
=> 'missing_key',
751 # keep in mind to fetch the value later
753 push @
{$self -> {fetch
}} => $_;
758 # I'm lazy - I know...
759 my $q = $self -> {cgi_object
};
764 unless (exists ($name {$_})) {
766 spec
=> 'unexpected_key',
776 unless ($self -> decode_param
) {
778 spec
=> 'unknown_encoding',
784 if ($self -> {response
} -> {reply
}) {
786 # get the parent-identifiers if we got a reply request
788 my ($ftid, $fmid) = split /;/ => $q -> param
($formdata -> {followUp
} -> {name
}) => 2;
790 unless ($ftid =~ /^\d+$/ and $fmid =~ /^\d+$/) {
792 spec
=> 'unknown_followup',
797 $self -> {fup_tid
} = $ftid;
798 $self -> {fup_mid
} = $fmid;
800 # fetch the missing keys
801 # if it fails, they're too short, too... ;)
804 $got_keys{$formdata -> {$_} -> {name
}} = 1 for (@
{$self -> {fetch
}});
807 # now we can check on length, type etc.
809 for (keys %got_keys) {
811 # we are sure, we've got only one value for one key
813 my $val = $q -> param
($_);
815 $val =~ s/\302\240/ /g; # convert nbsp (UTF-8 encoded) into normal spaces
816 $val =~ s/\015\012|\015|\012/ /g # convert \n into spaces unless it's a multiline field
818 exists ($formdata -> {$name {$_}} -> {type
})
819 and $formdata -> {$name {$_}} -> {type
} eq 'multiline-text'
822 $q -> param
($_ => $val); # write it back
826 if (length $val > $formdata -> {$name {$_}} -> {maxlength
}) {
830 type
=> $formdata -> {$name {$_}} -> {errorType
}
832 $self -> kill_param
or return;
836 # (only check if there's defined a minimum length)
838 if (exists ($formdata -> {$name {$_}} -> {minlength
})) {
840 # kill the whitespaces to get only the visible characters...
842 (my $val_ww = $val) =~ s/\s+//g;
844 if (exists ($formdata -> {$name {$_}} -> {type
}) and $formdata -> {$name {$_}} -> {type
} eq 'name') {
845 $val_ww =~ y/a-zA-Z//cd;
848 # my @badlist = map {qr/\Q$_/i} qw (
849 # # insert badmatchlist here
852 # push @badlist => map {qr/\b\Q$_\E\b/i} qw(
853 # # insert badwordlist here
857 if ($val_ww =~ /$_/) {
868 if (length $val_ww < $formdata -> {$name {$_}} -> {minlength
}) {
872 type
=> $formdata -> {$name {$_}} -> {errorType
}
874 $self -> kill_param
or return;
878 # check the values on expected kinds of content
879 # (email, http-url, url, option)
881 if (exists ($formdata -> {$name {$_}} -> {type
}) and length $val) {
882 if ($formdata -> {$name {$_}} -> {type
} eq 'email' and not is_email
$val) {
884 spec
=> 'wrong_mail',
886 type
=> $formdata -> {$name {$_}} -> {errorType
}
888 $self -> kill_param
or return;
891 elsif ($formdata -> {$name {$_}} -> {type
} eq 'http-url' and not is_URL
$val => 'http') {
893 spec
=> 'wrong_http_url',
895 type
=> $formdata -> {$name {$_}} -> {errorType
}
897 $self -> kill_param
or return;
900 elsif ($formdata -> {$name {$_}} -> {type
} eq 'url' and not is_URL
$val => ':ALL') {
904 type
=> $formdata -> {$name {$_}} -> {errorType
}
906 $self -> kill_param
or return;
909 elsif ($formdata -> {$name {$_}} -> {type
} eq 'unique-id' and not may_id
$val) {
911 spec
=> 'wrong_unique_id',
913 type
=> $formdata -> {$name {$_}} -> {errorType
}
915 print STDERR
"Manipuliert!";
916 $self -> kill_param
or return;
920 if (exists ($formdata -> {$name {$_}} -> {values})
921 and not exists ({map {$_ => undef} @
{$formdata -> {$name {$_}} -> {values}}} -> {$val})) {
925 type
=> $formdata -> {$name {$_}} -> {errorType
}
927 $self -> kill_param
or return;
934 ### sub kill_param #############################################################
936 # kill the param (set it on '') if wrong and declared as 'kill' in config file
938 # Return: true if killed
944 if ($self -> {conf
} -> {form_data
} -> {$self -> {error
} -> {desc
}} -> {errorType
} eq 'kill') {
945 $self -> {cgi_object
} -> param
($self -> {conf
} -> {form_data
} -> {$self -> {error
} -> {desc
}} -> {name
} => '');
946 $self -> {error
} = {};
953 ### sub fetch ##################################################################
955 # fetch "missing" keys from parent posting
959 my $q = $self -> {cgi_object
};
960 my $formdata = $self -> {conf
} -> {form_data
};
962 if (@
{$self -> {fetch
}}) {
963 my $thread = new Lock
($self -> {conf
} -> {message_path
}.'t'.$self -> {fup_tid
}.'.xml');
965 if ($thread -> lock (LH_SHARED
)) {
966 my $xml = parse_xml_file
($thread -> filename
);
970 my $mnode = get_message_node
($xml, 't'.$self -> {fup_tid
}, 'm'.$self -> {fup_mid
});
972 my $header = get_message_header
($mnode);
974 $q -> param
($formdata -> {$_} -> {name
} => $header -> {$formdata -> {$_} -> {header
}})
975 for (@
{$self -> {fetch
}});
984 # fillout the values with an empty string
986 $q -> param
($formdata -> {$_} -> {name
} => '')
987 for (@
{$self -> {fetch
}});
992 ### sub decode_param ###########################################################
994 # convert submitted form data into UTF-8
995 # unless it's not encoded yet
997 # Return: Status Code (Bool)
998 # false if unknown encoding (like UTF-7 for instance)
1003 my $q = $self -> {cgi_object
};
1004 my $formdata = $self -> {conf
} -> {form_data
};
1006 my $code = $q -> param
($formdata -> {quoteChar
} -> {name
});
1009 # Latin 1 (we hope so - there's no real way to find out :-( )
1010 if ($code =~ /^\377/) {
1011 $q -> param
($_ => map {toUTF8
($_)} $q -> param
($_)) for ($q -> param
);
1014 # UTF-8 is (probably) correct,
1015 # other encodings we don't know and fail
1016 return unless $code =~ /^\303\277/;
1019 # remove the ÿ (encoded as UTF-8) from quotechars
1020 $q -> param
($formdata -> {quoteChar
} -> {name
}
1021 => substr $q -> param
($formdata -> {quoteChar
} -> {name
}),2);
1023 # ok, params now should be UTF-8 encoded
1029 $text = 'An error has occurred.' unless defined $text;
1032 Content-type: text/plain
1039 We will fix it as soon as possible. Thank you for your patience.
1048 ### end of fo_posting.pl #######################################################
patrick-canterino.de