package Apache::Inject; use 5.010000; use strict; our $VERSION = '0.01'; use mod_perl2; use Apache2::CmdParms (); use Apache2::Const qw/OR_LIMIT OR_AUTHCFG TAKE12/; use Apache2::Log (); use Apache2::Module (); my @directives = ({ name => 'Inject', func => __PACKAGE__.'::Inject', req_override => OR_LIMIT|OR_AUTHCFG, args_how => TAKE12, errmsg => 'Inject HeadFile[!] FootFile[!]', }); Apache2::Module::add(__PACKAGE__, \@directives); sub Inject { my ($self, $parms, @args) = @_; # Construct directives for passing arguments to handler my $head = $args[0]; my $foot = $args[1] || '-'; # single hyphen signifies absence of argument $head =~ s/\\/\\\\/; $head =~ s/"/\\"/; $foot =~ s/\\/\\\\/; $foot =~ s/"/\\"/; # Add relevant directives to current configuration $parms->add_config([ 'PerlOutputFilterHandler Apache::Inject::Filter', qq{PerlSetVar InjectHead "$head"}, qq{PerlSetVar InjectFoot "$foot"}, ]); } 1; __END__ =encoding latin1 =head1 NAME Apache::Inject - Apache directive for injecting HTML headers and footers =head1 SYNOPSIS LoadModule perl_module libexec/apache24/mod_perl.so PerlLoadModule Apache::Inject DocumentRoot /usr/local/www/apache24/data # Inject both header and footer on all pages on the server Inject head.html foot.html # Inject only header on pages under /blog Inject head.html # Inject only footer on pages named index.html Inject - foot.html =head1 DESCRIPTION Apache::Inject is a mod_perl module that adds an Apache directive called Inject. The Inject directive takes one or two arguments, which correspond to the file names of two HTML files in the document root. The contents of these files are then inserted into any requested HTML file. The first file (the header) is inserted at the top of the body of the requested HTML file, while the second, optional file (the footer) is inserted at the bottom of the body. The directive is smart enough to place the header and footer in the proper places. The contents of the header file is inserted after any elements belonging to EheadE and before any elements belonging to EbodyE (regardless of whether any explicit EheadE or EbodyE tag is present in the source of the requested HTML page). Likewise, the contents of the second file is placed before any potential final E/htmlE. The Inject directive serves a much more specific purpose than server-side includes. It is designed for injecting headers and footers that belong in the EbodyE element, such as headings, menu bars and copyright notices. While you can technically include EheadE elements in your header file, Apache::Inject will place them in the body of the HTML page and not in the head. The main benefit over server-side includes is that the header and footer is specified in the server configuration instead of the HTML files themselves. Thus, it is useful for adding headers and footers to a large number of pre-existing static HTML pages. Furthermore, this means that the headers and footers on all pages can be changed at once by a single change in the server configuration. Please note: =over =item * The Inject directive is valid only inside directory sections, such as EDirectoryE, ELocationE and EFilesMatchE blocks. It is valid in .htaccess files if AllowOverride Limit/AuthConfig/All is enabled. =item * The file paths given to Inject are relative to the B of the current server or virtual server -- B the directory to which the current directory section or .htaccess file applies. They should be specified without a leading slash. =back =head1 INSTALLATION To install this module type the following: perl Makefile.PL make make test make install Note that all steps in this process require mod_perl2 to be installed. I recommend installing mod_perl2 via your operating system's package manager or ports collection before installing Apache::Inject. Note further that, because they depend on Apache, the tests require an unprivileged user and will be skipped if they are run as root. This is relevant if you install Apache::Inject via App::Cpan, which normally runs as root. =head1 SYNTAX The Inject directive takes one or two arguments: Inject HEADER_FILE [FOOTER_FILE] Each argument can consist of one of two things: =over =item 1. the path to a file relative to the document root, or =item 2. a single hyphen (C<->), signifying the absence of an argument. =back Passing a hyphen as the first argument disables the header, and passing a hyphen as the second argument disables the footer. If you leave out the second argument, then it is implicitly equivalent to a hyphen. =head1 OPERATION Behind the scenes, the Inject directive works as an alias for PerlOutputFilterHandler and PerlSetVar. For example, C results in the following configuration being added: PerlOutputFilterHandler Apache::Inject::Filter PerlSetVar InjectHeader head.html PerlSetVar InjectFooter foot.html This results in Apache::Inject::Filter being registered as an output filter for requests to the current directory or location. Apache::Inject::Filter accepts all requests where the content type is C. It receives the contents of the original page from Apache and, in addition, reads the contents of the C and C files. It then concatenates all of these intelligently and forwards their combined contents. =head1 CAVEATS Apache::Inject::Filter uses regular expressions to determine the proper location of the injected header. It supports all valid HTML. However, it does not take into account that embedded CSS and JavaScript code can contain strings that look like valid opening and closing HTML tags. On FreeBSD, you may need to enable the accf_http kernel module in order for the tests to work. Note that Apache::Inject works fine without the module; it is only the tests that require it. =head1 DIAGNOSTICS Apache::Inject and Apache::Inject::Filter log all errors and warnings to the Apache log file. Below is a list of all issued errors and warnings. Note that whenever Apache::Inject::Filter issues an error or a warning, this means that it also declines the request, letting Apache handle it as it would if the Inject directive were not used. =over =item Error: InjectHead/InjectFoot should not begin with slash, as it is already always relative to document root The paths given to Inject are always relative to the document root, even if the Inject directive is located within a directory section that applies to another path. Beginning any of the paths with a slash implies that there would be some difference in behavior compared to omitting the slash, which is false. =item Error: InjectHead/InjectFoot cannot extend past document root This error is issued if any of the paths given to Inject tries to go above the document root by using C<../>. =item Error: InjectHead/InjectFoot I does not exist This error is issued if any of the paths given to Inject doesn't exist. =item Warning: Declining request due to empty document root This warning is issued if Apache::Inject::Filter for some reason cannot retrieve the current document root from Apache. =back =head1 AUTHOR John Ankarström, Ejohn [at] ankarstrom.seE =head1 SEE ALSO mod_perl2: L =head1 COPYRIGHT AND LICENSE Copyright (C) 2021 by John Ankarström This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.32.1 or, at your option, any later version of Perl 5 you may have available. =cut