App-BPOMUtils-RPO-Checker

 view release on metacpan or  search on metacpan

lib/App/BPOMUtils/RPO/Checker.pm  view on Meta::CPAN


our %SPEC;

our %argspec0plus_filenames = (
    filenames => {
        schema => ['array*', of=>'filename', 'x.perl.default_value_rules' => [['Path::filenames'=>{recurse=>1}]]],
        pos => 0,
        slurpy => 1,
    },
);

$SPEC{':package'} = {
    v => 1.1,
    summary => 'Various checker utilities to help with Processed Food Registration (RPO - Registrasi Pangan Olahan) at BPOM',
};

$SPEC{bpom_rpo_check_files} = {
    v => 1.1,
    summary => 'Check document files',
    description => <<'_',

By default will check all files in the current directory, recursively.

Here's what it checks:
- filename should not contain unsafe symbols
- file must not be larger than 5MB
- file must be readable
- type of file must be PDF or image (JPG), other types will generate warnings
- file's mime type and extension must match

_
    args => {
        %argspec0plus_filenames,
    },
};
sub bpom_rpo_check_files {
    require Cwd;
    require File::Basename;
    require File::MimeInfo::Magic;

    my %args = @_;

    my $i = 0;
    my @errors;
    my @warnings;
    my %symlinks; # key=abspath of symlink target, val=(first) link filename

  FILE:
    for my $filename (@{ $args{filenames} }) {
        $i++;
        log_info "[%d/%d] Processing file %s ...", $i, scalar(@{ $args{filenames} }), $filename
            unless $args{_no_log};
        my ($basename, $dirname, undef) = File::Basename::fileparse($filename);
        unless (-f $filename) {
            push @errors, {file=>$filename, message=>"File not found or not a regular file"};
            next;
        }

      CHECK_FILENAME: {
            if ($basename =~ /\.[^.]+\./) {
                push @errors, {file=>$filename, message=>"Filename contains multiple dots, currently uploadable but not viewable in ereg-rba"};
            }
            if ($basename =~ /[^A-Za-z0-9 _.-]/) {
                push @warnings, {file=>$filename, message=>"Filename contains symbols, should be avoided to ensure viewable in ereg-rba"};
            }
        }

      CHECK_READABILITY: {
            if (!-r($filename)) {
                push @errors, {file=>$filename, message=>"File cannot be read"};
                next FILE;
            }
        }

      CHECK_SYMLINK_TARGET: {
            if (-l $filename) {
                my $abs_target = File::Spec->rel2abs(readlink($filename), $dirname);
                log_trace "Symlink target (absolute): %s", $abs_target;
                unless ($abs_target) {
                    push @errors, {file=>$filename, message=>"Symlink target cannot be made absolute"}; # should not happen if we already check -f $file
                    next FILE;
                }
                if (defined $symlinks{$abs_target}) {
                    push @warnings, {file=>$filename, message=>"WARNING: Targets to the same file ($abs_target) as link $symlinks{$abs_target}, probably not what we want"}; # should not happen if we already check -f $file
                    next FILE;
                } else {
                    $symlinks{$abs_target} = $filename;
                }
            }
        } # CHECK_SYMLINK_TARGET

      CHECK_SIZE: {
            my $filesize = -s $filename;
            if ($filesize > 5*1024*1024) {
                push @errors, {file=>$filename, message=>"File size too large (>5M)"};
            }
        } # CHECK_SIZE

      CHECK_TYPE_AND_EXTENSION: {
            # because File::MimeInfo::Magic will report mime='inode/symlink' for symlink
            my $realfile = -l $filename ? readlink($filename) : $filename;
            my $mime_type = File::MimeInfo::Magic::mimetype($realfile);
            if ($mime_type eq 'image/jpeg') {
                push @errors, {file=>$filename, message=>"File type is JPEG but extension is not jpg/jpeg"}
                    unless $filename =~ /\.(jpe?g)$/i;
            } elsif ($mime_type eq 'image/gif') {
                push @errors, {file=>$filename, message=>"File type is GIF but extension is not gif"}
            } elsif ($mime_type eq 'image/x-ms-bmp') {
                push @errors, {file=>$filename, message=>"File type is BMP but extension is not bmp"}
                    unless $filename =~ /\.(bmp)$/i;
            } elsif ($mime_type eq 'application/pdf') {
                push @errors, {file=>$filename, message=>"File type is PDF but extension is not pdf"}
                    unless $filename =~ /\.(pdf)$/i;
            } else {
                push @errors, {file=>$filename, message=>"File type is not JPEG/GIF/BMP/PDF"};
            }

        } # CHECK_TYPE_AND_EXTENSION

    }



( run in 1.277 second using v1.01-cache-2.11-cpan-b16cb0d3907 )