Apache2-SSI

 view release on metacpan or  search on metacpan

lib/Apache2/SSI.pm  view on Meta::CPAN

        $r->headers_out->unset( 'Content-Length' );
    }
    
    # Then, we might get called multiple time, since there may be multiple brigade buckets
    # Here, we retrieve the last buffer we put in $f->ctx->{data} if any
    my $html = exists( $ctx->{data} ) ? $ctx->{data} : '';
    $r->log->debug( "${class} [PerlOutputFilterHandler]: HTML data buffer set to '$html'." ) if( $debug > 0 );
    $ctx->{invoked}++;
    my $seen_eos = 0;
    my $max_length = int( $r->dir_config( 'Apache2_SSI_Max_Length' ) ) || 0;
    $r->log->debug( "${class} [PerlOutputFilterHandler]: Maximum length set to '$max_length'." ) if( $debug > 0 );
    # Get all the brigade buckets data
    for( my $b = $bb->first; $b; $b = $bb->next( $b ) )
    {
        $seen_eos++, last if( $b->is_eos );
        $b->read( my $bdata );
        $html .= $bdata;
        return( &Apache2::Const::DECLINED ) if( $max_length && length( $html ) >= $max_length );
    }
    
    # If we have not reached the special End-of-String bucket, we store our buffer in $f->ctx->{data} and return OK
    if( !$seen_eos )
    {
        # store context for all but the last invocation
        $r->log->debug( "${class} [PerlOutputFilterHandler]: Not reached the EOS bucket. Storing html to data buffer." ) if( $debug > 0 );
        $ctx->{data} = $html;
        $f->ctx( $ctx );
        return( &Apache2::Const::OK );
    }
    
    # Let's behave well as per the doc
    if( $f->c->keepalive == &Apache2::Const::CONN_KEEPALIVE )
    {
        $r->log->debug( "${class} [PerlOutputFilterHandler]: KeepAlive count (", $f->c->keepalive, ") reached the threshold of '", &Apache2::Const::CONN_KEEPALIVE, "'." ) if( $debug > 0 );
        $ctx->{data} = '';
        $f->ctx( $ctx );
    }
    
    my $size = length( $html );
    $ctx->{data} = '';
    $ctx->{invoked} = 0;
    $f->ctx( $ctx );
    if( $size == 0 )
    {
        $r->log->debug( "${class} [PerlOutputFilterHandler]: Data received is empty. Nothing to do." );
        return( &Apache2::Const::OK );
    }

    local $@;
    # try-catch
    $html = eval
    {
        Encode::decode( 'utf8', $html, Encode::FB_CROAK );
    };
    if( $@ )
    {
        $r->log->error( "${class} [PerlOutputFilterHandler]: Failed to decode data from utf8: $@" );
        return( &Apache2::Const::DECLINED );
    }
    
    #W We just add that the charset is utf-8
    $main->content_type( 'text/html; charset=utf-8' ) unless( $main->content_type =~ /\bcharset\n/i );
    
    my $params =
    {
    apache_filter => $f,
    apache_request => $r,
    debug => 3,
    };
    my $val;
    my $map = 
    {
    DEBUG   => 'debug',
    Echomsg => 'echomsg',
    Errmsg  => 'errmsg',
    Sizefmt => 'sizefmt',
    Timefmt => 'timefmt',
    };
    foreach my $key ( keys( %$map ) )
    {
        if( length( $val = $r->dir_config( "Apache2_SSI_${key}" ) ) )
        {
            $params->{ $map->{ $key } } = $val;
        }
    }
    if( $r->dir_config( 'Apache2_SSI_Expression' ) eq 'legacy' )
    {
        $params->{legacy} = 1;
    }
    elsif( $r->dir_config( 'Apache2_SSI_Expression' ) eq 'trunk' )
    {
        $params->{trunk} = 1;
    }
    $r->log->debug( "${class} [PerlOutputFilterHandler]: Creating a ${class} object." ) if( $debug > 0 );
    my $self = $class->new( $params ) || do
    {
        $r->log->error( "Error instantiating ${class}: ", $class->error );
        return( &Apache2::Const::DECLINED );
    };
    # my $addr = $r->useragent_addr;
    my $res = $self->parse( $html );
    if( !defined( $res ) )
    {
        $r->log->error( "${class} [PerlOutputFilterHandler]: is unable to process data: ", $self->error );
        return( &Apache2::Const::DECLINED );
    }
    else
    {
        # try-catch
        $res = eval
        {
            Encode::encode( 'utf8', $res, Encode::FB_CROAK );
        };
        if( $@ )
        {
            $r->log->error( "${class} [PerlOutputFilterHandler]: encountered an error while trying to encode data into utf8: $@" );
            return( &Apache2::Const::DECLINED );
        }
        
        # $r->headers_out->unset( 'Content-Length' );
        my $len = length( $res );
        # try-catch

lib/Apache2/SSI.pm  view on Meta::CPAN

    # No http header outside of Apache
    else
    {
        return( '' );
    }
}

sub parse_func_sha1
{
    my $self = shift( @_ );
    my $val  = join( '', @_ );
    require Digest::SHA;
    return( Digest::SHA::sha1_hex( $val ) );
}

sub parse_func_tolower
{
    my $self = shift( @_ );
    return( lc( join( '', @_ ) ) );
}

sub parse_func_toupper
{
    my $self = shift( @_ );
    return( uc( join( '', @_ ) ) );
}

sub parse_func_unbase64 { return( shift->decode_base64( join( '', @_ ) ) ); }

sub parse_func_unescape { return( shift->decode_uri( join( '', @_ ) ) ); }

sub parse_if
{
    my( $self, $args ) = @_;
    unshift( @{$self->{if_state}}, 0 );
    unshift( @{$self->{suspend}}, $self->{suspend}->[0] );
    return( '' ) if( $self->{suspend}->[0] );
    return( $self->_handle_ifs( $self->parse_eval_expr( $args->{expr} ) ) );
}

sub parse_include
{
    my( $self, $args ) = @_;
    unless( exists( $args->{file} ) or exists( $args->{virtual} ) )
    {
        return( $self->error( "No 'file' or 'virtual' attribute found in SSI 'include' tag" ) );
    }
    my $f = $self->find_file( $args );
    unless( $f->code == 200 )
    {
        return( $self->errmsg );
    }
    my $filename = $f->filename;
    if( !-e( "$filename" ) )
    {
        return( $self->errmsg );
    }
    
    # TODO This needs to be improved, as we should not assume the file encoding is utf8
    # It could be binary or some other text encoding like iso-2022-jp
    # So we should slurp it, parse the meta tags if this is an html and decode if the charset attribute is set or default to utf8
    # But this complicates things quite a bit, so for now, it is just utf8 simply
    my $html = $f->slurp_utf8;
    if( !defined( $html ) )
    {
        $self->error( "Unable to get html data of included file \"", $f->filename, "\": ", $f->error );
        return( $self->errmsg );
    }
    my $clone = $self->clone || do
    {
        warn( $self->error );
        return( $self->errmsg );
    };
    # share our environment variables with our clone so we pass it to included files.
    # If we are running under mod_perl, we'll use subprocess_env
    my $env = $self->env;
    $clone->{_env} = $env;
    return( $clone->parse( $html ) );
}

# NOTE: Legacy
# http://perl.apache.org/docs/1.0/guide/snippets.html#Passing_Arguments_to_a_SSI_script
sub parse_perl
{
    my( $self, $args, $margs ) = @_;
    my $r = $self->apache_request;

    my( $pass_r, @arg1, @arg2, $sub ) = (1);
    {
        my @a;
        while( @a = splice( @$margs, 0, 2 ) )
        {
            $a[1] =~ s/\\(.)/$1/gs;
            if( lc( $a[0] ) eq 'sub' )
            {
                $sub = $a[1];
            }
            elsif( lc( $a[0] ) eq 'arg' )
            {
                push( @arg1, $a[1] );
            }
            elsif( lc( $a[0] ) eq 'args' )
            {
                push( @arg1, split( /,/, $a[1] ) );
            }
            elsif( lc( $a[0] ) eq 'pass_request' )
            {
                $pass_r = 0 if( lc( $a[1] ) eq 'no' );
            }
            elsif( $a[0] =~ s/^-// )
            {
                push( @arg2, @a );
            }
            # Any unknown get passed as key-value pairs
            else
            {
                push( @arg2, @a );
            }
        }
    }

lib/Apache2/SSI.pm  view on Meta::CPAN

The query string value is set automatically when you provide an L<document_uri> upon instantiation or after:

    $ssi->document_uri( '/some/path/to/file.html?q=something&l=ja_JP' );

This will also set automatically the C<QUERY_STRING> environment variable.

=head2 remote_ip

Sets or gets the remote ip address of the visitor.

Under Apache mod_perl, this will call L<Apache2::Connection/remote_ip> for version 2.2 or lower and will call L<Apache2::Connection/useragent_ip> for version above 2.2, and otherwise this will get the value from the environment variable C<REMOTE_ADDR...

This value can also be overriden by being provided during object instantiation.

    # Pretend the ssi directives are accessed from this ip
    $ssi->remote_ip( '192.168.2.20' );

This is useful when one wants to check how the rendering will be when accessed from certain ip addresses.

This is used primarily when there is an expression such as

    <!--#if expr="-R '192.168.1.0/24' -->
    Visitor is part of my private network
    <!--#endif -->

or

    <!--#if expr="v('REMOTE_ADDR') -R '192.168.1.0/24' -->
    <!--#include file="/home/john/special_hidden_login_feature.html" -->
    <!--#endif -->

L<Apache2::Connection> also has a L<Apache2::Connection/remote_addr> method, but this returns a L<APR::SockAddr> object that is used to get the binary version of the ip. However you can also get the string version like this:

    use APR::SockAddr ();
    my $ip = $r->connection->remote_addr->ip_get();

Versions above 2.2 make a distinction between ip from direct connection, or the real ip behind a proxy, i.e. L<Apache2::Connection/useragent_ip>

=head2 request_uri

This is an alias for L</document_uri>

=head2 server_version

Returns the server version as a L<version> object can caches that value.

Under mod_perl2, it uses L<Apache2::ServerUtil/get_server_description> and outside of mod_perl, it tries to find C<apxs> using L<File::Which> and in last resort, tries to find the C<apache2> or C<httpd> binary to get its version information.

=head2 sizefmt

Sets or gets the formatting for file sizes. Value can be either C<bytes> or C<abbrev>

=head2 timefmt

Sets or gets the formatting for date and time values. The format takes the same values as L<POSIX/strftime>

=head1 Encoding

At present time, the html data are treated as utf8 data and decoded and encoded back as such.

If there is a need to broaden support for other charsets, let me know.

=head1 SSI Directives

This is taken from Apache documentation and summarised here for convenience and clarity to the perl community.

=head2 config

    <!--#config errmsg="Error occurred" sizefmt="abbrev" timefmt="%B %Y" -->
    <!--#config errmsg="Oopsie" -->
    <!--#config sizefmt="bytes" -->
    # Thursday 24 December 2020
    <!--#config timefmt="%A $d %B %Y" -->

=head2 echo

     <!--#set var="HTMl_TITLE" value="Un sujet intéressant" -->
     <!--#echo var="HTMl_TITLE" encoding="entity" -->

Encoding can be either C<entity>, C<url> or C<none>

=head2 exec

    # pwd is "print working directory" in shell
    <!--#exec cmd="pwd" -->
    <!--#exec cgi="/uri/path/to/prog.cgi" -->

=head2 include

    # Filesystem file path
    <!--#include file="/home/john/var/quote_of_the_day.txt" -->
    # Relative to the document root
    <!--#include virtual="/footer.html" -->

=head2 flastmod

     <!--#flastmod file="/home/john/var/quote_of_the_day.txt" -->
     <!--#flastmod virtual="/copyright.html" -->

=head2 fsize

    <!--#fsize file="/download/software-v1.2.tgz" -->
    <!--#fsize virtual="/images/logo.jpg" -->

=head2 printenv

    <!--#printenv -->

=head2 set

    <!--#set var="debug" value="2" -->

=head2 if, elif, endif and else

    <!--#if expr="$debug > 1" -->
    I will print a lot of debugging
    <!--#else -->
    Debugging output will be reasonable
    <!--#endif -->

or with new version of Apache SSI:



( run in 1.236 second using v1.01-cache-2.11-cpan-39bf76dae61 )