#!/usr/pkg/bin/perl
package IkiWiki::Plugin::loginselector;

use warnings;
use strict;
use IkiWiki 3.00;

# Plugins that provide login methods can register themselves here.
# Note that the template and js file also have be be modifed to add a new
# login method.
our %login_plugins;

sub register_login_plugin ($$$$) {
	# Same as the name of the plugin that is registering itself as a
	# login plugin. eg, "openid"
	my $plugin_name=shift;
	# This sub is passed a cgi object and a template object which it
	# can manipulate. It should return true if the plugin can be used
	# (it might load necessary modules for auth checking, for example).
	my $plugin_setup=shift;
	# This sub is passed a cgi object, and should return true
	# if it looks like the user is logging in using the plugin.
	my $plugin_check_input=shift;
	# This sub is passed a cgi object, a session object, an error
	# display callback, and an info display callback, and should
	# handle the actual authentication. It can either exit w/o
	# returning, if it is able to handle auth, or it can pass an
	# error message to the error display callback to make the
	# openid selector form be re-disiplayed with an error message
	# on it.
	my $plugin_auth=shift;
	$login_plugins{$plugin_name}={
		setup => $plugin_setup,
		check_input => $plugin_check_input,
		auth => $plugin_auth,
	};
}

sub login_selector {
	my $real_cgi_signin=shift;
	my $otherform_label=shift;
	my $q=shift;
	my $session=shift;

	my $template=IkiWiki::template("login-selector.tmpl");

	foreach my $plugin (keys %login_plugins) {
		if (! $login_plugins{$plugin}->{setup}->($template)) {
			delete $login_plugins{$plugin};
		}
		else {
			$template->param("login_selector_$plugin", 1);
		}
	}

	foreach my $plugin (keys %login_plugins) {
		if ($login_plugins{$plugin}->{check_input}->($q)) {
			$login_plugins{$plugin}->{auth}->($q, $session, sub {
				$template->param(login_error => shift());
			}, sub {
				$template->param(login_info => shift());
			});
			last;
		}
	}

	$template->param(
		cgiurl => IkiWiki::cgiurl(),
		($real_cgi_signin ? (otherform => $real_cgi_signin->($q, $session, 1)) : ()),
		otherform_label => $otherform_label,
	);

	IkiWiki::printheader($session);
	print IkiWiki::cgitemplate($q, "signin", $template->output);
	exit;
}

sub import {
	add_underlay("login-selector");
	add_underlay("jquery");
	hook(type => "getsetup", id => "loginselector",  call => \&getsetup);
	hook(type => "checkconfig", id => "loginselector", call => \&checkconfig);
	hook(type => "auth", id => "loginselector", call => \&authstub);
}

sub checkconfig () {
	if ($config{cgi}) {
		# Intercept normal signin form, so the login selector
		# can be displayed.
		# 
		# When other auth hooks are registered, give the selector
		# a reference to the normal signin form.
		require IkiWiki::CGI;
		my $real_cgi_signin;
		my $otherform_label=gettext("Other");
		if (keys %{$IkiWiki::hooks{auth}} > 1) {
			# Special case to avoid labeling password auth as
			# "Other" when it's the only auth plugin not
			# integrated with the loginselector.
			my %h=%{$IkiWiki::hooks{auth}};
			foreach my $p (keys %login_plugins) {
				delete $h{$p};
			}
			delete $h{loginselector};
			if (keys %h == 1 && exists $h{passwordauth}) {
				$otherform_label=gettext("Password");
			}
			if (keys %h > 0) {
				$real_cgi_signin=\&IkiWiki::cgi_signin;
			}
		}
		inject(name => "IkiWiki::cgi_signin", call => sub ($$) {
			login_selector($real_cgi_signin, $otherform_label, @_);
		});
	}
}

sub getsetup () {
	return
		plugin => {
			# this plugin is safe but only makes sense as a
			# dependency
			safe => 0,
			rebuild => 0,
		},
}

sub authstub ($$) {
	# While this hook is not currently used, it needs to exist
	# so ikiwiki knows that the wiki supports logins, and will
	# enable the Preferences page.
}

1
