package Minima; use strict; our $Dispatcher; our @Langs = qw(ru en fr); local *FLOG; sub import { my ($class, $cmd, $project) = @_; my $cpkg = caller; if ($cmd eq 'PROJECT') { $Dispatcher = (bless {}, $class)->reconfigure($project); } elsif ($cmd eq 'LOADFORE') { require $class->moduleFILE($cpkg, 1); } return; } sub finalize { my $class = shift; splice @INC, 0, scalar(@INC) - $Dispatcher->{sys_inc} if $Dispatcher && $Dispatcher->{sys_inc}; undef $Dispatcher; $class; } sub dispatcher { $Dispatcher || die "dispatcher not initialized yet"; } sub reconfigure { my ($this, $project) = @_; # SELF-IDENTIFYING ($this->{MINIMA}) = $INC{"Minima.pm"} =~ m#^(.+?)/Minima\.pm$#; $this->{MINIMA} ||= '.'; $this->{PROJECT} = $project || $ENV{SERVER_NAME} || $this->fatal("undefined engine name"); $this->{PROJECT} =~ s/\.homenet$//; # READ PROJECT CONFIG local *F; $_ = "$this->{MINIMA}/config.$this->{PROJECT}"; open F, $_ or $this->fatal("can't open project [$_] config: $!"); my (%cfg, @line); while () {@line = split; $cfg{$line[0]} = [@line[1..$#line]] if @line >= 2} close F; # PROJECT CONFIGURATION # (NB: dependencies are not processed recurrently) $this->setLang($cfg{Language}[0]); $cfg{Depends} ||= []; if ($this->{sys_inc}) { splice @INC, 0, scalar(@INC) - $this->{sys_inc}; } else { $this->{sys_inc} = @INC; } unshift @INC, grep {-d $_} map {"$this->{MINIMA}/$_/modules"} @{$cfg{Depends}}; umask 0000; for (qw/POOL LOGS COMP/) { ($this->{$_} = $this->{MINIMA}) =~ s/(\-\w+)?$/'-'.lc()/e; mkdir $this->{$_}, 0770 unless -d $this->{$_}; } for (qw(SiteName DBC Mailer)) {$this->{$_} = join ' ', @{$cfg{$_}} if $cfg{$_}}; $this->{_cfg} = $this->{Cfg} = \%cfg; $ENV{DOCUMENT_ROOT} ||= $cfg{DocumentRoot}[0]; $ENV{MINIMA_HTTP_PROTOCOL} = $ENV{HTTPS} eq 'on' ? 'https://' : 'http://'; # MINIMA-SPECIFIC UMASK (write for groups granted) umask 0113; unless ($cfg{NoLog}) { # PREPARE LOGGING open(FLOG, ">>$this->{LOGS}/$this->{PROJECT}.errlog") or $this->fatal("can't open [$this->{LOGS}/$this->{PROJECT}.errlog]: $!"); { my $oldfs = select FLOG; $| = 1; select $oldfs; }; # SIG HANDLING $SIG{__WARN__} = sub { my $msg = shift; print FLOG sprintf("W [%s] %s", scalar(localtime time), $msg); }; $SIG{__DIE__} = sub { my $err = shift; warn($err); require Minima::Error; Minima::Error->handleFatal($err, $cfg{TurnKey}[0]); exit(1); }; } $this; } sub moduleFILE { my ($this, $name, $fore) = @_; $name =~ s#::#/#g; my @file = grep {-f $_} map {"$_/$name.pm"} @INC; $fore ? $file[1] : $file[0]; } sub configFILE { $_[0]->engineFile($_[1], 'config', 'conf', $_[2]) } sub skelFILE { $_[0]->engineFile($_[1], 'skel') } sub engineFile { my ($this, $name, $type, $ext, $fore) = @_; if ($ext) { $name =~ s#::#/#g; $name .= ".$ext"; } $name =~ s|^/||; my @file = grep {-f $_} map {"$_/$name"} @{ $this->{Dirs}{$type} ||= [grep {-d $_} map {"$this->{MINIMA}/$_/$type"} @{$this->{Cfg}{Depends}}] }; my $file = $fore ? $file[1] : $file[0]; return unless $file; my $comp = "$this->{COMP}/" . $this->getLang . substr($file, ($this->{MinimaPfxLen} ||= length $this->{MINIMA})); if (! -e $comp or ! $this->{Cfg}{NoCheckComp}[0] && (stat $comp)[9] < (stat $file)[9]) { require Minima::Comp; Minima::Comp->compile($file, $comp, [$name, $type, $ext, $fore]); } $comp; } sub getLang { $_[0]->{LANG} or $_[0]->fatal('lang not set') } sub setLang { my ($this, $lang) = @_; ($lang) = $ENV{HTTP_COOKIE} =~ /MINIMA_LANG=(\w{2})/ unless $lang; $lang ||= $Langs[0]; $this->fatal("unknown language [$lang]") unless grep {$lang eq $_} @Langs; $this->{LANGS} = [@Langs]; $this->{LANG} = $lang; } sub getRequest { $_[0]->{REQUEST} } sub fatal { my (undef, $msg) = @_; print STDERR "FATAL MINIMA: $msg\n"; exit(1); } 1;