#/*
# *  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: filter.pm 1870 2009-11-07 15:04:39Z hikarin $
#

package Zeromin2::App::filter;

use strict;

sub load_ip {
    my ($zApp) = @_;
    $zApp->privilege('can_enter_control_section')
        or return $zApp->return_value( 1, [] );

    require Zeromin::Filter::IP;
    my $iBBS = $zApp->bbs();
    my $zFIP = Zeromin::Filter::IP->new( $iBBS || $zApp->kernel() );
    $zFIP->load();

    return $zApp->return_value( 0,
        $zFIP->all_in_with_page( 0, $zApp->page_param('filter_ip') ) );
}

sub load_host {
    my ($zApp) = @_;
    $zApp->privilege('can_enter_control_section')
        or return $zApp->return_value( 1, [] );

    require Zeromin::Filter::RemoteHost;
    my $iBBS = $zApp->bbs();
    my $zFRH = Zeromin::Filter::RemoteHost->new( $iBBS || $zApp->kernel() );
    $zFRH->load();

    my $content      = $zFRH->all();
    my $template_tag = [];
    map { push @$template_tag, { item => "$_\n" } } @$content;

    $zApp->add_template_param( { Hosts => $template_tag } );
    return $zApp->return_value( 0, $content );
}

sub load_ngword {
    my ($zApp) = @_;
    $zApp->privilege('can_enter_control_section')
        or return $zApp->return_value( 1, [] );

    require Zeromin::Filter::NGWord;
    my ( $unijp, $encoding ) = $zApp->unijp();
    my $zFNGW
        = Zeromin::Filter::NGWord->new( $zApp->bbs() || $zApp->kernel() );
    $zFNGW->load();
    my @data = $zFNGW->all_in_with_page( $unijp, 0,
        $zApp->page_param('filter_ngword') );
    my $ngwords = $data[0];
    foreach my $ngword (@$ngwords) {
        my $apply = $ngword->{apply};
        $ngword->{"type$ngword->{type}"} = 1;
        map { $apply & $_ and $ngword->{"apply${_}"} = 1 } qw(1 2 4 8 16);
    }

    return $zApp->return_value( 0, @data );
}

sub save_ip {
    my ( $zApp, $level ) = @_;
    $zApp->check_csrf() or return $zApp->return_value(1);
    $zApp->privilege('can_add_access') or return $zApp->return_value(1);

    my ( $args, $bbs, $ip );
    my $iBBS = $zApp->bbs();
    my $bbs_name = $iBBS ? $iBBS->get_name() : '(global)';
    if ($level) {
        my $iLog   = $iBBS->get_log_instance( $zApp->key() );
        my $ip_int = $iLog->get( $zApp->request()->param_int('resno') )->[1]
            || return $zApp->return_value(4);
        $args = $level & 2 ? $zApp->kernel() : $iBBS;
        $bbs  = $iBBS->get_id();
        $ip   = join '.', unpack( 'C*', $ip_int );
    }
    else {
        my $error = {};
        my $param = {};
        _validate_ip_param( $zApp, $param, $error )
            or return $zApp->return_value( $error->{code} );
        $args = $iBBS || $zApp->kernel();
        $bbs  = $param->{bbs};
        $ip   = $param->{ip};
    }

    require Zeromin::Filter::IP;
    my $zFIP = Zeromin::Filter::IP->new($args);
    $zFIP->load();
    if ( $zFIP->get($ip)->{ip} ) {
        $zApp->logger( 0, 'DUPLICATED DENY IP: %s at %s',
            [ $ip, $bbs_name ] );
        return $zApp->return_value(3);
    }
    else {
        $zFIP->add( $ip, $bbs );
        $zFIP->save();
        $zApp->logger( 1, 'CREATE DENY IP: %s at %s', [ $ip, $bbs_name ] );
        return $zApp->return_value(0);
    }
}

sub save_host {
    my ( $zApp, $level ) = @_;
    $zApp->check_csrf()                   or return $zApp->return_value(1);
    $zApp->privilege('can_add_access')    or return $zApp->return_value(1);
    $zApp->privilege('can_remove_access') or return $zApp->return_value(1);

    require Zeromin::Filter::RemoteHost;
    my $zFRH;
    my $iBBS = $zApp->bbs();

    if ($level) {
        $zFRH = Zeromin::Filter::RemoteHost->new(
            ( $level & 2 ? $zApp->kernel() : $iBBS ) );
        $zFRH->load();

        my $duplicate_check_hash = {};
        map { $duplicate_check_hash->{$_} = 1 } @{ $zFRH->all() };

        require Img0ch::Log;
        my $iLog = Img0ch::Log->new( $iBBS, $zApp->key() );
        my $ser = $iLog->get( $zApp->request()->param_int('resno') )->[2]
            || return $zApp->return_value(4);
        $duplicate_check_hash->{$ser} = 1;
        $zFRH->update( [ keys %$duplicate_check_hash ] );
    }
    else {
        my @regexps = split "\n", $zApp->request()->param('content');
        my $update = [];
        $zFRH = Zeromin::Filter::RemoteHost->new( $iBBS || $zApp->kernel() );
        $zFRH->load();
        for my $regexp (@regexps) {
            $regexp =~ s/[\r\s\t]+//gxms;
            $regexp or next;
            push @$update, $regexp;
        }
        $zFRH->update($update);
    }

    $zFRH->save();
    $zApp->logger( 1, 'UPDATED DENIED HOST LIST' );
    return $zApp->return_value(0);
}

sub save_ngword {
    my ($zApp) = @_;
    $zApp->check_csrf() or return $zApp->return_value(1);
    $zApp->privilege('can_edit_ngword')
        or $zApp->privilege('can_add_ngword')
        or return $zApp->return_value(1);

    my $error = {};
    my $param = {};
    my ( $unijp, $encoding ) = $zApp->unijp();
    _validate_ngword_param( $zApp, $param, $error, $unijp, $encoding )
        or return $zApp->return_value( $error->{code} );

    require Zeromin::Filter::NGWord;
    my $iBBS      = $zApp->bbs();
    my $zFNGW     = Zeromin::Filter::NGWord->new( $iBBS || $zApp->kernel() );
    my $id        = $zApp->request()->param('id');
    my $ngword    = $zFNGW->get( { id => $id } );
    my $word      = $param->{word};
    my $meth      = $param->{method};
    my $subs      = $param->{subs};
    my $bbs       = $param->{bbs};
    my $apply     = $param->{apply};
    my $bbs_name  = $iBBS ? $iBBS->get_name() : '(global)';

    if ( $ngword->{id} ) {
        $zApp->privilege('can_edit_ngword') or return $zApp->return_value(1);
        $zFNGW->load();
        $zFNGW->set( $id, $meth, $subs, $bbs_name, $apply );
        $zFNGW->save();
        $zApp->logger(
            1,
            'EDIT NGWORD: %s type is %d and replace to %s at %s',
            [ $word, $meth, $subs, $bbs_name ],
        );
        $zApp->add_template_param( { Done_filter_edit_ngword => 1 } );
        $zApp->return_value(0);
    }
    else {
        $zApp->privilege('can_add_ngword') or return $zApp->return_value(1);
        $zFNGW->load();
        $zFNGW->add( $word, $meth, $subs, $apply );
        $zFNGW->save();
        $zApp->logger(
            1,
            'CREATE NGWORD: %s type is %d and replace to %s at %s',
            [ $word, $meth, $subs, $bbs_name ],
        );
        $zApp->add_template_param( { Done_filter_create_ngword => 1 } );
        $zApp->return_value( 0,
            { id => $zFNGW->get( { word => $word } )->{id} } );
    }
}

sub create_ngword {
    my ($zApp) = @_;
    $zApp->privilege('can_enter_control_section')
        or return $zApp->return_value(1);

    my $iBBS = $zApp->bbs();
    $zApp->add_template_param( { BBS => ( $iBBS ? $iBBS->get_id() : 0 ) } );
    return $zApp->return_value(0);
}

sub edit_ngword {
    my ($zApp) = @_;
    $zApp->privilege('can_edit_ngword')
        or return $zApp->return_value(1);

    require Zeromin::Filter::NGWord;
    my ( $unijp, $encoding ) = $zApp->unijp();
    my $zFNGW
        = Zeromin::Filter::NGWord->new( $zApp->bbs() || $zApp->kernel() );
    $zFNGW->load();
    my $ngword = $zFNGW->get( { id => $zApp->request()->param('id') },
        $unijp, $encoding );

    my %applies;
    my $apply = $ngword->{apply};
    map { $apply & $_ and $applies{"Apply$_"} = 1 } qw(1 2 4 8 16);
    $zApp->add_template_param(
        {   Entry                 => $ngword->{entry},
            Id                    => $ngword->{id},
            To                    => $ngword->{to},
            "Type$ngword->{type}" => 1,
            %applies,
        }
    );
    return $zApp->return_value(0);
}

sub deny_ip_local { save_ip( shift, 1 ) }

sub deny_ip_global { save_ip( shift, 2 ) }

sub deny_serial_local { save_host( shift, 1 ) }

sub deny_serial_global { save_host( shift, 2 ) }

sub remove_ip {
    my ($zApp) = @_;
    $zApp->check_csrf() or return $zApp->return_value(1);
    $zApp->privilege('can_remove_access') or return $zApp->return_value(1);

    require Zeromin::Filter::IP;
    _remove( $zApp,
        Zeromin::Filter::IP->new( $zApp->bbs() || $zApp->kernel() ),
        'DENY IP' );
}

sub remove_ngword {
    my ($zApp) = @_;
    $zApp->check_csrf() or return $zApp->return_value(1);
    $zApp->privilege('can_remove_ngword') or return $zApp->return_value(1);

    require Zeromin::Filter::NGWord;
    _remove( $zApp,
        Zeromin::Filter::NGWord->new( $zApp->bbs() || $zApp->kernel() ),
        'NGWORD' );
}

sub _remove {
    my ( $zApp, $iObject, $type ) = @_;
    my @param = $zApp->request()->param('id');
    scalar @param or return $zApp->return_value(2);

    my $status = {};
    $iObject->load();
    for my $id (@param) {
        if ( $iObject->remove($id) ) {
            $zApp->logger( 1, "REMOVE $type: \%s", [$id] );
            $status->{$id} = 1;
        }
        else {
            $zApp->logger( 0, "TRIED REMOVING INEXIST $type: \%s", [$id] );
            $status->{$id} = 0;
        }
    }
    $iObject->save();

    $zApp->return_value( 0, $status );
}

sub _validate_ip_param {
    my ( $zApp, $param, $error ) = @_;
    my $iRequest = $zApp->request();

    my $ip = $iRequest->param('ip');
    if ( $ip !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/ ) {
        $zApp->logger( 0, 'INVALID IP ADDRESS WAS SET' );
        %{$error} = ( code => 2 );
        return 0;
    }

    $param->{ip}  = $ip;
    $param->{bbs} = Img0ch::Kernel::intval( $iRequest->param('bbs') );
    1;
}

sub _validate_ngword_param {
    my ( $zApp, $param, $error, $unijp, $encoding ) = @_;
    my $iRequest = $zApp->request();
    my $word     = $iRequest->param('entry');

    if ( $word eq '' ) {
        $zApp->logger( 0, 'NO NGWORD WAS SET' );
        %{$error} = ( code => 2 );
        return 0;
    }

    my $meth    = $iRequest->param_int('type');
    my @applies = $iRequest->param('apply');
    my $apply   = 0;
    map { $apply += Img0ch::Kernel::intval($_) } @applies;
    $meth = { '1' => 1, '2' => 2, '4' => 4 }->{$meth} || 1;
    $param->{bbs}    = $iRequest->param_int('bbs');
    $param->{word}   = $word;
    $param->{method} = $meth;
    $param->{subs}   = $iRequest->param('to');
    $param->{apply}  = $apply;
    1;
}

1;
__END__
