mirror of
https://github.com/dave-theunsub/clamtk.git
synced 2026-01-02 19:58:01 -05:00
815 lines
23 KiB
Perl
815 lines
23 KiB
Perl
# ClamTk, copyright (C) 2004-2021 Dave M
|
|
#
|
|
# This file is part of ClamTk
|
|
# (https://gitlab.com/dave_m/clamtk/).
|
|
#
|
|
# ClamTk is free software; you can redistribute it and/or modify it
|
|
# under the terms of either:
|
|
#
|
|
# a) the GNU General Public License as published by the Free Software
|
|
# Foundation; either version 1, or (at your option) any later version, or
|
|
#
|
|
# b) the "Artistic License".
|
|
package ClamTk::Scan;
|
|
|
|
use Glib 'TRUE', 'FALSE';
|
|
use File::Find;
|
|
use Cwd 'chdir';
|
|
|
|
# use strict;
|
|
# use warnings;
|
|
$| = 1;
|
|
|
|
use constant HATE_GNOME_SHELL => -6;
|
|
use constant DESTROY_GNOME_SHELL => -11;
|
|
|
|
use POSIX 'locale_h', 'strftime';
|
|
use File::Basename 'basename', 'dirname', 'fileparse';
|
|
use Locale::gettext;
|
|
use Encode 'decode';
|
|
|
|
binmode( STDIN, ':utf8' );
|
|
binmode( STDOUT, ':utf8' );
|
|
|
|
my $SCAN; # File handle for scanning
|
|
my $found; # Holds information of bad stuff found
|
|
|
|
my $found_count = 0; # Scalar number of bad stuff found
|
|
my $num_scanned = 0; # Overall number of files scanned
|
|
my %dirs_scanned; # Directories scanned
|
|
|
|
my $hb; # HeaderBar
|
|
my $pb; # ProgressBar
|
|
my $pb_file_counter = 0; # For ProgressBar
|
|
my $pb_step; # For ProgressBar
|
|
my $root_scan = FALSE; # Scanning /
|
|
my $scan_pid; # PID of scanner, for killing/cancelling scan
|
|
|
|
my $stopped = 1; # Whether scanner is stopped (1) or running (0)
|
|
my $directive; # Options sent to scanner
|
|
my $topbar; # InfoBar on top
|
|
my $bottombar; # InfoBar on bottom
|
|
my $files_scanned_label; # Label
|
|
my $threats_label; # Label
|
|
my $show; # Whether or not to show the preferences button
|
|
my $window; # Main window/dialog
|
|
my $from_cli; # from the commandline?
|
|
|
|
sub filter {
|
|
# $pkg_name = drop this
|
|
# $scanthis = file or directory to be scanned
|
|
# $show = whether or not to show the preferences button;
|
|
# we do if it's a commandline or right-click scan; otherwise we don't
|
|
# $from = from the commandline?
|
|
my ( $pkg_name, $scanthis, $show, $from ) = @_;
|
|
$from_cli = $from;
|
|
|
|
# Currently just to test permissions:
|
|
# If given a file/directory from the commandline
|
|
# AND we can't scan it, just die.
|
|
# However, if interface is running
|
|
# AND we can't scan it, just return to interface.
|
|
if ( !sanity_check( $scanthis ) ) {
|
|
if ( $from && $from eq 'startup' ) {
|
|
Gtk3->main_quit;
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
# We're gonna need these:
|
|
my $paths = ClamTk::App->get_path( 'all' );
|
|
my %prefs = ClamTk::Prefs->get_all_prefs();
|
|
|
|
# Don't bother doing anything if clamscan can't be found
|
|
if ( !-e $paths->{ clampath } ) {
|
|
warn "Cannot scan without clamscan!\n";
|
|
return;
|
|
}
|
|
|
|
# Begin popup scanning
|
|
$window
|
|
= Gtk3::Dialog->new( undef, undef,
|
|
[ qw| modal destroy-with-parent use-header-bar | ],
|
|
);
|
|
$window->set_deletable( FALSE );
|
|
$window->set_default_size( 450, 80 );
|
|
|
|
$hb = Gtk3::HeaderBar->new;
|
|
$window->set_titlebar( $hb );
|
|
$hb->set_title( _( 'Please wait...' ) );
|
|
$hb->set_show_close_button( TRUE );
|
|
$hb->set_decoration_layout( 'menu:close' );
|
|
|
|
$window->signal_connect(
|
|
'destroy' => sub {
|
|
if ( !$stopped ) {
|
|
return TRUE;
|
|
} else {
|
|
$window->destroy;
|
|
1;
|
|
}
|
|
}
|
|
);
|
|
$window->set_border_width( 5 );
|
|
$window->set_position( 'center-on-parent' );
|
|
|
|
my $images_dir = ClamTk::App->get_path( 'images' );
|
|
if ( -e "$images_dir/clamtk.png" ) {
|
|
my $pixbuf
|
|
= Gtk3::Gdk::Pixbuf->new_from_file( "$images_dir/clamtk.png" );
|
|
my $transparent = $pixbuf->add_alpha( TRUE, 0xff, 0xff, 0xff );
|
|
$window->set_icon( $transparent );
|
|
}
|
|
|
|
my $eb = Gtk3::EventBox->new;
|
|
$window->get_content_area->add( $eb );
|
|
|
|
my $box = Gtk3::VBox->new( FALSE, 5 );
|
|
$eb->add( $box );
|
|
|
|
my $hbox = Gtk3::HBox->new( FALSE, 0 );
|
|
$box->add( $hbox );
|
|
|
|
$topbar = Gtk3::InfoBar->new;
|
|
$hbox->pack_start( $topbar, TRUE, TRUE, 5 );
|
|
|
|
Gtk3::main_iteration while Gtk3::events_pending;
|
|
$topbar->set_message_type( 'other' );
|
|
set_infobar_text( $topbar, _( 'Preparing...' ) );
|
|
Gtk3::main_iteration while Gtk3::events_pending;
|
|
|
|
$pb = Gtk3::ProgressBar->new;
|
|
$box->pack_start( $pb, FALSE, FALSE, 5 );
|
|
$window->{ pb } = $pb;
|
|
|
|
# reset numbers
|
|
reset_stats();
|
|
|
|
$files_scanned_label
|
|
= Gtk3::Label->new( sprintf _( "Files scanned: %d" ), $num_scanned );
|
|
$files_scanned_label->set_alignment( 0.0, 0.5 );
|
|
|
|
$threats_label
|
|
= Gtk3::Label->new( sprintf _( "Possible threats: %d" ),
|
|
$found_count );
|
|
$threats_label->set_alignment( 0.0, 0.5 );
|
|
|
|
my $text_box = Gtk3::VBox->new( FALSE, 5 );
|
|
$text_box->add( $files_scanned_label );
|
|
$text_box->add( $threats_label );
|
|
|
|
$bottombar = Gtk3::InfoBar->new;
|
|
$box->pack_start( $bottombar, FALSE, FALSE, 5 );
|
|
|
|
$bottombar->set_message_type( 'other' );
|
|
# Stupid infobars
|
|
# $use_image = ClamTk::Icons->get_image( 'gtk-cancel' );
|
|
$use_image = 'gtk-cancel';
|
|
$bottombar->add_button( $use_image, HATE_GNOME_SHELL );
|
|
if ( $show ) {
|
|
# $use_image = ClamTk::Icons->get_image( 'preferences-system' );
|
|
$use_image = ClamTk::Icons->get_image( 'gtk-preferences' );
|
|
$bottombar->add_button( $use_image, DESTROY_GNOME_SHELL );
|
|
}
|
|
$bottombar->signal_connect(
|
|
response => sub {
|
|
my ( $bar, $button ) = @_;
|
|
if ( $button eq 'cancel' ) {
|
|
cancel_scan();
|
|
} elsif ( $button eq 'help' ) {
|
|
system( 'clamtk &' );
|
|
return FALSE;
|
|
}
|
|
}
|
|
);
|
|
$bottombar->get_content_area->add( $text_box );
|
|
|
|
$window->show_all;
|
|
$window->set_gravity( 'south-east' );
|
|
$window->queue_draw;
|
|
$window->set_position( 'mouse' );
|
|
Gtk3::main_iteration while Gtk3::events_pending;
|
|
|
|
# Try to avoid MS Windows file systems...
|
|
# This fubars Live ISOs: see
|
|
# https://github.com/dave-theunsub/clamtk/issues/67
|
|
# $directive .= ' --cross-fs=no';
|
|
|
|
# Try to avoid scanning emails...
|
|
$directive .= ' --scan-mail=no';
|
|
|
|
# I didn't know we had to explicitly state this.
|
|
# https://github.com/dave-theunsub/clamtk/issues/59
|
|
$directive .= ' --scan-archive=yes';
|
|
|
|
# Increase maximum amount of data to scan for each container file;
|
|
# goes hand in hand with the $directive above
|
|
# https://github.com/dave-theunsub/clamtk/issues/59
|
|
$directive .= ' --max-scansize=500M';
|
|
|
|
# By default, we ignore .gvfs directories.
|
|
# Once we figure out KDE's process, we'll exclude that too.
|
|
#<<<
|
|
for my $m (
|
|
'smb4k',
|
|
"/run/user/$ENV{USER}/gvfs",
|
|
"$ENV{HOME}/.gvfs" ) {
|
|
$directive .= " --exclude-dir=$m";
|
|
}
|
|
#>>>
|
|
|
|
# Now strip whitelisted directories
|
|
for my $ignore (
|
|
split(
|
|
/;/,
|
|
ClamTk::Prefs->get_preference( 'Whitelist' )
|
|
. $paths->{ whitelist_dir }
|
|
)
|
|
)
|
|
{
|
|
# warn "excluding $ignore\n";
|
|
# --exclude-dir=REGEX Don't scan directories matching REGEX
|
|
# Using REGEX is important because users could have some
|
|
# of the whitelisted domains as part of a directory that
|
|
# should be scanned.
|
|
# Github #61 - https://github.com/dave-theunsub/clamtk/issues/61
|
|
$directive .= " --exclude-dir=^" . quotemeta( $ignore );
|
|
}
|
|
|
|
# Remove mail directories and some backup directories for now.
|
|
# Not all of these can be appended to $HOME for a more
|
|
# specific path - kmail (e.g.) is somewhere
|
|
# under $HOME/.kde/blah/foo/...
|
|
my @maildirs = qw(
|
|
.thunderbird .mozilla-thunderbird
|
|
Mail kmail evolution timeshift
|
|
);
|
|
for my $mailbox ( @maildirs ) {
|
|
# warn "excluding mailbox directory $mailbox\n";
|
|
$directive .= " --exclude-dir=$mailbox";
|
|
}
|
|
|
|
# remove the hidden files if chosen:
|
|
if ( !$prefs{ ScanHidden } ) {
|
|
# But only if Trash directory is not being scanned
|
|
if ( $scanthis !~ m#/.local/share/Trash# ) {
|
|
$directive .= ' --exclude="\/\."';
|
|
}
|
|
}
|
|
|
|
# symlinks:
|
|
# The symlink stuff from clamscan requires >= 0.97.
|
|
my ( $version ) = ClamTk::App->get_AV_version();
|
|
# Ensure it's just digits and dots:
|
|
$version =~ s/[^0-9\.]//g;
|
|
$version =~ s/^[0\.]+//;
|
|
$version =~ s/\.0$//;
|
|
warn "clamscan version = >$version<\n";
|
|
if ( ( $version <=> '97' ) == 0
|
|
|| ( $version <=> '97' ) == 1 )
|
|
{
|
|
$directive .= ' --follow-dir-symlinks=1';
|
|
$directive .= ' --follow-file-symlinks=1';
|
|
}
|
|
# This feature mitigates the risk of malformed media files intended
|
|
# to exploit vulnerabilities in other software. At present, media
|
|
# validation exists for JPEG, TIFF, PNG and GIF files.
|
|
if ( $version >= '103.1' ) {
|
|
$directive .= ' --alert-broken-media=yes';
|
|
}
|
|
|
|
# we'll count this as ! $stopped
|
|
#$stopped = 0;
|
|
|
|
# reset %$found
|
|
$found = {};
|
|
|
|
# These lines are for 'thorough'. :)
|
|
# If it's selected, we add detection for both
|
|
# potentially unwanted applications and broken executables.
|
|
# Version 0.101.0 changed and added some options.
|
|
if ( $prefs{ Thorough } ) {
|
|
if ( ( $version <=> '101' ) == 0
|
|
|| ( $version <=> '101' ) == 1 )
|
|
{
|
|
$directive .= ' --detect-pua --alert-broken' . ' --alert-macros';
|
|
} else {
|
|
$directive .= ' --detect-pua --algorithmic-detection';
|
|
}
|
|
} else {
|
|
if ( ( $version cmp '101' ) == 0
|
|
|| ( $version cmp '101' ) == 1 )
|
|
{
|
|
$directive =~ s/\s--detect-pua --alert-broken --alert-macros'//;
|
|
} else {
|
|
$directive =~ s/\s--detect-pua --algorithmic-detection//;
|
|
}
|
|
}
|
|
|
|
# Heuristic scanning
|
|
if ( $prefs{ Heuristic } ) {
|
|
# By default, if included, == yes
|
|
$directive .= ' --heuristic-alerts=yes';
|
|
} else {
|
|
$directive .= ' --heuristic-alerts=no';
|
|
}
|
|
|
|
# only a single file
|
|
|
|
# By default, 20Mb is the largest we go -
|
|
# unless the preference is to ignore size.
|
|
if ( !$prefs{ SizeLimit } ) {
|
|
$directive .= ' --max-filesize=20M';
|
|
}
|
|
|
|
if ( !$prefs{ Recursive } ) {
|
|
$directive .= ' --max-dir-recursion=1';
|
|
} else {
|
|
$directive .= ' --recursive=yes';
|
|
}
|
|
|
|
scan( $scanthis, $directive );
|
|
|
|
clean_up();
|
|
}
|
|
|
|
sub scan {
|
|
my ( $path_to_scan, $directive ) = @_;
|
|
chomp( $path_to_scan );
|
|
chomp( $directive );
|
|
|
|
$pb_step = get_step( $path_to_scan );
|
|
if ( $pb_step ) {
|
|
$pb->set_pulse_step( $pb_step );
|
|
} else {
|
|
$pb->{ timer } = Glib::Timeout->add( 200, \&progress_timeout, $pb );
|
|
$root_scan = TRUE;
|
|
}
|
|
|
|
$pb->show;
|
|
|
|
my $quoted = quotemeta( $path_to_scan );
|
|
chomp( $quoted );
|
|
|
|
# Leave if we have no real path
|
|
if ( !$path_to_scan ) {
|
|
warn "No path to scan!\n";
|
|
return;
|
|
}
|
|
|
|
my $paths = ClamTk::App->get_path( 'all' );
|
|
my $command = $paths->{ clamscan };
|
|
|
|
# Use the user's sig db if it's selected
|
|
if ( ClamTk::Prefs->get_preference( 'Update' ) eq 'single' ) {
|
|
$command .= " --database=$paths->{db}";
|
|
}
|
|
|
|
# Implicit fork; gives us the PID of clamscan so we can
|
|
# kill it if the user hits the Stop button
|
|
#<<<
|
|
# Using the verbose (-v) switch gives us the next file
|
|
# for the display "Scanning $1..."
|
|
$scan_pid
|
|
= open( $SCAN, '-|', "$command $directive -v $quoted 2>&1" );
|
|
defined( $scan_pid ) or die "couldn't fork: $!\n";
|
|
$window->queue_draw;
|
|
Gtk3::main_iteration while Gtk3::events_pending;
|
|
#>>>
|
|
# binmode( $SCAN, ':utf8:bytes' );
|
|
|
|
Gtk3::main_iteration while Gtk3::events_pending;
|
|
while ( <$SCAN> ) {
|
|
chomp;
|
|
$window->queue_draw;
|
|
Gtk3::main_iteration while Gtk3::events_pending;
|
|
|
|
# Warning stuff we don't need
|
|
next if ( /^LibClamAV/ );
|
|
next if ( /^\s*$/ );
|
|
|
|
if ( /^Scanning (.*?)$/ ) {
|
|
my $base = decode( 'UTF-8', basename( $1 ) );
|
|
# Display stuff in popup infobar
|
|
set_infobar_text(
|
|
$topbar,
|
|
# sprintf( _( 'Scanning %s...' ), $dirname )
|
|
# sprintf( _( 'Scanning %s...' ), $dirname )
|
|
sprintf( _( 'Scanning %s...' ), $base )
|
|
);
|
|
$num_scanned++;
|
|
if ( !$root_scan ) {
|
|
my $pb_current = $pb->get_fraction;
|
|
|
|
if ( $pb_current + $pb_step <= 0
|
|
|| $pb_current + $pb_step >= 1.0 )
|
|
{
|
|
$pb_current = .99;
|
|
} else {
|
|
$pb_current += $pb_step;
|
|
}
|
|
$pb->set_fraction( $pb_current );
|
|
}
|
|
|
|
Gtk3::main_iteration while Gtk3::events_pending;
|
|
$files_scanned_label->set_text( sprintf _( "Files scanned: %d" ),
|
|
$num_scanned );
|
|
$topbar->show_all;
|
|
Gtk3::main_iteration while Gtk3::events_pending;
|
|
$window->queue_draw;
|
|
next;
|
|
}
|
|
|
|
my ( $file, $status );
|
|
if ( /(.*?): ([^:]+) FOUND/ ) {
|
|
$file = $1;
|
|
$status = $2;
|
|
}
|
|
|
|
# Ensure the file is still there (things get moved)
|
|
# and that it got scanned
|
|
next unless ( $file && -e $file && $status );
|
|
next if ( $status =~ /module failure/ );
|
|
|
|
chomp( $file ) if ( defined $file );
|
|
chomp( $status ) if ( defined $status );
|
|
|
|
my $dirname = decode( 'UTF-8', dirname( $file ) );
|
|
my $fileparse = decode( 'UTF-8', fileparse( $file ) );
|
|
|
|
my $hidden = ClamTk::Prefs->get_preference( 'ScanHidden' );
|
|
next if ( !$hidden && basename( $fileparse ) =~ /^\./ );
|
|
|
|
my $dirparse;
|
|
if ( length( $dirname ) > 35 ) {
|
|
$dirparse = substr $dirname, 0, 35;
|
|
} else {
|
|
$dirparse = $dirname;
|
|
}
|
|
|
|
# Lots of temporary things under /tmp/clamav;
|
|
# we'll just ignore them.
|
|
$dirs_scanned{ $dirname } = 1
|
|
unless ( dirname( $file ) =~ /\/tmp\/clamav/
|
|
|| dirname( $file ) eq '.' );
|
|
|
|
# Do not show files in archives - we just want the end-result.
|
|
# It still scans and we still show the result.
|
|
next if ( $file =~ /\/tmp\/clamav/ );
|
|
|
|
# $status is the "virus" name.
|
|
$status =~ s/\s+FOUND$//;
|
|
|
|
# These aren't necessarily clean (despite the variable's name)
|
|
# - we just don't want them counted as viruses
|
|
my $clean_words = join( '|',
|
|
'OK', 'Zip module failure',
|
|
"RAR module failure", 'Encrypted.PDF',
|
|
'Encrypted.RAR', 'Encrypted.Zip',
|
|
'Empty file', 'Excluded',
|
|
'Input/Output error', 'Files number limit exceeded',
|
|
'handler error', 'Broken.Executable',
|
|
'Oversized.Zip', 'Symbolic link' );
|
|
|
|
if ( $status !~ /$clean_words/ ) { # a virus
|
|
$found->{ $found_count }->{ name } = $file;
|
|
$found->{ $found_count }->{ status } = $status;
|
|
$found->{ $found_count }->{ action } = _( 'None' );
|
|
$found_count++;
|
|
$threats_label->set_text( sprintf _( "Possible threats: %d" ),
|
|
$found_count );
|
|
}
|
|
|
|
}
|
|
|
|
Gtk3::main_iteration while Gtk3::events_pending;
|
|
|
|
# Done scanning - close filehandle and return to
|
|
# filter() and then to clean-up
|
|
close( $SCAN ); # or warn "Unable to close scanner! $!\n";
|
|
}
|
|
|
|
sub cancel_scan {
|
|
kill 15, $scan_pid + 1;
|
|
waitpid( $scan_pid + 1, 0 ) if ( $scan_pid + 1 );
|
|
kill 15, $scan_pid if ( $scan_pid );
|
|
waitpid( $scan_pid, 0 ) if ( $scan_pid );
|
|
|
|
close( $SCAN );
|
|
$stopped = 1;
|
|
}
|
|
|
|
sub clean_up {
|
|
set_infobar_text( $topbar, _( 'Cleaning up...' ) );
|
|
$pb->set_fraction( 1.00 );
|
|
destroy_progress();
|
|
|
|
destroy_buttons();
|
|
add_closing_buttons();
|
|
|
|
$hb->set_title( _( 'Complete' ) );
|
|
my $message = '';
|
|
if ( !$found_count ) {
|
|
$message = _( 'Scanning complete' );
|
|
} else {
|
|
$message = _( 'Possible threats found' );
|
|
}
|
|
set_infobar_text( $topbar, $message );
|
|
|
|
# Save scan information
|
|
logit();
|
|
|
|
if ( $found_count ) {
|
|
ClamTk::Results->show_window( $found, $window );
|
|
if ( $from_cli ) {
|
|
Gtk3->main_quit;
|
|
}
|
|
} else {
|
|
bad_popup();
|
|
}
|
|
|
|
# reset numbers
|
|
reset_stats();
|
|
}
|
|
|
|
sub destroy_progress {
|
|
Glib::Source->remove( $window->{ pb }->{ timer } )
|
|
if ( $root_scan );
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
sub reset_stats {
|
|
# reset things
|
|
$num_scanned = 0;
|
|
$found_count = 0;
|
|
$pb_file_counter = 0;
|
|
$pb_step = 0;
|
|
%dirs_scanned = ();
|
|
$stopped = 1;
|
|
$directive = '';
|
|
$root_scan = FALSE;
|
|
}
|
|
|
|
sub bad_popup {
|
|
my $dialog = Gtk3::MessageDialog->new(
|
|
$window, [ qw| modal destroy-with-parent | ],
|
|
'info', 'close', _( 'No threats found' ),
|
|
);
|
|
$dialog->run;
|
|
$dialog->destroy;
|
|
}
|
|
|
|
sub logit {
|
|
my $db_total = ClamTk::App->get_sigtool_info( 'count' );
|
|
# warn "in Scan: db_total = >$db_total<\n";
|
|
my $REPORT; # filehandle for histories log
|
|
|
|
#<<<
|
|
my ( $mon, $day, $year )
|
|
= split / /, strftime( '%b %d %Y', localtime );
|
|
|
|
# Save date of scan
|
|
if ( $found_count > 0 ) {
|
|
ClamTk::Prefs->set_preference(
|
|
'LastInfection', "$day $mon $year"
|
|
);
|
|
}
|
|
#>>>
|
|
|
|
my %prefs = ClamTk::Prefs->get_all_prefs();
|
|
my $paths = ClamTk::App->get_path( 'history' );
|
|
|
|
my $virus_log
|
|
= $paths . "/" . decode( 'UTF-8', "$mon-$day-$year" ) . '.log';
|
|
|
|
#<<<
|
|
# sort the directories scanned for display
|
|
my @sorted = sort { $a cmp $b } keys %dirs_scanned;
|
|
if ( open $REPORT, '>>:encoding(UTF-8)', $virus_log ) {
|
|
print $REPORT "\nClamTk, v",
|
|
ClamTk::App->get_TK_version(), "\n",
|
|
scalar localtime,
|
|
"\n";
|
|
print $REPORT sprintf _(
|
|
"ClamAV Signatures: %d\n" ),
|
|
$db_total;
|
|
print $REPORT _( "Directories Scanned:\n" );
|
|
for my $list ( @sorted ) {
|
|
print $REPORT "$list\n";
|
|
}
|
|
printf $REPORT _(
|
|
"\nFound %d possible %s (%d %s scanned).\n\n" ),
|
|
$found_count,
|
|
$found_count == 1 ? _( 'threat' ) : _( 'threats' ),
|
|
$num_scanned,
|
|
$num_scanned == 1 ? _( 'file' ) : _( 'files' );
|
|
} else {
|
|
warn "Could not write to logfile. Check permissions.\n";
|
|
}
|
|
#>>>
|
|
|
|
# Set the minimum sizes for the two columns,
|
|
# the filename and its status - if we're saving a log (which we
|
|
# do, by default)
|
|
my $lsize = 20;
|
|
my $rsize = 20;
|
|
if ( $found_count == 0 ) {
|
|
print $REPORT _( "No threats found.\n" );
|
|
} else {
|
|
# Now get the longest lengths of the column contents.
|
|
for my $length ( sort keys %$found ) {
|
|
$lsize
|
|
= ( length( $found->{ $length }->{ name } ) > $lsize )
|
|
? length( $found->{ $length }->{ name } )
|
|
: $lsize;
|
|
$rsize
|
|
= ( length( $found->{ $length }->{ status } ) > $rsize )
|
|
? length( $found->{ $length }->{ status } )
|
|
: $rsize;
|
|
}
|
|
# Set a buffer which is probably unnecessary.
|
|
$lsize += 5;
|
|
$rsize += 5;
|
|
# Print to the log:
|
|
for my $num ( sort keys %$found ) {
|
|
printf $REPORT "%-${lsize}s %-${rsize}s\n",
|
|
decode( 'utf8', $found->{ $num }->{ name } ),
|
|
$found->{ $num }->{ status };
|
|
}
|
|
}
|
|
|
|
print $REPORT '-' x ( $lsize + $rsize + 5 ), "\n";
|
|
close( $REPORT );
|
|
|
|
return;
|
|
}
|
|
|
|
sub set_infobar_text {
|
|
my ( $bar, $text ) = @_;
|
|
|
|
Gtk3::main_iteration while Gtk3::events_pending;
|
|
for my $c ( $bar->get_content_area->get_children ) {
|
|
if ( $c->isa( 'Gtk3::Label' ) ) {
|
|
$c->set_text( $text );
|
|
Gtk3::main_iteration while Gtk3::events_pending;
|
|
return;
|
|
}
|
|
}
|
|
|
|
#<<<
|
|
my $label = Gtk3::Label->new;
|
|
$label->set_text( _( $text ) );
|
|
$label->set_alignment( 0.0, 0.5 );
|
|
$label->set_ellipsize( 'middle' );
|
|
$bar->get_content_area->add(
|
|
#Gtk3::Label->new( _( $text ) )
|
|
$label
|
|
);
|
|
#>>>
|
|
$window->queue_draw;
|
|
Gtk3::main_iteration while Gtk3::events_pending;
|
|
}
|
|
|
|
sub add_default_buttons {
|
|
# We're going to show the following:
|
|
# cancel-button: obviously cancels the scan
|
|
# prefs-button: allows popup 'clamtk' with no args, for settings.
|
|
# We don't even need translations for this.
|
|
$bottombar->add_button( 'gtk-cancel', HATE_GNOME_SHELL, );
|
|
$bottombar->add_button( 'gtk-preferences', DESTROY_GNOME_SHELL, );
|
|
|
|
$bottombar->signal_connect(
|
|
response => sub {
|
|
my ( $bar, $response ) = @_;
|
|
if ( $response eq 'cancel' ) {
|
|
cancel_scan();
|
|
return TRUE;
|
|
} elsif ( $response eq 'help' ) {
|
|
system( 'clamtk &' );
|
|
return FALSE;
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
sub add_closing_buttons {
|
|
$bottombar->add_button( 'gtk-close', -7 );
|
|
if ( $show ) {
|
|
$bottombar->add_button( 'gtk-preferences', DESTROY_GNOME_SHELL, );
|
|
}
|
|
|
|
$bottombar->signal_connect(
|
|
response => sub {
|
|
my ( $bar, $response ) = @_;
|
|
if ( $response eq 'close' ) {
|
|
$window->destroy;
|
|
} elsif ( $response eq 'help' ) {
|
|
system( 'clamtk &' );
|
|
return FALSE;
|
|
}
|
|
}
|
|
);
|
|
$bottombar->show_all;
|
|
}
|
|
|
|
sub destroy_buttons {
|
|
for my $c ( $bottombar->get_action_area->get_children ) {
|
|
if ( $c->isa( 'Gtk3::Button' ) ) {
|
|
$c->destroy;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
sub get_step {
|
|
my $dir = shift;
|
|
|
|
my $recur = ClamTk::Prefs->get_preference( 'Recursive' );
|
|
|
|
return if ( $dir eq '/' );
|
|
|
|
if ( $recur ) {
|
|
find( \&wanted, $dir );
|
|
} else {
|
|
find( { wanted => \&wanted, preprocess => \&nodirs }, $dir );
|
|
}
|
|
|
|
#find( { \&wanted, no_chdir => ( $recur ) ? 0 : 1 }, $dir );
|
|
|
|
if ( !$pb_file_counter ) {
|
|
return;
|
|
}
|
|
|
|
return 1 / $pb_file_counter;
|
|
}
|
|
|
|
sub nodirs {
|
|
grep !-d, @_;
|
|
}
|
|
|
|
sub progress_timeout {
|
|
$pb->pulse;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
sub wanted {
|
|
my $file = $_;
|
|
return unless ( -f $file );
|
|
my $hidden = ClamTk::Prefs->get_preference( 'ScanHidden' );
|
|
next if ( !$hidden && $file =~ /^\./ );
|
|
$pb_file_counter++;
|
|
}
|
|
|
|
sub sanity_check {
|
|
my $check = shift;
|
|
|
|
if ( -d $check ) {
|
|
if ( !chdir( $check ) || $check =~ m#^(/proc|/sys|/dev)# ) {
|
|
popup(
|
|
_( 'You do not have permissions to scan that file or directory'
|
|
)
|
|
);
|
|
reset_stats();
|
|
return 0;
|
|
} else {
|
|
return 1;
|
|
}
|
|
} elsif ( -f $check ) {
|
|
if ( !-r $check || $check =~ m#^(/proc|/sys|/dev)# ) {
|
|
popup(
|
|
_( 'You do not have permissions to scan that file or directory'
|
|
)
|
|
);
|
|
reset_stats();
|
|
return 0;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
sub popup {
|
|
my ( $message, $option ) = @_;
|
|
|
|
my $dialog = Gtk3::MessageDialog->new(
|
|
undef, # no parent
|
|
[ qw| modal destroy-with-parent | ],
|
|
'info',
|
|
$option ? 'ok-cancel' : 'close',
|
|
$message,
|
|
);
|
|
|
|
if ( 'ok' eq $dialog->run ) {
|
|
$dialog->destroy;
|
|
return TRUE;
|
|
}
|
|
$dialog->destroy;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
1;
|