#/*
# *  Copyright 2007 hkrn <hikarin@users.sourceforge.jp>
# *
# *  Licensed under the Apache License, Version 2.0 (the "License");
# *  you may not use this file except in compliance with the License.
# *  You may obtain a copy of the License at
# *
# *      http://www.apache.org/licenses/LICENSE-2.0
# *
# *  Unless required by applicable law or agreed to in writing, software
# *  distributed under the License is distributed on an "AS IS" BASIS,
# *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# *  See the License for the specific language governing permissions and
# *  limitations under the License.
# */
#
# $Id: Res.pm 610 2007-05-09 14:49:15Z hikarin $
#

package Img0ch::Filter::Res;

use strict;

sub write_ok {
    my ( $iClass, $hash, $iKernel ) = @_;
    my $now = $hash->{stamp};
    my $len = $hash->{len};
    my $ip  = $hash->{ip};
    my $ser = $hash->{serial};
    my $term
        = $ser
        ? $hash->{serial}
        : join( '.', unpack( 'C*', $hash->{ip} ) );

    my $repos_path  = $iKernel->get_repos_path('log');
    my $iRepos      = $iKernel->get_repos($repos_path);
    my $max         = $iRepos->get_int('I:F:R.max');
    my $min         = $iRepos->get_int('I:F:R.min');
    my $samba_close = $hash->{samba_close} || 0;
    my $my_stack    = [];
    my $other_stack = [];
    my $atime       = 0;
    my $is_samba    = 0;
    my $latest_len  = 0;
    my $samba_count = 0;

    for ( my $i = $min; $i <= $max; $i++ ) {
        my $id = $iRepos->get("I:F:R.${i}.id") || next;
        if ( $id eq $term ) {
            $atime      = $iRepos->get_int("I:F:R.${i}.time");
            $is_samba   = $iRepos->get_int("I:F:R.${i}.samba");
            $latest_len = $iRepos->get_int("I:F:R.${i}.len");
            $samba_close >= $now - $atime and $samba_count++;
            push @{$my_stack}, $i;
        }
        else {
            push @{$other_stack}, $i;
        }
    }

    my $my_count  = @{$my_stack};
    my $can_store = $hash->{store};
    if ( $can_store > 0 and $my_count == 0 ) {
        $iRepos->set( 'I:F:R.min', 0 );
        _write( $iRepos, $max, $now, $term, $len, 0 );
        return 1;
    }

    if ( $my_count >= $can_store ) {
        _remove( $iRepos, $my_stack, $other_stack );
        return 1;
    }

    if ( !$hash->{samba_ok} ) {
        if ($is_samba) {
            my $wait = $hash->{samba_wait};
            if ( $now >= $atime + $wait ) {
                _remove( $iRepos, $my_stack, $other_stack );
                return 1;
            }
            else {
                $hash->{samba_rest}
                    = int( ( $atime - ( $now - $wait ) ) / 60 ) || 1;
                $hash->{error} = 'DENY_FROM_SAMBA_LISTED_HOST';
                return 0;
            }
        }
    }

    if ( !$hash->{duplicate_ok} ) {
        if ( $latest_len == $len ) {
            $hash->{error} = 'DOUPLICATED_POST';
            return 0;
        }
    }

    if ( !$hash->{throttle_ok} ) {
        my $timecount = $hash->{timecount};
        my $throttle = $my_count > $timecount ? $timecount : $my_count;
        if ( $throttle >= $hash->{timeclose} ) {
            $hash->{error} = 'THROTTLED_POST';
            return 0;
        }
    }

    if ( !$hash->{samba_ok} ) {
        if ( $samba_count > 0 ) {
            $hash->{samba_count} = $samba_count;
            $hash->{samba_rest}  = $now - $atime;
            my $mark = 0;
            if ( $samba_count < 4 ) {
                $hash->{error} = 'SAMBA_LEVEL1';
            }
            elsif ( $samba_count == 4 ) {
                $hash->{error} = 'SAMBA_LEVEL2';
            }
            else {
                $mark = 1;
                $hash->{error} = 'SAMBA_LEVEL3';
            }
            _write( $iRepos, $max, $now, $term, $len, $mark );
            return 0;
        }
    }

    _write( $iRepos, $max, $now, $term, $len, 0 );
    return 1;
}

sub _write {
    my ( $iRepos, $max, $now, $term, $len, $samba_mark ) = @_;
    $iRepos->set( 'I:F:R.max',          ++$max );
    $iRepos->set( "I:F:R.${max}.id",    $term );
    $iRepos->set( "I:F:R.${max}.len",   $len );
    $iRepos->set( "I:F:R.${max}.samba", $samba_mark );
    $iRepos->set( "I:F:R.${max}.time",  $now );
    $iRepos->save();
}

sub _remove {
    my ( $iRepos, $my_stack, $other_stack ) = @_;
    $iRepos->set( 'I:F:R.min', $other_stack->[0] || 0 );
    @$my_stack == 0 and @$other_stack == 0 and $iRepos->set( 'I:F:R.max', 0 );
    for my $i ( @${my_stack} ) {
        $iRepos->remove("I:F:R.${i}.id");
        $iRepos->remove("I:F:R.${i}.len");
        $iRepos->remove("I:F:R.${i}.samba");
        $iRepos->remove("I:F:R.${i}.time");
    }
    $iRepos->save();
}

1;
__END__
