sub Wrequire ($);
sub Wuse ($@);
our $W;
-our @EXPORT=qw(&Wrequire &Wuse &a_href &a_href_cz &vskip &img ¢erimg &rightimg $W &top_dir &top_dir_disk);
-our @ISA=qw(Exporter);
+our @EXPORT=qw(&Wrequire &Wuse &a_href &a_href_cz &vskip &img ¢erimg &rightimg $W &top_dir &top_dir_disk &Wprint &input_hidden_persistents);
+our @ISA=qw(Exporter Tie::Handle);
BEGIN
{
+ $W->{"__My::Web_init"}=1;
+
sub Wrequire ($)
{
my($file)=@_;
my $class=$file;
$file=~s#::#/#g;
$file.=".pm";
- my $aref=($W->{"packages_used"}{$Apache::Registry::curstash}||=[]);
- push @$aref,$class
- if !{ map(($_=>1),@$aref) }->{$class}; # Prevent duplicated entries.
+ my $who=$W->{"__PACKAGE__"};
+ $who||="__My::Web" if $W->{"__My::Web_init"};
+ if ($who) {
+ my $aref=($W->{"packages_used"}{$who}||=[]);
+ push @$aref,$class
+ if !{ map(($_=>1),@$aref) }->{$class}; # Prevent duplicated entries.
+ }
CORE::require $file;
1; # Otherwise 'require' would already file above.
}
require HTTP::BrowserDetect;
require HTTP::Negotiate;
my $have_Geo_IP; BEGIN { $have_Geo_IP=eval { require Geo::IP; 1; }; }
-require CGI;
-require Apache2;
-
-
-# Undo 'www/engine/httpd-restart' as it may use obsolete Perl for 'mod_perl'
-delete $ENV{"PERLLIB"};
-delete $ENV{"LD_LIBRARY_PATH"};
+use ModPerl::Util qw(exit);
+use POSIX qw(strftime);
+use Tie::Handle;
+use Apache::Const qw(HTTP_MOVED_TEMPORARILY);
+use URI;
+use URI::QueryParam;
#our $W;
# $W->{"head"}
# $W->{"head_css"}
# $W->{"force_charset"}
- # %{$W->{"packages_used"}
# $W->{"heading_done"}
# $W->{"footer_passed"}
# %{$W->{"headers"}}
# %{$W->{"headers_lc"}} # maps lc($headers_key)=>$headers_key
- # @{$W->{"packages_used"}{$Apache::Registry::curstash}}}
+ # @{$W->{"packages_used"}{$W->{"__PACKAGE__"}}}
+ # @{$W->{"packages_used"}{"__My::Web"}}
# %{$W->{"args"}}
sub init ($%)
{
my($class,%args)=@_;
- print STDERR "$class->init ".$ENV{"REQUEST_URI"}."\n";
+ print STDERR "$class->init ".Apache->request()->unparsed_uri()."\n";
my $packages_used_save=$W->{"packages_used"};
$W={ %WebConfig,%args }; # override %WebConfig settings
$W->{"packages_used"}=$packages_used_save;
- $W->{"__PACKAGE__"}||="Apache::ROOT".$Apache::Registry::curstash;
+ # {"__PACKAGE__"} is mandatory for mod_perl-2.0;
+ # $Apache::Registry::curstash is no longer supported.
+ do { cluck "No $_" if !$W->{$_}; } for "__PACKAGE__";
$W->{"top_dir"}||=eval '$'.$W->{"__PACKAGE__"}.'::top_dir';
do { $W->{$_}=0 if !defined $W->{$_}; } for ("detect_ent");
do { $W->{$_}=0 if !defined $W->{$_}; } for ("detect_js");
do { $W->{$_}=1 if !defined $W->{$_}; } for ("have_css"); # AFAIK it does not hurt anyone.
- do { $W->{$_}=1 if !defined $W->{$_}; } for ("heading");
do { $W->{$_}=1 if !defined $W->{$_}; } for ("footer");
do { $W->{$_}=1 if !defined $W->{$_}; } for ("footer_delimit");
do { $W->{$_}=1 if !defined $W->{$_}; } for ("footer_mailme");
$W->{"r"}=Apache->request();
$W->{"QUERY_STRING"}=$W->{"r"}->args() || "";
- if ($W->{"QUERY_STRING"}=~/[&]amp;have_ent/)
- { $W->{"have_ent"}=0; }
- elsif ($W->{"QUERY_STRING"}=~ /[&]have_ent/)
- { $W->{"have_ent"}=1; }
- else
- { delete $W->{"have_ent"}; }
- if ($W->{"detect_ent"} && !defined $W->{"have_ent"} && $W->{"r"}->method() eq "GET") {
- $W->{"head"}.='<meta http-equiv="Refresh" content="0; URL='
- .CGI::escapeHTML("http://".&{$W->{"web_hostname_sub"}}()."/".($W->{"r"}->uri()=~m#^/*(.*)$#)[0]
- ."?".($W->{"QUERY_STRING"} || "detect_ent_glue=1").'&have_ent=detect')
- .'" />'."\n";
+ if ($W->{"detect_ent"}) {
+ if ($W->{"QUERY_STRING"}=~/[&]amp;have_ent/)
+ { $W->{"have_ent"}=0; }
+ elsif ($W->{"QUERY_STRING"}=~ /[&]have_ent/)
+ { $W->{"have_ent"}=1; }
+ else
+ { delete $W->{"have_ent"}; }
+ if (!defined $W->{"have_ent"} && $W->{"r"}->method() eq "GET") {
+ $W->{"head"}.='<meta http-equiv="Refresh" content="0; URL='
+ .CGI::escapeHTML("http://".&{$W->{"web_hostname_sub"}}()."/".($W->{"r"}->uri()=~m#^/*(.*)$#)[0]
+ ."?".($W->{"QUERY_STRING"} || "detect_ent_glue=1").'&have_ent=detect')
+ .'" />'."\n";
+ }
}
$W->{"QUERY_STRING"}=~s/([&])amp;/$1/g;
$W->{"r"}->args($W->{"QUERY_STRING"});
- $ENV{"QUERY_STRING"}=$W->{"QUERY_STRING"};
+ # Workaround: &CGI::Vars behaves weird if strings passed both as POST data and in: $QUERY_STRING
+ do { $W->{"r"}->args(""); delete $ENV{"QUERY_STRING"}; } if $W->{"r"}->method() eq "POST";
# Do not: $W->{"r"}->args()
# as it parses only QUERY_STRING (not POST data).
- $W->{"args"}={ CGI->new()->Vars() };
- for (keys(%{$W->{"args"}})) {
- my @vals=split /\x00/,$W->{"args"}{$_};
+ $W->{"args"}={ CGI->new($W->{"r"})->Vars() };
+ for my $name (keys(%{$W->{"args"}})) {
+ my @vals=split /\x00/,$W->{"args"}{$name};
next if @vals<=1;
- $W->{"args"}{$_}=[@vals];
+ $W->{"args"}{$name}=[@vals];
}
- do { $W->{$_}=$ENV{"HTTP_ACCEPT"} if !defined $W->{$_}; } for ("accept");
- do { $W->{$_}=$ENV{"HTTP_USER_AGENT"} if !defined $W->{$_}; } for ("user_agent");
+ do { $W->{$_}=$W->{"r"}->headers_in()->{"Accept"} if !defined $W->{$_}; } for ("accept");
+ do { $W->{$_}=$W->{"r"}->headers_in()->{"User-Agent"} if !defined $W->{$_}; } for ("user_agent");
$W->{"browser"}=HTTP::BrowserDetect->new($W->{"user_agent"});
do { args_check(%$_) if $_; } for ($W->{"args_check"});
- return $W;
+ $ENV{"HOSTNAME"}||=&{$W->{"web_hostname_sub"}}();
+
+ return bless $W,$class;
+}
+
+sub Wprint($)
+{
+my($text)=@_;
+
+ $W->{"r"}->puts($text);
+}
+
+# local *FH;
+# tie *FH,ref($W),$W;
+sub TIEHANDLE($)
+{
+my($class,$W)=@_;
+
+ my $self={};
+ $self->{"W"}=$W or confess "Missing W";
+ return bless $self,$class;
+}
+
+sub WRITE
+{
+my($self,$scalar,$length,$offset)=@_;
+
+ Wprint substr($scalar,0,$length);
}
sub top_dir_disk ()
return $INC[0]; # fallback
}
-sub top_dir (;$)
+# $args{"abs"}
+sub top_dir (;$%)
{
-my($in)=@_;
+my($in,%args)=@_;
- if (my $uri=$ENV{"REQUEST_URI"}) {
- if ($W->{"args"}{"Wabs"}) {
+ if (my $uri=$W->{"r"}->unparsed_uri()) {
+ if ($W->{"args"}{"Wabs"} || $args{"abs"}) {
# FIXME: $in may not be defined here!
# to prevent: Use of uninitialized value in ...
if ($in=~m#^/#) {
$in=~s#^/*##;
}
else {
- $in=$ENV{"REQUEST_URI"}."/".$in;
+ $in=$uri."/".$in;
$in=~tr#/#/#s;
1 while $in=~s#/(?:[^/]+)/\Q..\E/#/#g
}
$W->{"args"}{$name}=[ $W->{"args"}{$name} ] if !ref $W->{"args"}{$name} && ref $regex;
fatal "$name_html passed as multivar although singlevar expected"
if ref $W->{"args"}{$name} && !ref $regex;
- $regex=${$regex}[0] if ref $regex;
+ $regex=$regex->[0] if ref $regex;
for my $val (!ref $W->{"args"}{$name} ? $W->{"args"}{$name} : @{$W->{"args"}{$name}}) {
$val="" if !defined $val;
fatal "$name_html <span class=\"quote\">".CGI::escapeHTML($val)."</span>"
my($msg)=@_;
$msg="UNKNOWN" if !$msg;
+ cluck "FATAL: $msg";
$W->{"indexme"}=0; # For the case no heading was sent yet.
+ $W->{"heading_done"}=0; # for the case of already sent {"header_only"}==1
+ $W->{"header_only"}=0; # assurance for &heading
My::Web->heading();
- print "\n".vskip("3ex")."<hr /><h1 class=\"error\">FATAL ERROR: $msg!</h1>\n"
+ Wprint "\n".vskip("3ex")."<hr /><h1 class=\"error\">FATAL ERROR: $msg!</h1>\n"
."<p>You can report this problem's details to"
." ".a_href("mailto:".$W->{"admin_mail"},"admin of this website").".</p>\n";
footer();
{
exit 1 if $W->{"footer_passed"}++; # deadlock prevention:
- print vskip if $W->{"footer_delimit"};
+ Wprint vskip if $W->{"footer_delimit"};
- if ($W->{"heading"}) {
- do { &{$_}() if $_; } for ($W->{"footing_delimit"});
- }
+ do { &{$_}() if $_; } for ($W->{"footing_delimit"});
- print "<hr />\n" if $W->{"footer"};
+ Wprint "<hr />\n" if $W->{"footer"};
if ($W->{"footer_mailme"}) {
- print '<form action="'.top_dir('/SendMsg.pl').'" method="post" onsubmit="'
+ Wprint '<form action="'.top_dir('/SendMsg.pl').'" method="post" onsubmit="'
."this.elements['msgscript'].value=this.elements['msghtml'].value;"
."this.elements['msghtml'].value='';"
."this.submit();"
.'">'."\n";
- print '<p align="right">'."\n";
- print '<input name="msgscript" type="hidden" />'."\n";
- print '<input name="msghtml" type="text" size="32" alt="Message" />'."\n";
- print '<input name="submit" type="submit" value="Quick Note" />'."\n";
- print '</p>'."\n";
- print '</form>'."\n";
+ Wprint input_hidden_persistents()."\n";
+ Wprint '<p align="right">'."\n";
+ Wprint '<input name="msgscript" type="hidden" />'."\n";
+ Wprint '<input name="msghtml" type="text" size="32" alt="Message" />'."\n";
+ Wprint '<input name="submit" type="submit" value="Quick Note" />'."\n";
+ Wprint '</p>'."\n";
+ Wprint '</form>'."\n";
}
+ my @packages_used=(
+ $W->{"__PACKAGE__"},
+ __PACKAGE__,
+ @{$W->{"packages_used"}{"__My::Web"}},
+ map((!$_ ? () : @$_),$W->{"packages_used"}{$W->{"__PACKAGE__"}}),
+ );
+ my %packages_used;
+ @packages_used=grep((!$packages_used{$_}++),@packages_used);
if ($W->{"footer_ids"}) {
- print '<p class="cvs-id">';
- print join("<br />\n",map({ my $package=$_;
+ Wprint '<p class="cvs-id">';
+ Wprint join("<br />\n",map({ my $package=$_;
my $cvs_id=(eval('$'.$package."::CVS_ID")
# || $package # debug
);
my $file=$package;
$file=~s#::#/#g;
my $ext;
+ my @tried;
for (qw(.html.pl .pl .pm),"") {
$ext=$_;
- last if -r top_dir_disk()."/$file$ext";
- cluck "Class file $file not found" if !$ext;
+ my $pathname=top_dir_disk()."/$file$ext";
+ push @tried,$pathname;
+ last if -r $pathname;
+ cluck "Class file $file not found; tried: ".join(" ",@tried) if !$ext;
}
$file.=$ext;
$cvs_id_split[2]=""
}
join " ",@cvs_id_split;
}
- } (
- $W->{"__PACKAGE__"},
- __PACKAGE__,
- @{$W->{"packages_used"}{$Apache::Registry::curstash}},
- )));
- print "</p>\n";
+ } @packages_used));
+ Wprint "</p>\n";
}
- for my $package (
- $W->{"__PACKAGE__"},
- __PACKAGE__,
- @{$W->{"packages_used"}{$Apache::Registry::curstash}},
- ) {
+ for my $package (@packages_used) {
my $cvs_id=(eval('$'.$package."::CVS_ID")
# || $package # debug
);
- print '<!-- '.$package.' - $'.$cvs_id.'$ -->'."\n" if $cvs_id;
+ Wprint '<!-- '.$package.' - $'.$cvs_id.'$ -->'."\n" if $cvs_id;
}
- if ($W->{"heading"}) {
- do { &{$_}() if $_; } for ($W->{"footing"});
- }
+ do { &{$_}() if $_; } for ($W->{"footing"});
- print "</body></html>\n";
+ Wprint "</body></html>\n";
exit(0);
}
return $url!~m#^[a-z]+://#;
}
+sub url_out($%)
+{
+my($url,%args)=@_;
+
+ return $url if !url_is_local $url;
+ $url=top_dir($url,%args) if $url=~m#^/# || $args{"abs"};
+
+ my $uri=URI->new($url);
+ for my $key (keys(%{$W->{"args_persistent"}})) {
+ my $val=$W->{"args"}{$key};
+ next if !defined $val;
+ $uri->query_param_append($key=>$val);
+ }
+ $url="".$uri;
+
+ return $url;
+}
+
sub a_href ($;$%)
{
my($url,$contents,%args)=@_;
$contents=~s#<a\b[^>]*>##gi;
$contents=~s#</a>##gi;
- $url=top_dir($url) if url_is_local $url && $url=~m#^/#;
+ $url=url_out($url,%args);
my $r='<a href="';
my $urlent=CGI::escapeHTML($url);
return $r;
}
+sub input_hidden_persistents()
+{
+ return join("",map({
+ my $key=$_;
+ my $val=$W->{"args"}{$key};
+ (!defined $val ? () : '<input type="hidden"'
+ .' name="'.CGI::escapeHTML($key).'"'
+ .' value="'.CGI::escapeHTML($val).'"'
+ .' />'."\n");
+ } (keys(%{$W->{"args_persistent"}}))));
+}
+
+sub http_moved($$;$)
+{
+my($self,$url,$status)=@_;
+
+ $url=url_out($url,"abs"=>1);
+ $status||=Apache::HTTP_MOVED_TEMPORARILY;
+ $W->{"r"}->status($status);
+ $W->{"r"}->header_out("Location"=>$url);
+ $W->{"header_only"}=1;
+ My::Web->heading();
+ exit;
+ die "NOTREACHED";
+}
+
sub remote_ip ()
{
# Do not: PerlModule Apache::ForwardedFor
# As 'Apache::ForwardedFor' takes the first of $ENV{"HTTP_X_FORWARDED_FOR"}
# while the contents is '127.0.0.1, 213.220.195.171' if client has its own proxy.
# We must take the last item ourselves.
- my $r=$ENV{"HTTP_X_FORWARDED_FOR"} || $W->{"r"}->get_remote_host();
+ my $r=$W->{"r"}->headers_in()->{"X-Forwarded-For"} || $W->{"r"}->get_remote_host();
$r=~s/^.*,\s*//;
return $r;
}
return @r;
}
+sub no_cache($)
+{
+my($self)=@_;
+
+ header("Expires"=>"Mon, 26 Jul 1997 05:00:00 GMT"); # date in the past
+ header("Last-Modified"=>strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime())); # always modified
+ header("Cache-Control"=>"no-cache, must-revalidate"); # HTTP/1.1
+ header("Pragma"=>"no-cache"); # HTTP/1.0
+}
+
sub heading ()
{
my($class)=@_;
- return if $W->{"heading_passed"}++;
-
# $ENV{"CLIENT_CHARSET"} ignored (mod_czech support dropped!)
my $client_charset=$W->{"force_charset"} || "us-ascii";
header("Content-Style-Type"=>"text/css");
header("Content-Script-Type"=>"text/javascript");
+ $class->no_cache() if $W->{"no_cache"};
while (my($key,$val)=each(%{$W->{"headers"}})) {
$W->{"r"}->header_out($key,$val);
}
- $W->{"r"}->send_http_header("text/html; charset=$client_charset"); # "Content-type"; do not use header()
+ if (!$W->{"header_only"}) {
+ $W->{"r"}->send_http_header("text/html; charset=$client_charset"); # "Content-type"; do not use header()
+ }
- return if $W->{"heading_done"}++;
exit if $W->{"r"}->header_only();
+ return if $W->{"header_only"};
+ # We still can append headers before we put out some text.
+ # FIXME: It is not clean to still append them without overwriting.
+ return if $W->{"heading_done"}++;
if (1) { # || !$msie_major || $msie_major>=4) # TODO:dyn
- print '<?xml version="1.0" encoding="'.$client_charset.'"?>'."\n";
+ Wprint '<?xml version="1.0" encoding="'.$client_charset.'"?>'."\n";
}
- print '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'."\n";
- print '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">'."\n";
+ Wprint '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'."\n";
+ Wprint '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">'."\n";
my $title=$W->{"title_prefix"}.join("",map({ ': '.$_; } ($W->{"title"} || ())));
$title=~s#<[^>]*>##g;
- print "<head>";
- print "<title>$title</title>\n";
+ Wprint "<head>";
+ Wprint "<title>$title</title>\n";
if ($W->{"have_css"}) {
- print <<'HERE';
+ Wprint <<'HERE';
<style type="text/css"><!--
.cvs-id { font-family: monospace; }
.error { color: red; background-color: transparent; }
caption { caption-side: bottom; }
.footer img { vertical-align: middle; }
HERE
- print $W->{"head_css"}."\n";
- print "--></style>\n";
+ Wprint $W->{"head_css"}."\n";
+ Wprint "--></style>\n";
}
- print '<meta name="robots" content="'.($W->{"indexme"} ? "" : "no" ).'index,follow" />'."\n";
- print $W->{"head"};
+ Wprint '<meta name="robots" content="'.($W->{"indexme"} ? "" : "no" ).'index,follow" />'."\n";
+ Wprint $W->{"head"};
for my $type (qw(prev next index contents start up)) {
- do { print '<link rel="'.$type.'" href="'.$_.'" />'."\n" if $_; } for ($W->{"rel_$type"});
+ do { Wprint '<link rel="'.$type.'" href="'.$_.'" />'."\n" if $_; } for ($W->{"rel_$type"});
}
- print "</head><body";
-# print ' bgcolor="black" text="white" link="aqua" vlink="teal"'
+ Wprint "</head><body";
+# Wprint ' bgcolor="black" text="white" link="aqua" vlink="teal"'
# if $W->{"browser"}->netscape() && (!$W->{"browser"}->major() || $W->{"browser"}->major()<=4);
- print ">\n";
+ do { &{$_}($W) if $_; } for $W->{"body_attr_sub"};
+ Wprint ">\n";
- if ($W->{"heading"}) {
- do { &{$_}() if $_; } for ($W->{"heading"});
- }
+ do { &{$_}() if $_; } for ($W->{"heading"});
}
+BEGIN {
+ delete $W->{"__My::Web_init"};
+ }
+
1;