]>
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 ################################################################################
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;
30 # my $null = $0; #$null =~ s/\\/\//g; # for win :-(
31 # $Bin = ($null =~ /^(.*)\/.*$/)? $1 : '.';
32 # $Config = "$Bin/../../../cgi-config/devforum";
33 # $Shared = "$Bin/../../../cgi-shared";
34 # $Script = ($null =~ /^.*\/(.*)$/)? $1 : $null;
38 use CGI
::Carp
qw(fatalsToBrowser);
44 # load script configuration and admin default conf.
46 my $conf = read_script_conf
($Config, $Shared, $Script);
47 my $adminDefault = read_admin_conf
($conf -> {files
} -> {adminDefault
});
49 # Initialize the request
51 my $request = new Posting
::Request
($conf, $adminDefault);
53 # fetch and parse the cgi-params
55 $request -> parse_cgi
;
57 # handle errors or save the posting
59 $request -> handle_error
or $request -> save
;
65 # shorten the main file?
67 $request -> severance
;
71 ### main end ###################################################################
73 ################################################################################
74 ### Posting::Request ###########################################################
75 package Posting
::Request
;
79 use Encode
::Plain
; $Encode::Plain
::utf8
= 1; # generally convert from UTF-8
93 use Template
::Posting
;
97 ### sub new ####################################################################
99 # initialising the Posting::Request object
100 # check parameters and fill in object properties
103 my ($class, $conf, $adminDefault) = @_;
105 my $sp = $conf -> {show
} -> {Posting
};
110 admin
=> $adminDefault,
112 message_path
=> $conf -> {files
} -> {messagePath
},
113 forum_file_name
=> $conf -> {files
} -> {forum
},
116 assign
=> $sp -> {assign
},
117 template
=> $conf -> {template
},
118 form_must
=> $sp -> {form
} -> {must
},
119 form_data
=> $sp -> {form
} -> {data
},
120 form_action
=> $sp -> {form
} -> {action
},
123 template
=> new Template
$sp -> {templateFile
},
135 my $stat = cut_tail
({
136 forumFile
=> $self -> {conf
} -> {forum_file_name
},
137 messagePath
=> $self -> {conf
} -> {message_path
},
138 archivePath
=> $self -> {conf
} -> {original
} -> {files
} -> {archivePath
},
139 lockFile
=> $self -> {conf
} -> {original
} -> {files
} -> {sev_lock
},
140 adminDefault
=> $self -> {conf
} -> {admin
},
141 cachePath
=> $self -> {conf
} -> {original
} -> {files
} -> {cachePath
}
143 # die $stat->{(keys %$stat)[0]} if (%$stat);
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 violent_unlock_file
($self -> {conf
} -> {forum_file_name
}) unless write_unlock_file
($self -> {conf
} -> {forum_file_name
});
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)
543 unless ($lock_stat = write_lock_file
($self -> {conf
} -> {forum_file_name
})) {
544 if (defined $lock_stat) {
545 # occupied or no w-bit set for the directory..., hmmm
547 violent_unlock_file
($self -> {conf
} -> {forum_file_name
});
558 spec
=> 'master_lock',
565 $self -> {forum
} -> {flocked
} = 1;
566 ( $self -> {forum
} -> {threads
},
567 $self -> {forum
} -> {last_thread
},
568 $self -> {forum
} -> {last_message
},
569 $self -> {forum
} -> {dtd
},
570 $self -> {forum
} -> {unids
}
571 ) = get_all_threads
($self -> {conf
} -> {forum_file_name
}, KEEP_DELETED
);
578 ### sub check_reply_dupe #######################################################
580 # check whether a reply is legal
581 # (followup posting must exists)
583 # check whether this form request is a dupe
584 # (unique id already exists)
586 # Return: Status Code (Bool)
588 sub check_reply_dupe
{
592 # return true unless it's not a reply
596 $self -> {response
} -> {reply
}
597 or $self -> {response
} -> {new
}
600 if ($self -> {response
} -> {reply
}) {
602 my ($threads, $ftid, $fmid, $i, %msg) = (
603 $self -> {forum
} -> {threads
},
608 # thread doesn't exist
610 unless (exists($threads -> {$ftid})) {
618 # build a reverse lookup hash (mid => number in array)
619 # and ignore invisible messages
620 # (users can't reply to "deleted" msg)
622 for ($i=0; $i < @
{$threads -> {$ftid}}; $i++) {
624 if ($threads -> {$ftid} -> [$i] -> {deleted
}) {
625 $i+=$threads -> {$ftid} -> [$i] -> {answers
};
628 $msg{$threads -> {$ftid} -> [$i] -> {mid
}}=$i;
632 # message doesn't exist
634 unless (exists($msg{$fmid})) {
642 # build a unique id lookup hash
643 # use the unids of parent message's kids
645 %unids = map {$_ => 1} @
{$threads -> {$ftid} -> [$msg{$fmid}] -> {unids
}};
648 # build a unique id lookup hash, too
649 # but use only the level-zero-messages
651 %unids = map {$_ => 1} @
{$self -> {forum
} -> {unids
}};
657 $self -> {cgi_object
} -> param
(
658 $self -> {conf
} -> {form_data
} -> {uniqueID
} -> {name
})})) {
670 ### sub check_cgi ##############################################################
672 # cgi params are like raw eggs...
674 # Return: Status Code (Bool)
675 # creates content for the handle_error method if anything fails
680 # count the submitted keys and get the keys themselves
682 my %got_keys = map {($_ => 1)} $self -> {cgi_object
} -> param
;
683 my $cnt_got_keys = keys %got_keys;
684 my $formdata = $self -> {conf
} -> {form_data
};
685 my $formmust = $self -> {conf
} -> {form_must
};
687 # user requested the 'new thread' page
688 # (no params but perhaps the user-ID have been submitted)
690 if ($cnt_got_keys == 0 or (
691 exists ($formdata -> {userID
})
692 and $cnt_got_keys == 1
693 and $got_keys{$formdata -> {userID
} -> {name
}}
695 $self -> {response
} -> {new_thread
} = 1;
696 $self -> {check_success
} = 1;
700 # now we know, we've got a filled out form
701 # we do the following steps to check it:
703 # 1st: create a reverse Hash (CGI-key - identifier)
704 # 2nd: did we get _all_ must-keys?
705 # check whether reply or new message request
706 # 3rd: did we get too many keys?
707 # 4th: do _all_ submitted values accord to
709 # fetch the "missing" keys
715 exists($formdata -> {$_} -> {name
})
716 ?
($formdata -> {$_} -> {name
} => $_)
722 $self -> {response
} -> {reply
} = $got_keys {$formdata -> {followUp
} -> {name
}}?
1 : 0;
723 $self -> {response
} -> {new
} = not $self -> {response
} -> {reply
};
725 # define the fetch array (values to fetch from parent message)
727 $self -> {fetch
} = [];
729 for ( @
{$formmust -> {$self -> {response
} -> {reply
}?
'reply':'new'}} ) {
731 unless ($got_keys {$formdata -> {$_} -> {name
}}) {
733 # only miss the key unless we're able to fetch it from parent posting
736 not $self -> {response
} -> {reply
}
737 or $formdata -> {$_} -> {errorType
} eq 'fetch') {
740 spec
=> 'missing_key',
747 # keep in mind to fetch the value later
749 push @
{$self -> {fetch
}} => $_;
754 # I'm lazy - I know...
755 my $q = $self -> {cgi_object
};
760 unless (exists ($name {$_})) {
762 spec
=> 'unexpected_key',
772 unless ($self -> decode_param
) {
774 spec
=> 'unknown_encoding',
780 if ($self -> {response
} -> {reply
}) {
782 # get the parent-identifiers if we got a reply request
784 my ($ftid, $fmid) = split /;/ => $q -> param
($formdata -> {followUp
} -> {name
}) => 2;
786 unless ($ftid =~ /^\d+$/ and $fmid =~ /^\d+$/) {
788 spec
=> 'unknown_followup',
793 $self -> {fup_tid
} = $ftid;
794 $self -> {fup_mid
} = $fmid;
796 # fetch the missing keys
797 # if it fails, they're too short, too... ;)
800 $got_keys{$formdata -> {$_} -> {name
}} = 1 for (@
{$self -> {fetch
}});
803 # now we can check on length, type etc.
805 for (keys %got_keys) {
807 # we are sure, we've got only one value for one key
809 my $val = $q -> param
($_);
811 $val =~ s/\302\240/ /g; # convert nbsp (UTF-8 encoded) into normal spaces
812 $val =~ s/\015\012|\015|\012/ /g # convert \n into spaces unless it's a multiline field
814 exists ($formdata -> {$name {$_}} -> {type
})
815 and $formdata -> {$name {$_}} -> {type
} eq 'multiline-text'
818 $q -> param
($_ => $val); # write it back
822 if (length $val > $formdata -> {$name {$_}} -> {maxlength
}) {
826 type
=> $formdata -> {$name {$_}} -> {errorType
}
828 $self -> kill_param
or return;
832 # (only check if there's defined a minimum length)
834 if (exists ($formdata -> {$name {$_}} -> {minlength
})) {
836 # kill the whitespaces to get only the visible characters...
838 (my $val_ww = $val) =~ s/\s+//g;
840 $val_ww =~ y/a-zA-Z//cd
841 if (exists ($formdata -> {$name {$_}} -> {type
}) and $formdata -> {$name {$_}} -> {type
} eq 'name');
843 if (length $val_ww < $formdata -> {$name {$_}} -> {minlength
}) {
847 type
=> $formdata -> {$name {$_}} -> {errorType
}
849 $self -> kill_param
or return;
853 # check the values on expected kinds of content
854 # (email, http-url, url, option)
856 if (exists ($formdata -> {$name {$_}} -> {type
}) and length $val) {
857 if ($formdata -> {$name {$_}} -> {type
} eq 'email' and not is_email
$val) {
859 spec
=> 'wrong_mail',
861 type
=> $formdata -> {$name {$_}} -> {errorType
}
863 $self -> kill_param
or return;
866 elsif ($formdata -> {$name {$_}} -> {type
} eq 'http-url' and not is_URL
$val => 'http') {
868 spec
=> 'wrong_http_url',
870 type
=> $formdata -> {$name {$_}} -> {errorType
}
872 $self -> kill_param
or return;
875 elsif ($formdata -> {$name {$_}} -> {type
} eq 'url' and not is_URL
$val => ':ALL') {
879 type
=> $formdata -> {$name {$_}} -> {errorType
}
881 $self -> kill_param
or return;
884 elsif ($formdata -> {$name {$_}} -> {type
} eq 'unique-id' and not may_id
$val) {
886 spec
=> 'wrong_unique_id',
888 type
=> $formdata -> {$name {$_}} -> {errorType
}
890 print STDERR
"Manipuliert!";
891 $self -> kill_param
or return;
895 if (exists ($formdata -> {$name {$_}} -> {values})
896 and not exists ({map {$_ => undef} @
{$formdata -> {$name {$_}} -> {values}}} -> {$val})) {
900 type
=> $formdata -> {$name {$_}} -> {errorType
}
902 $self -> kill_param
or return;
909 ### sub kill_param #############################################################
911 # kill the param (set it on '') if wrong and declared as 'kill' in config file
913 # Return: true if killed
919 if ($self -> {conf
} -> {form_data
} -> {$self -> {error
} -> {desc
}} -> {errorType
} eq 'kill') {
920 $self -> {cgi_object
} -> param
($self -> {conf
} -> {form_data
} -> {$self -> {error
} -> {desc
}} -> {name
} => '');
921 $self -> {error
} = {};
928 ### sub fetch ##################################################################
930 # fetch "missing" keys from parent posting
934 my $q = $self -> {cgi_object
};
935 my $formdata = $self -> {conf
} -> {form_data
};
937 if (@
{$self -> {fetch
}}) {
938 my $filename = $self -> {conf
} -> {message_path
}.'t'.$self -> {fup_tid
}.'.xml';
940 if (lock_file
($filename)) {
941 my $xml = parse_xml_file
($filename);
942 violent_unlock_file
($filename) unless unlock_file
($filename);
945 my $mnode = get_message_node
($xml, 't'.$self -> {fup_tid
}, 'm'.$self -> {fup_mid
});
947 my $header = get_message_header
($mnode);
949 $q -> param
($formdata -> {$_} -> {name
} => $header -> {$formdata -> {$_} -> {header
}})
950 for (@
{$self -> {fetch
}});
959 # fillout the values with an empty string
961 $q -> param
($formdata -> {$_} -> {name
} => '')
962 for (@
{$self -> {fetch
}});
967 ### sub decode_param ###########################################################
969 # convert submitted form data into UTF-8
970 # unless it's not encoded yet
972 # Return: Status Code (Bool)
973 # false if unknown encoding (like UTF-7 for instance)
978 my $q = $self -> {cgi_object
};
979 my $formdata = $self -> {conf
} -> {form_data
};
981 my $code = $q -> param
($formdata -> {quoteChar
} -> {name
});
984 # Latin 1 (we hope so - there's no real way to find out :-( )
985 if ($code =~ /^\377/) {
986 $q -> param
($_ => map {toUTF8
($_)} $q -> param
($_)) for ($q -> param
);
989 # UTF-8 is (probably) correct,
990 # other encodings we don't know and fail
991 return unless $code =~ /^\303\277/;
994 # remove the ÿ (encoded as UTF-8) from quotechars
995 $q -> param
($formdata -> {quoteChar
} -> {name
}
996 => substr $q -> param
($formdata -> {quoteChar
} -> {name
}),2);
998 # ok, params now should be UTF-8 encoded
1003 my $text = $_[1] || 'An error has occurred.';
1005 Content-type: text/plain
1012 We will fix it as soon as possible. Thank you for your patience.
1021 ### end of fo_posting.pl #######################################################
patrick-canterino.de