# Plugin for Foswiki - The Free and Open Source Wiki, http://foswiki.org/
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details, published at
# http://www.gnu.org/copyleft/gpl.html

=pod

---+ package Foswiki::Plugins::HttpsRedirectPlugin

To interact with TWiki use ONLY the official API functions
in the Foswiki::Func module. Do not reference any functions or
variables elsewhere in TWiki, as these are subject to change
without prior warning, and your plugin may suddenly stop
working.

For increased performance, all handlers except initPlugin are
disabled below. *To enable a handler* remove the leading DISABLE_ from
the function name. For efficiency and clarity, you should comment out or
delete the whole of handlers you don't use before you release your
plugin.

__NOTE:__ When developing a plugin it is important to remember that
TWiki is tolerant of plugins that do not compile. In this case,
the failure will be silent but the plugin will not be available.
See [[%SYSTEMWEB%.Plugins#FAILEDPLUGINS]] for error messages.

__NOTE:__ Defining deprecated handlers will cause the handlers to be 
listed in [[%SYSTEMWEB%.Plugins#FAILEDPLUGINS]]. See
[[%SYSTEMWEB%.Plugins#Handlig_deprecated_functions]]
for information on regarding deprecated handlers that are defined for
compatibility with older TWiki versions.

__NOTE:__ When writing handlers, keep in mind that these may be invoked
on included topics. For example, if a plugin generates links to the current
topic, these need to be generated before the afterCommonTagsHandler is run,
as at that point in the rendering loop we have lost the information that we
the text had been included from another topic.

=cut

package Foswiki::Plugins::HttpsRedirectPlugin;

# Always use strict to enforce variable scoping
use strict;
use warnings;

use Foswiki::Func();       # The plugins API
use Foswiki::Plugins();    # For the API version
use Foswiki::LoginManager::Session();
use CGI::Cookie();

# $VERSION is referred to by TWiki, and is the only global variable that
# *must* exist in this package.
#use vars qw( $VERSION $RELEASE $SHORTDESCRIPTION $debug $pluginName $NO_PREFS_IN_TOPIC );

# This should always be $Rev$ so that TWiki can determine the checked-in
# status of the plugin. It is used by the build automation tools, so
# you should leave it alone.
our $VERSION = '$Rev$ (2011-03-28)';

# This is a free-form string you can use to "name" your own plugin version.
# It is *not* used by the build automation tools, but is reported as part
# of the version number in PLUGINDESCRIPTIONS.
our $RELEASE = '1.1.1';

# Short description of this plugin
# One line description, is shown in the %SYSTEMWEB%.TextFormattingRules topic:
our $SHORTDESCRIPTION = 'Redirect authenticated users to HTTPS url.';

# You must set $NO_PREFS_IN_TOPIC to 0 if you want your plugin to use preferences
# stored in the plugin topic. This default is required for compatibility with
# older plugins, but imposes a significant performance penalty, and
# is not recommended. Instead, use $Foswiki::cfg entries set in LocalSite.cfg, or
# if you want the users to be able to change settings, then use standard TWiki
# preferences that can be defined in your %USERSWEB%.SitePreferences and overridden
# at the web and topic level.
our $NO_PREFS_IN_TOPIC = 1;

my $debug = 0;

=pod

---++ initPlugin($topic, $web, $user, $installWeb) -> $boolean
   * =$topic= - the name of the topic in the current CGI query
   * =$web= - the name of the web in the current CGI query
   * =$user= - the login name of the user
   * =$installWeb= - the name of the web the plugin is installed in

REQUIRED

Called to initialise the plugin. If everything is OK, should return
a non-zero value. On non-fatal failure, should write a message
using Foswiki::Func::writeWarning and return 0. In this case
%FAILEDPLUGINS% will indicate which plugins failed.

In the case of a catastrophic failure that will prevent the whole
installation from working safely, this handler may use 'die', which
will be trapped and reported in the browser.

You may also call =Foswiki::Func::registerTagHandler= here to register
a function to handle variables that have standard TWiki syntax - for example,
=%MYTAG{"my param" myarg="My Arg"}%. You can also override internal
TWiki variable handling functions this way, though this practice is unsupported
and highly dangerous!

__Note:__ Please align variables names with the Plugin name, e.g. if 
your Plugin is called FooBarPlugin, name variables FOOBAR and/or 
FOOBARSOMETHING. This avoids namespace issues.


=cut

sub initPlugin {
    my ( $topic, $web, $user, $installWeb ) = @_;
    my $requestObj;
    my $context = Foswiki::Func::getContext();

    # check for Plugins.pm versions
    if ( $Foswiki::Plugins::VERSION < 1.026 ) {
        Foswiki::Func::writeWarning(
            'Version mismatch between ' . __PACKAGE__ . ' and Plugins.pm' );
        return 0;
    }

    $debug = $Foswiki::cfg{Plugins}{HttpsRedirectPlugin}{Debug} || 0;
    if ( defined &Foswiki::Func::getRequestObject ) {

        # Foswiki 1.1+
        $requestObj = Foswiki::Func::getRequestObject();
    }
    else {

        # Foswiki 1.0
        $requestObj = Foswiki::Func::getCgiQuery();
    }
    if ( $requestObj->https() ) {
        if ( not Foswiki::Func::isGuest() ) {
            _setRedirectCookie( $requestObj, 1 );
        }
        else {
            _setRedirectCookie( $requestObj, undef );
        }
    }
    elsif (
        (
               not Foswiki::Func::isGuest()
            or $requestObj->cookie('FOSWIKIHttpsRedirect')
            or $context->{'login'}
        )
        and not $context->{'command_line'}
      )
    {
        _doRedirect($requestObj);
    }

    # Plugin correctly initialized
    return 1;
}

sub _doRedirect {
    my ($requestObj) = @_;
    my $url = $requestObj->url() . $requestObj->path_info();

    # TODO: We lose the #anchorpart here :-(
    if ( $requestObj->query_string() ) {
        $url .= '?' . $requestObj->query_string();
    }
    $url =~ s/^http/https/;
    Foswiki::Func::writeDebug("HttpsRedirectPlugin: redirecting to: $url")
      if $debug;
    Foswiki::Func::redirectCgiQuery( $requestObj, $url );

    return;
}

# ApacheLogin: Auth is carried in http rather than cookies. It's not
# possible to tell if a user has an authenticated session over on the
# https side if they've accidentally returned to http. So, we set a
# special cookie (with secure flag off) that can be read by the server
# on both http and https protocols.
sub _setRedirectCookie {
    my ( $requestObj, $value ) = @_;

    if ( $Foswiki::cfg{LoginManager} ne 'Foswiki::LoginManager::TemplateLogin' )
    {
        Foswiki::Func::writeDebug(
            'HttpsRedirectPlugin: Setting redirect cookie')
          if $debug;
        if (    $Foswiki::Plugins::SESSION
            and $Foswiki::Plugins::SESSION->{response} )
        {
            my @cookies = $Foswiki::Plugins::SESSION->{response}->cookies();

            if ( $value or $requestObj->cookie('FOSWIKIHttpsRedirect') ) {
                push(
                    @cookies,
                    CGI::Cookie->new(
                        -name     => 'FOSWIKIHttpsRedirect',
                        -value    => $value,
                        -httponly => 1
                    )
                );
            }
            $Foswiki::Plugins::SESSION->{response}->cookies( [@cookies] );
        }
        else {
            print STDERR
"HttpsRedirectPlugin - incompatible Foswiki: couldn't peek at response obj\n";
        }
    }
    elsif ($debug) {
        Foswiki::Func::writeDebug(
            'HttpsRedirectPlugin: Not setting redirect cookie for TemplateLogin'
        );
    }

    return;
}

1;
