#!/usr/bin/perl -w # # TWiki Enterprise Collaboration Platform, http://TWiki.org/ # # Copyright (C) 2000-2007 TWiki Contributors. # # 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. For # more details read LICENSE in the root of this distribution. # # 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. # # As per the GPL, removal of this notice is prohibited. # # Configuration script for TWiki. Once you have a basic webserver # configuration that lets you access this script, the rest of the # configuration process is done from here. # # The script works from the top down, by checking features of the # environment before moving on. The sequence is: # 1. Check the version of perl # 2. Check we have the modules to run this script # 3. Check the environment # 4. Check we have the modules to load the rest of configure # ... and so on. At any stage, the script reports any errors in the # best way it can given the environment established so far. # When the basic checks are complete, the script moves into the # real configuration steps; setting configuration variables. # # This phase of the configure environment follows a Model-View- # Controller pattern. # # Controller # This script is the controller; it handles communication with the # browser (and thus the user). Communication is very simple; this script # is re-invoked with different 'action' parameters to determine what it does. # # Model # The Model consists of a simple node tree, where each node represents a # structural element in the *presentation* of the configuration (this may # not be consistent with the structure of $TWiki:cfg, so beware). Each # leaf node has an associated Type (in the Types subdirectory) that has # collected model and view behaviours for the basic types. # The Model is independent of the language used to represent the # configuration. There is one parser/generator provided, TWikiCfg, but it # would be trivial to add others. # # The View is a DOM document, generated as HTML by a set of UI classes. # Because of some convoluted history, there are actually three sets of classes # that generate views. They are all subclasses of TWiki::Configure::UI # UIs - are top-level and pluggable UI components. All the main screens are # implemented here. # Checkers - are specialised UIs designed to give checking support for # variable values. Checkers also include the read-only checking # UIs used for checking environment. # Types - provide some UI support in the form of type-specific prompters. # this is really an abuse of the Model, but it saves creating # decorator classes for all the Model types. # HTML is generated for the model using Visitor pattern. Each node in the tree # is visited in depth-first order. # use strict; use warnings; # This is absolutely essential for error reporting. We load it using # an eval so we can report the problem. eval "use CGI::Carp qw(fatalsToBrowser)"; if ($@) { print <<"REPORT"; Content-type: text/plain Could not load CGI::Carp. Please install this module before continuing. It can be downloaded from http://www.cpan.org The error seen was: $@ REPORT exit 1; } ########################################################### # VERY basic stuff required for configure to work. Any errors # during this phase will throw a die, which will be picked up # using CGI::Carp fatalsToBrowser # Warnings are fatal $SIG{'__WARN__'} = sub { die @_ }; eval 'require 5.00503'; die $@ if $@; # We warn against running TWiki on an older Perl version then 5.8.4 # but we will not let configure die in this situation. The user # may have updated many libraries and tweaked TWiki so let us give # him a chance. my $perlversion = $]; if ($perlversion < 5.006) { print STDERR <) || ''; }; eval { $WebServer_gid = join(',', map { lc(getgrgid( $_ )) } split( ' ', $( )); }; if( $@ ) { # Try to use Cygwin's 'id' command - may be on the path, since Cygwin # is probably installed to supply ls, egrep, etc - if it isn't, give # up. # Run command without stderr output, to avoid CGI giving error. # Get names of primary and other groups. $WebServer_gid = lc(qx(sh -c '( id -un ; id -gn) 2>/dev/null' 2>nul )); } my $localLibFailure; sub _loadBasicModule { my ($module) = @_; eval "use $module"; if ($@) { my $reason = "Failed to load the perl module $module. The module "; # See if we can find the .pm on @INC my $foundAt = "could not be found. "; my $modpath = $module; if ($modpath =~ /^([\w:]+)/) { $modpath =~ s#::#/#g; $modpath .= '.pm'; foreach my $path (@INC) { if (-e "$path/$modpath") { $foundAt = "was found at $path/$modpath"; if (!-r $foundAt) { $foundAt .= ", but I don't have permission to read it."; } last; } } } $reason .= $foundAt; $reason .= <splitdir($1); pop(@root); my @script = File::Spec->splitdir($0); my $scriptName = pop(@script); $scriptName =~ s/.*[\/\\]//; # Fix for Item3511, on Win XP # Try to load the LocalLib.cfg optional overload # Paths from LocalLib.cfg (preferred) use vars qw( $twikiLibPath @localPerlLibPath ); eval 'require "setlib.cfg"'; if ($@) { # No joy. Remember the failure so we can report it later. $localLibFailure = $@; # Stick the root/lib on the path; there's a high probability we'll be # able to find the bits of TWiki::Configure that way. We will report # the setlib error later. unshift(@INC, File::Spec->catfile(@root, 'lib')); } # Load all the bits of the configure module that we explicitly use # The loadBasicModule does some extra analysis on errors. foreach my $module ( 'Cwd', 'Data::Dumper', 'File::Copy', 'File::Temp', 'TWiki::Configure::Checker', 'TWiki::Configure::CSS', 'TWiki::Configure::Item', 'TWiki::Configure::JS', 'TWiki::Configure::Load', 'TWiki::Configure::Pluggable', 'TWiki::Configure::Root', 'TWiki::Configure::Section', 'TWiki::Configure::Type', 'TWiki::Configure::Types::BOOLEAN', 'TWiki::Configure::Types::NUMBER', 'TWiki::Configure::Types::SELECT', 'TWiki::Configure::Types::STRING', 'TWiki::Configure::TWikiCfg', 'TWiki::Configure::UI', 'TWiki::Configure::UIs::Section', 'TWiki::Configure::Value', 'TWiki::Configure::Valuer', ) { _loadBasicModule($module); } $| = 1; # no buffering on STDOUT ########################################################### # From this point on we shouldn't have any more "fatal" (to configure) # errors, so we can report errors in the browser (i.e. without using die) # We are configuring $TWiki::cfg, so we need to be in package TWiki from # now on. package TWiki; # We keep the actual config, and the default from TWiki.cfg, separate use vars qw( %cfg $defaultCfg ); # Declared in TWiki to support checkers use vars qw( $query ); # 'constants' used in TWiki.cfg use vars qw( $TRUE $FALSE ); $TRUE = 1; $FALSE = 0; # Remember what we detected previously, for use by Checkers if( $scriptName =~ /(\.\w+)$/ ) { $TWiki::cfg{DETECTED}{ScriptExtension} = $1; } # very basic tool sub findFileOnPath { my $file = shift; $file =~ s(::)(/)g; foreach my $dir ( @INC ) { if ( -e "$dir/$file" ) { return "$dir/$file"; } } return undef; } ########################################################### # Grope the OS. This duplicates a bit of code in TWiki.pm, # but it has to be duplicated because we don't want to deal # with loading TWiki just yet. unless( $TWiki::cfg{DetailedOS} ) { $TWiki::cfg{DetailedOS} = $^O; unless( $TWiki::cfg{DetailedOS} ) { require Config; $TWiki::cfg{DetailedOS} = $Config::Config{osname}; } } unless( $TWiki::cfg{OS} ) { if ($TWiki::cfg{DetailedOS} =~ /darwin/i) { # MacOS X $TWiki::cfg{OS} = 'UNIX'; } elsif ($TWiki::cfg{DetailedOS} =~ /Win/i) { $TWiki::cfg{OS} = 'WINDOWS'; } elsif ($TWiki::cfg{DetailedOS} =~ /vms/i) { $TWiki::cfg{OS} = 'VMS'; } elsif ($TWiki::cfg{DetailedOS} =~ /bsdos/i) { $TWiki::cfg{OS} = 'UNIX'; } elsif ($TWiki::cfg{DetailedOS} =~ /dos/i) { $TWiki::cfg{OS} = 'DOS'; } elsif ($TWiki::cfg{DetailedOS} =~ /^MacOS$/i) { # MacOS 9 or earlier $TWiki::cfg{OS} = 'MACINTOSH'; } elsif ($TWiki::cfg{DetailedOS} =~ /os2/i) { $TWiki::cfg{OS} = 'OS2'; } else { $TWiki::cfg{OS} = 'UNIX'; } } $query = new CGI; my $url = $query->url(); my $action = $query->param('action') || 'Configure'; my $expertsMode = defined($query->param('expert')); # Handle serving an image embedded in the configure page, before generating # any other output if( $action eq 'image' ) { my $image = $query->param('image' ); $image =~ /^([-.\w]+)$/; # filter-in $image = $1; if( (defined($image)) && (($image eq 'favicon.ico') || ($image eq 'T-logo34x26-t.gif') || ($image eq 'T-logo-140x40-t.gif') || ($image eq 'warning.gif') || ($image eq 'info.gif')) ) { #ignore $query->param('type') and set it using our special knowledge my $type = 'image/gif'; if ($image =~ /.*\.ico$/) { $type = 'image/x-icon'; } if (open(my $F, '<', 'logos/'.$image)) { local $/ = undef; # SMELL: this call is correct, but causes a perl error # on some versions of CGI.pm # print $query->header(-type => $query->param('type')); # So use this instead: print 'Content-type: '.$type."\n\n"; print <$F>; close($F); } } exit 0; } my @meta = ( CGI::meta({ 'http-equiv'=>'Pragma', content=>'no-cache' }), CGI::meta({ 'http-equiv'=>'Cache-Control', content=>'no-cache' }), CGI::meta({ 'http-equiv'=>'Expires', content=>0 }), CGI::meta({ name=>'robots', content=>'noindex' }), CGI::Link( { -rel=>'icon', -href=>$scriptName.'?action=image;image=favicon.ico;type=image/x-icon', -type=>'image/x-icon' } ), CGI::Link( { -rel=>'shortcut icon', -href=>$scriptName.'?action=image;image=favicon.ico;type=image/x-icon', -type=>'image/x-icon' } ), CGI::script( { language => 'JavaScript', type => 'text/javascript' }, TWiki::Configure::JS::js1() ), CGI::style( { -type=>'text/css' }, TWiki::Configure::CSS::css()), CGI::script( { language => 'JavaScript', type => 'text/javascript' }, TWiki::Configure::JS::js2() ), ); # Generate standard page header my $hdr = CGI::start_html( -title => 'TWiki Configuration', -head => \@meta, -class => 'patternNoViewPage'); # XML header confuses IE, so strip it out. This is fixed in CGI.pm 3.06 # (and IE 7, but who's counting?) if ($CGI::VERSION < 3.06) { $hdr =~ s/^<\?xml.*?>//s; } print CGI::header('text/html'). $hdr; print <<'HERE';
HERE # use this script recursively to serve the icon image print CGI::img({src=>$scriptName.'?action=image;image=T-logo-140x40-t.gif;type=image/gif', class=>'logo', alt=>'TWiki', width=>'140', height=>'40'}); my $stub = new TWiki::Configure::Item(); my $sanityUI = TWiki::Configure::UI::loadChecker('BasicSanity', $stub); my ($sanityStatement, $badLSC) = $sanityUI->ui(); # This is the dispatcher; $action is the name of the action to perform, # this is concatenated to _action to determine the name of the procedure. # Dispatcher methods return a boolean to indicate whether to generate a # link back to the main page at the end. if ($sanityUI->insane() || $query->param('abort')) { print $sanityStatement; } else { $action =~ s/\W//g; my $method = '_action'.$action; die "Undefined action $action" unless defined(&$method); no strict 'refs'; my $reroute = &$method(); use strict 'refs'; if ($reroute) { print '
'; print CGI::a( { href=>$scriptName.'?t='.time(), rel => 'nofollow' }, 'Return to configuration'); print CGI::br(); print "
\n"; } } print <<'HERE';
 
HERE print CGI::end_html(),"\n"; ########################################################### # End of the main program; the rest is all subs sub _checkLoadUI { my ($uiname, $root) = @_; my $ui = TWiki::Configure::UI::loadUI($uiname, $root); unless ($ui) { print "Could not load $uiname UI. Error was:
$@
"; if ($@ =~ /Can't locate (\S+)/) { print <loadCGIParams($TWiki::query, \%updated); # create the root of the UI my $root = new TWiki::Configure::Root(); my $ui; if (!TWiki::Configure::UI::authorised()) { print CGI::h2('Authorisation is required to save.'); print CGI::div($modified.' configuration item'. ($modified==1?' was':'s were').' changed'); if ($modified) { print CGI::div(join(' ', keys %updated)); } $ui = _checkLoadUI('AUTH', $root); return 1 unless $ui; print $ui->ui(1, 'Save'); } else { # Load the specs from the .spec files and generate the UI template TWiki::Configure::TWikiCfg::load($root, 1); $ui = _checkLoadUI('UPDATE', $root); return 1 unless $ui; print $ui->ui($root, $valuer, \%updated); } return 1; } # Invoked by "find more extensions" button in the Extensions section sub _actionFindMoreExtensions { my $root = new TWiki::Configure::Root(); print CGI::h1( 'Find TWiki Extensions'); print '
'; my $ui = _checkLoadUI('EXTENSIONS',$root); return 1 unless $ui; print $ui->ui(); return 1; } # Invoked when an extension is to be installed sub _actionInstallExtension { my $root = new TWiki::Configure::Root(); my $ui; if (!TWiki::Configure::UI::authorised()) { $ui = _checkLoadUI('AUTH', $root); return 1 unless $ui; print $ui->ui(0, 'Install '.($query->param('extension')||'')); } else { $ui = _checkLoadUI('EXTEND', $root); return 1 unless $ui; print $ui->ui(); } return 1; } # This is the default screen sub _actionConfigure { $TWiki::Configure::UI::toterrors = 0; $TWiki::Configure::UI::totwarnings = 0; print CGI::h1( 'Configuration'); print $sanityStatement; # The first three sections go without a root my $stub = new TWiki::Configure::Item(); my $eui = TWiki::Configure::UI::loadChecker('Environment', $stub); # See if this platform has special detection or checking requirements # (most don't) $stub = new TWiki::Configure::Item(); my $osui = TWiki::Configure::UI::loadChecker( $Config::Config{osname}, $stub); $stub = new TWiki::Configure::Item(); my $cgiui = TWiki::Configure::UI::loadChecker('CGISetup', $stub); # Use a separate root for the _saveable_ sections my $root = new TWiki::Configure::Root(); my $valuer = new TWiki::Configure::Valuer( $TWiki::defaultCfg, \%TWiki::cfg); # Load the config structures. TWiki::Configure::TWikiCfg::load($root, !$badLSC); if (!$badLSC) { print <

Use this page to set the configuration options for TWiki. Fill in the settings, and then press 'Next'.

Explanation of colours and symbols:
  • Settings marked like this are required (they must have a value).
  • Any errors in your configuration will be highlighted.
  • Warnings are non-fatal, but are often a good indicator that something that is wrong.
  • The little δ after an entry means that the current value is not the same as the default value. If you hover the cursor over the δ, a popup will show you what the default value is.
  • EXPERT means a setting is for expert use only. You should not fiddle with it unless you know what you are doing, or at least have read all the documentation. HERE if (!$expertsMode) { print <
HERE } print CGI::start_form({ name=>'update', action=>$scriptName, method=>"post" }); # use time to make sure we never allow cacheing print CGI::hidden( 'time', time() ); print '
'; print CGI::div( { class => 'optionHeader' }, CGI::span( 'Settings' . CGI::span( { class => 'twikiSmall' }, 'Click the buttons below to open each section' ) ) . ' ' . CGI::span( { class => 'twikiSmall' }, CGI::a( { href => '#', rel => 'nofollow', onclick => 'toggleAllOptions(true); return false;' }, 'Open all options' ) ) ); print $eui->ui(); print $osui->ui() if $osui; print $cgiui->ui() if $cgiui; # Load the UI for the configuration and whack it out my $ui = _checkLoadUI('Root', $root); return 1 unless $ui; $ui->{experts} = $expertsMode; print $ui->ui($root, $valuer); print "
\n"; if ($TWiki::Configure::UI::toterrors || $TWiki::Configure::UI::totwarnings) { my $mess = 'Total: '.$TWiki::Configure::UI::toterrors.' error'. ($TWiki::Configure::UI::toterrors==1?'':'s').', '. $TWiki::Configure::UI::totwarnings.' warning'. ($TWiki::Configure::UI::totwarnings==1?'':'s'); print CGI::div($mess); } print CGI::p(CGI::submit(-class=>'twikiSubmit', -name=>'action', -value=>'Next', -accesskey=>'N')); print "Cancel and return to TWiki WebHome"; print CGI::end_form(); print <
DIVS return 0; } 1;