# ================================================================== # Gossamer Links - enhanced directory management system # # Website : http://gossamer-threads.com/ # Support : http://gossamer-threads.com/scripts/support/ # CVS Info : 087,071,086,086,085 # Revision : $Id: Links.pm,v 1.227 2009/05/09 17:01:33 brewt Exp $ # # Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved. # Redistribution in part or in whole strictly prohibited. Please # see LICENSE file for full details. # ================================================================== package Links; # ================================================================== use strict; use vars qw/ $VERSION $DEBUG $IN $DB $CFG $USER $PLG $GLOBALS $TPL $LANGUAGE %STASH @EXPORT_OK %EXPORT_TAGS @ISA /; # Load Links::Custom before anything else. Usually, it is empty, but it is not # overwritten during upgrades, and so site-specific options (for example, a # "use lib" required to make your installation work) can go in there. use Links::Custom; use GT::Base qw/:all/; use Exporter(); use GT::AutoLoader; use GT::Config; use GT::CGI; use GT::Template; use GT::Plugins; use GT::Delay; use GT::Date; use Links::Config; use constants # Payment option constants GLOBAL => 0, NOT_ACCEPTED => 1, OPTIONAL => 2, REQUIRED => 3, # Payment expiry special values (anything else is a unix time) UNPAID => -1, UNLIMITED => 0x7fff_fffe, FREE => 0x7fff_ffff; $DEBUG = 0; $VERSION = '3.3.0'; @ISA = 'Exporter'; # $TPL is still exported for compatibility with (very) old plugins @EXPORT_OK = qw( $IN $CFG $DB $USER $PLG $TPL %STASH $MOD_PERL MOD_PERL SPEEDY PERSIST GLOBAL NOT_ACCEPTED OPTIONAL REQUIRED UNPAID FREE UNLIMITED VIEWABLE ); %EXPORT_TAGS = ( 'payment' => [qw/GLOBAL NOT_ACCEPTED OPTIONAL REQUIRED UNPAID FREE UNLIMITED/], 'objects' => [qw/$IN $CFG $DB $USER $PLG %STASH VIEWABLE/], 'persist' => [qw/MOD_PERL SPEEDY PERSIST/] ); # Returns a new condition object to use for viewable links. Currently, this # means isValidated = 'Yes', and, if payment support is enabled, # ExpiryDate >= time. You can use it as: # my $cond = VIEWABLE; $cond->add(some => other => condition); # or: # $table->select(VIEWABLE); # or: # $table->select({ column => $value }, VIEWABLE); sub VIEWABLE() { require GT::SQL::Condition; my $cond = GT::SQL::Condition->new(isValidated => '=' => 'Yes'); $cond->add(ExpiryDate => '>=' => time) if $CFG->{payment}->{enabled}; $cond; } sub init { # -------------------------------------------------------------------- # This function initializes Gossamer Links, and must be called before any other # action is performed. # my $path = shift || '.'; # If under mod_perl, reset our environments. if (PERSIST) { GT::SQL->reset_env if $INC{'GT/SQL.pm'}; GT::CGI->reset_env(); } # Get our config object. $CFG = Links::Config->new($path); my $debug = $CFG->{debug_level} || $DEBUG; # Set our tmp directory to store files. $ENV{GT_TMPDIR} = $CFG->{admin_root_path} . '/tmp'; $IN = GT::CGI->new(); $PLG = GT::Plugins->new(directory => "$CFG->{admin_root_path}/Plugins", debug => $debug); if ($DB and not ref $DB eq 'GT::Delay') { # $DB is already a GT::SQL object $DB = GT::SQL->new({ def_path => $CFG->{admin_root_path} . '/defs', cache => 1, debug => $debug, }); } else { $DB = GT::Delay( 'GT::SQL', 'HASH', { def_path => $CFG->{admin_root_path} . '/defs', cache => 1, debug => $debug, } ); } $PLG->dispatch('init', sub {}); (%STASH, $GLOBALS, $LANGUAGE, $USER) = (); } sub init_date { # -------------------------------------------------------------------- # Load the date module and setup any custom date strings. # return unless $CFG and not $STASH{date_loaded}; my $debug = defined $CFG->{debug_level} ? $CFG->{debug_level} : $DEBUG; require GT::Date; $GT::Date::DEBUG = $debug if ($debug); $GT::Date::OFFSET = $CFG->{date_offset} * 3600; $GT::Date::LANGUAGE = { 'month_names' => $CFG->{date_month_long}, 'day_names' => $CFG->{date_days_long}, 'short_month_names' => $CFG->{date_month_short}, 'short_day_names' => $CFG->{date_days_short} }; GT::Date::build_lang(); $STASH{date_loaded} = 1; } sub init_user { # -------------------------------------------------------------------- # User initialization, but passes through plugins. # $PLG->dispatch('init_user', sub { _plg_init_user(@_); }, @_); } sub _plg_init_user { # -------------------------------------------------------------------- # Check to see if the request is for a valid user, if so, set # $USER to the user. # GT::Template->root("$CFG->{admin_root_path}/templates/" . template_set()); # Authenticate the user. require Links::Authenticate; Links::Authenticate::auth('init'); $USER = undef; my $username = shift || $IN->param('Username') || undef; my $password = shift || $IN->param('Password') || undef; # Validate the username, either through logging on, or checking the # session. my $valid_user; if (defined $username && defined $password) { if (Links::Authenticate::auth('valid_user', { Username => $username, Password => $password })) { $valid_user = $username; } } else { $valid_user = Links::Authenticate::auth('valid_session'); } return unless $valid_user; # We have a valid_user, now let's get the user from Gossamer Links, if he's not there # then we auto-create him. $USER = Links::Authenticate::auth('get_user', { Username => $valid_user, Password => $password, auto_create => 1 }); if ($USER and $USER->{Status} eq 'Not Validated') { return; } return $USER; } sub init_admin { # ----------------------------------------------------------------------------- $PLG->dispatch('init_admin', sub { _plg_init_admin(@_); }, @_); } sub _plg_init_admin { # ----------------------------------------------------------------------------- $LANGUAGE = GT::Config->load("$CFG->{admin_root_path}/templates/admin/language.txt", { inheritance => 1, local => 1 }); } sub check_request { # -------------------------------------------------------------------- # Checks to make sure the request is allowed. # if ($CFG->{disabled}) { print $IN->header; print Links::SiteHTML::display('error', { error => Links::language('GENERAL_DISABLED') }); return; } if (ref $CFG->{bans}) { for (@{$CFG->{bans}}) { # Turn a ban into a regexp my $ban = quotemeta; # *'s match anything $ban =~ s/\\\*/.*/g; # +'s match anything with at least one character $ban =~ s/\\\+/.+/g; # ?'s match any single character $ban =~ s/\\\?/./g; if ($ENV{REMOTE_HOST} and $ENV{REMOTE_HOST} =~ /^$ban$/i or $ENV{REMOTE_ADDR} =~ /^$ban$/i) { print $IN->header; print Links::SiteHTML::display('error', { error => Links::language('GENERAL_BANNED') }); return; } } } return 1; } sub language { # -------------------------------------------------------------------- # Process a language request, it's only loaded once, and saved in # $LANGUAGE. All arguments are automatically html escaped unless the argument # is passed as a scalar ref. # $_[0] and $_[0] eq 'Links' and shift; my $code = shift || ''; $LANGUAGE ||= GT::Config->load("$CFG->{admin_root_path}/templates/" . template_set() . "/language.txt", { inheritance => 1, local => 1 }); if (exists $LANGUAGE->{$code}) { return @_ ? sprintf($LANGUAGE->{$code}, map { ref $_ ? $$_ : $IN->html_escape($_) } @_) : $LANGUAGE->{$code}; } else { return if ($code =~ /^PAYMENT_/); return $code; } } sub https { # ----------------------------------------------------------------------------- # Returns 1 if the request is (probably) under HTTPS. Under CGI or front-end # mod_perl servers this is easy - just check for $ENV{HTTPS} eq 'on'. Under # proxied back-end mod_perl servers it is _IMPOSSIBLE_. # return ($ENV{HTTPS} and $ENV{HTTPS} eq 'on'); } sub config_vars { # -------------------------------------------------------------------- # Return the config as a hash for use in templates. We flatten array refs # and hash refs. # my $t = {}; while (my ($key, $val) = each %$CFG) { (ref $val eq 'ARRAY') and ($val = join ",", @$val); (ref $val eq 'HASH') and do { my $tmp; foreach (sort keys %$val) { $tmp .= "$_ = $val->{$_}, "; } chop $tmp; chop $tmp; $val = $tmp; }; $t->{"cfg_$key"} = $val; } return $t; } $COMPILE{admin_page} = __LINE__ . <<'END_OF_SUB'; sub admin_page { # ------------------------------------------------------------------ # Display an admin template page. # (defined $_[0] and $_[0] eq 'Links') and shift; my ($file, $params, $opts) = @_; $file ||= $IN->param('page'); $file =~ /\.\./ and return die "Invalid template '$file' requested (Invalid name)"; $file =~ m,^/, and return die "Invalid template '$file' requested (Invalid name)"; # Add on $IN $params ||= []; my $cgi = $IN->get_hash; for (keys %$cgi) { $cgi->{$_} = ref $cgi->{$_} eq 'ARRAY' ? join("\n", @{$cgi->{$_}}) : $cgi->{$_}; } ref $params eq 'ARRAY' ? (push @$params, $cgi) : ($params = [$cgi, $params]); push @$params, { config => $CFG, in => $IN }; # Make sure a root is set. $opts ||= {}; $opts->{root} ||= "$CFG->{admin_root_path}/templates/admin"; defined $opts->{print} or ($opts->{print} = 1); defined $opts->{inheritance} or ($opts->{inheritance} = 1); $opts->{stream} = 1 if not exists $opts->{stream} and $IN->param('stream'); my $cookies = []; if ($IN->param('set-cookie')) { foreach my $key ($IN->param) { if ($key =~ /^cookie-(.*)/) { my $path = $IN->param("cookie_path-$1") || '/'; my $exp = $IN->param("cookie_exp-$1") || '+5y'; push @$cookies, $IN->cookie(-name => $1, -value => $IN->param($key), -path => $path, -expires => $exp); } } } if ($opts->{print}) { @$cookies ? print $IN->header(-cookie => $cookies, -charset => $CFG->{header_charset}) : print $IN->header(-charset => $CFG->{header_charset}); } # Print the page. GT::Template->parse($file, $params, $opts); } END_OF_SUB sub template_set { # ----------------------------------------------------------------------------- # Tries to find and return a valid user template set name as well as a template # theme name. It will check input from the following in the listed order: # 1) The first argument to template_set() # 2) CGI input ($IN->param('t')) # 3) $CFG->{build_default_tpl} # If no valid template is found, then it fatals. If no valid theme exists then # no theme value is returned. # shift if @_ and UNIVERSAL::isa($_[0], 'Links'); my $template_set = shift; for ($template_set, $IN->param('t'), $CFG->{build_default_tpl}, 'luna') { next unless $_; # Template name + theme can be passed in as: