Revision 3 (by ahitrov@rambler.ru, 2010/03/24 15:19:32) The CORE
package Contenido::Apache;

# ----------------------------------------------------------------------------
# ����� ����� ��������� ��� �������� Apache...
# ----------------------------------------------------------------------------

use strict;
use warnings;
use locale;

use vars qw($VERSION);
$VERSION = '7.0';

use Apache::Constants;
use Apache::Request;

use Contenido::Globals;
use Contenido::Project;
use Contenido::Request;
use Contenido::State;

use Contenido::Keeper;
use Contenido::Object;
use Contenido::User;
use Contenido::Document;
use Contenido::Section;
use Contenido::Link;

use Contenido::Init;
use Contenido::Logger;

# ----------------------------------------------------------------------------
# �������, ������� ����������� ��� ������ ������� ������ ��������� ��������
#  web-�������.
# ----------------------------------------------------------------------------
sub child_init {
    my $r = shift;

    $log->info("������������ �������� ��������� �������� Apache") if $DEBUG;

    my $project_keeper_module;
    eval {
        $project_keeper_module=$state->project().'::Keeper';
        $keeper = $project_keeper_module->new($state);
    };
    if ($@) {
        $log->error("�� ���� ���������������� $project_keeper_module ��-��: $@");
        die;
    }

    for my $plugin ($state->project, split(/\s+/, $state->plugins)) {
        my $class = $plugin.'::Apache';
        eval { $class->child_init($r); };
        if ( $@ ) {
            $log->error("�� ���� ��������� ����� child_init ������� $plugin ($class) �� ������� '$@'");
        }
    }

    return OK;
}

# ----------------------------------------------------------------------------
# �������, ������� ����������� � ����� ������ ��������� ������� ������������.
# ----------------------------------------------------------------------------
sub request_init {
    my $r = shift;

    $r = ref($r) eq 'Apache::Request' ? $r : Apache::Request->instance($r);

    my $URI = $r->uri;
    my $ARGS = $r->args;

    $log->info("������ ��������� ������� ".$URI.($ARGS ? "?$ARGS":'')) if $DEBUG;

    $state->_refresh_();

    $request = Contenido::Request->new($state);

    if ($DEBUG) {
        $request->{_start} = Time::HiRes::time();
        $Contenido::Globals::DB_TIME   = 0;
        $Contenido::Globals::CORE_TIME = 0;
        $Contenido::Globals::DB_COUNT  = 0;
        $Contenido::Globals::RPC_TIME  = 0;
    }

    if (ref($r)) {

        my %headers = $r->headers_in();
        my $ip = $headers{'X-Real-IP'} ? $headers{'X-Real-IP'} : $r->connection->remote_ip();
        my @ips = split(/\s*,\s*/, $ip);
        $ip = $ips[ $#ips ];

        $request->set_properties (  
                        'uri'   => $URI||'',
                        'query' => $r->args()||'',
                        'ip'    => $ip||'',
                        'user'  => $r->connection->user()||'',
                        'http_host' => $ENV{HTTP_HOST},
                        'r' => $r,
                     )
    }
    $project->restore($keeper);

    for my $plugin ($state->project, split(/\s+/, $state->plugins)) {
        my $class = $plugin.'::Apache';
        eval { $class->request_init($r); };
        if ( $@ ) {
            $log->error("�� ���� ��������� ����� request_init ������� $plugin ($class) �� ������� '$@'");
        }
    }

    return OK;
}

sub is_valid_request {
    my $r = shift;
    if ($r->uri =~ /^(?:\/i\/|\/images\/|\/binary\/)/ or ($r->content_type && $r->content_type !~ m#(?:^text/|javascript|json|^httpd/unix-directory)#i)) {
        return 0;            
    } else {
        return 1;
    }
} 
#���������� ������� (������������ ����� ������ ������... ��� ��� ����� ��� ������ ��������������� ��� ����� ���������� ����������
sub cleanup {
    my $r = shift;

    #����� ���������� ���������� �������
    $user    = undef;
    $session = undef;

    return Apache::Constants::DECLINED unless Contenido::Apache::is_valid_request($r);

    #��������� ���������� ������ � memcached
    if ($state->{memcached_enable} and $request->{_to_memcache}) {
        while ( my ($key, $values) = each(%{$request->{_to_memcache}}) ) {
            my ($value, $expire, $mode) = @$values;
            if (ref($key) or !$key) { 
                $log->warning("bad key value in set ($key)");
                next;
            }
            $mode ||= 'set';
            if ($mode eq 'add') {
                $keeper->{MEMD}->add($key, $value, $expire);
            } elsif ($mode eq 'delete') {
                $keeper->{MEMD}->delete($key);
            #�� ��������� set
            } else {
                $keeper->{MEMD}->set($key, $value, $expire);
            }
        }
    }

        for my $plugin ($state->project, split(/\s+/, $state->plugins)) {
                my $class = $plugin.'::Apache';
        next unless ($class->can('cleanup'));
                eval { $class->cleanup($r); };
                if ( $@ ) {
                        $log->error("�� ���� ��������� ����� request_init ������� $plugin ($class) �� ������� '$@'");
                }
        }

        # ������������� �� ���� ������� � ��������
        # (���� �� ������������ ���������� �����������)
        # ����� ���������� ������������ �������� �������������� ����������
        unless ($state->db_type eq 'none') {
                $keeper->shutdown unless $state->db_keepalive;
                $keeper->{_connect_ok} = 0;
                $keeper->t_shutdown(1);
        }
        for (split /\s+/, $state->plugins) {
                next unless $keeper->{$_};
                next if $state->{$_}->db_type eq 'none';
                $keeper->{$_}->shutdown unless $state->{$_}->db_keepalive;
                $keeper->{$_}->{_connect_ok} = 0;                    
                $keeper->{$_}->t_shutdown(1);
        }

        if ($DEBUG) {
            my $finish = Time::HiRes::time();
            my $time = int(10000*($finish-$request->{_start}))/10;
            my $db_time = int(10000*($Contenido::Globals::DB_TIME)/10);
            my $core_time = int(10000*($Contenido::Globals::CORE_TIME)/10);
            my $rpc_time = int(10000*($Contenido::Globals::RPC_TIME)/10);
            $log->info("DEBUG: $$ ".__PACKAGE__."  ".scalar(localtime())." ".($r->uri || '').($r->args ? '?'.$r->args : '')." worked $time ms, database time $db_time ms, rpc time $rpc_time ms, core time: $core_time ms, db requests count: $Contenido::Globals::DB_COUNT");
        }

        $request = undef;

    return OK;
}

# ----------------------------------------------------------------------------
# �������, ������� ����������� ��� ��������� ������ ��������� ��������
#  web-�������...
# ----------------------------------------------------------------------------
sub child_exit {
    my $r = shift;

    for my $plugin ($state->project, split(/\s+/, $state->plugins)) {
        my $class = $plugin.'::Apache';
        eval { $class->child_exit($r); };
        if ( $@ ) {
            $log->error("�� ���� ��������� ����� child_exit ������� $plugin ($class) �� ������� '$@'");
        }
    }
    unless ($state->db_type eq 'none') {
        $keeper->shutdown;
    }

    $log->info("������������ ������ ��������� �������� Apache.") if ($DEBUG);

    return OK;
}


# ----------------------------------------------------------------------------
# ������� ��� ��������������.
#   - ���� ��� �������� ��� ������������� ����������, �� ������
#     ������������� ������ ���� ������� 1 (���������)
# ----------------------------------------------------------------------------
sub authentication {
    my $r = shift;

    return Apache::Constants::DECLINED unless Contenido::Apache::is_valid_request($r);
    return FORBIDDEN if $state->db_type eq 'none';

    my ($res, $sent_pw) = $r->get_basic_auth_pw();
    return $res if $res != OK;

    my $username = $r->connection->user();

        $keeper->db_connect unless $keeper->is_connected;
    $user = $keeper->get_user_by_login($username);
        $keeper->shutdown   unless $state->db_keepalive;

    if (ref($user) && ($user->login() eq $username) && ($user->passwd() eq $sent_pw) && $user->passwd() && $user->status == 1) {
        return OK;
    } else {
        $log->warning("������� ����������� � �������� ����� �����/������ ($username, $sent_pw)");
        $r->note_basic_auth_failure();
        return AUTH_REQUIRED;
    }
}

1;