mirror of
https://github.com/dave-theunsub/clamtk.git
synced 2026-01-21 04:58:17 -05:00
366 lines
9.5 KiB
Perl
366 lines
9.5 KiB
Perl
# ClamTk, copyright (C) 2004-2024 Dave M
|
|
#
|
|
# This file is part of ClamTk
|
|
# https://github.com/dave-theunsub/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::Prefs;
|
|
|
|
# use strict;
|
|
# use warnings;
|
|
$| = 1;
|
|
|
|
use Digest::MD5 'md5_hex';
|
|
use File::Path 'mkpath';
|
|
|
|
use Locale::gettext;
|
|
use POSIX 'locale_h';
|
|
|
|
sub structure {
|
|
my $paths = ClamTk::App->get_path( 'all' );
|
|
|
|
# Ensure default paths/files exist.
|
|
# If they do, ensure they have the proper permissions.
|
|
# The default Fedora umask for users is 0002,
|
|
# while Ubuntu's is 0022... sigh.
|
|
# I'm going to assume that it's one or the other.
|
|
# my $umask = sprintf("%04o", umask());
|
|
my $mask = ( umask() == 2 ) ? '0775' : '0755';
|
|
|
|
# This is /home/user/.clamtk/viruses,
|
|
# used for the quarantine directory
|
|
if ( !-d $paths->{ viruses } ) {
|
|
eval { mkpath( $paths->{ viruses }, { mode => oct( $mask ) } ) };
|
|
warn $@ if ( $@ );
|
|
return 0 if ( $@ );
|
|
} else {
|
|
# Ensure the permissions are correct
|
|
chmod oct( $mask ), $paths->{ viruses };
|
|
}
|
|
|
|
# This is /home/user/.clamtk/history,
|
|
# which holds records of scans
|
|
if ( !-d $paths->{ history } ) {
|
|
eval { mkpath( $paths->{ history }, { mode => oct( $mask ) } ) };
|
|
warn $@ if ( $@ );
|
|
return 0 if ( $@ );
|
|
} else {
|
|
# Ensure the permissions are correct
|
|
chmod oct( $mask ), $paths->{ history };
|
|
}
|
|
|
|
# The path /home/user/.clamtk/db stores signatures
|
|
if ( !-d $paths->{ db } ) {
|
|
eval { mkpath( $paths->{ db }, { mode => oct( $mask ) } ) };
|
|
warn $@ if ( $@ );
|
|
return 0 if ( $@ );
|
|
} else {
|
|
# Ensure the permissions are correct
|
|
chmod oct( $mask ), $paths->{ db };
|
|
}
|
|
|
|
# This is /home/user/.clamtk/prefs,
|
|
# a custom INI-style file.
|
|
if ( !-e $paths->{ prefs } ) {
|
|
# warn "note: (re)creating prefs file.\n";
|
|
open( my $F, '>:encoding(UTF-8)', $paths->{ prefs } )
|
|
or do {
|
|
warn "Unable to create preferences! $!\n";
|
|
return 0;
|
|
};
|
|
close( $F );
|
|
eval { custom_prefs() };
|
|
warn $@ if ( $@ );
|
|
return 0 if ( $@ );
|
|
}
|
|
|
|
# This is /home/user/.clamtk/submit.
|
|
# This was used for submitting to ClamAV;
|
|
# we're reusing the directory structure
|
|
if ( !-d $paths->{ submit } ) {
|
|
eval { mkpath( $paths->{ submit }, { mode => oct( $mask ) } ) };
|
|
warn $@ if $@;
|
|
return 0 if ( $@ );
|
|
} else {
|
|
# Ensure the permissions are correct
|
|
chmod oct( $mask ), $paths->{ submit };
|
|
}
|
|
|
|
# This is /home/user/.clamtk/submit/previous_submissions,
|
|
# a csv file.
|
|
if ( !-e $paths->{ previous_submissions } ) {
|
|
open( my $F, '>:encoding(UTF-8)', $paths->{ previous_submissions } )
|
|
or do {
|
|
warn "Unable to create previous_submissions! $!\n";
|
|
return 0;
|
|
};
|
|
close( $F );
|
|
}
|
|
|
|
# This is /home/user/.clamtk/submit/virustotal_links,
|
|
# a csv file.
|
|
if ( !-e $paths->{ virustotal_links } ) {
|
|
open( my $F, '>:encoding(UTF-8)', $paths->{ virustotal_links } )
|
|
or do {
|
|
warn "Unable to create virustotal_links! $!\n";
|
|
return 0;
|
|
};
|
|
close( $F );
|
|
}
|
|
|
|
# This is /home/user/.clamtk/restore, which holds
|
|
# information for putting back false positives
|
|
if ( !-e $paths->{ restore } ) {
|
|
# warn "restore does not exist; re-creating it\n";
|
|
open( my $F, '>:encoding(UTF-8)', $paths->{ restore } )
|
|
or do {
|
|
warn "Unable to create restore file! $!\n";
|
|
return 0;
|
|
};
|
|
close( $F );
|
|
}
|
|
|
|
# Automatically set local freshclam.conf for individual updates
|
|
set_local_config();
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub custom_prefs {
|
|
# ensure prefs have normalized variables:
|
|
my %pkg;
|
|
# Get the user's current prefs
|
|
my $paths = ClamTk::App->get_path( 'prefs' );
|
|
|
|
open( my $F, '<:encoding(UTF-8)', $paths )
|
|
or do {
|
|
warn "Unable to read preferences! $!\n";
|
|
return 0;
|
|
};
|
|
|
|
while ( <$F> ) {
|
|
my ( $k, $v ) = split( /=/ );
|
|
chomp( $v );
|
|
$pkg{ $k } = $v;
|
|
}
|
|
close( $F );
|
|
|
|
# If the preferences aren't already set,
|
|
# use 'shared' by default. This makes it work out of the box.
|
|
if ( !exists $pkg{ Update } ) {
|
|
$pkg{ Update } = 'shared';
|
|
} elsif ( $pkg{ Update } !~ /shared|single/ ) {
|
|
# If it's set to 'shared' or 'single', leave it alone.
|
|
# Otherwise, look for system signatures
|
|
$pkg{ Update } = 'shared';
|
|
}
|
|
|
|
# The proxy is off by default
|
|
if ( !exists $pkg{ HTTPProxy } ) {
|
|
$pkg{ HTTPProxy } = 0;
|
|
}
|
|
|
|
# The whitelist is off by default
|
|
if ( !exists $pkg{ Whitelist } ) {
|
|
$pkg{ Whitelist } = '';
|
|
}
|
|
|
|
# Date of last infected file
|
|
if ( !exists $pkg{ LastInfection } ) {
|
|
$pkg{ LastInfection } = _( 'Never' );
|
|
}
|
|
|
|
# ScanHidden: Scan files beginning with a dot
|
|
# SizeLimit: Scan files larger than 20MB
|
|
# Thorough: Scan files for PUA
|
|
# Recursive: Scan all files/directories within a directory
|
|
# Mounted: Scan gvfs and related directories
|
|
for my $o (
|
|
qw{ScanHidden SizeLimit Heuristic
|
|
Thorough Recursive Mounted}
|
|
)
|
|
{
|
|
# off by default
|
|
if ( !exists $pkg{ $o } ) {
|
|
$pkg{ $o } = 0;
|
|
}
|
|
}
|
|
|
|
# GUICheck: Check for GUI updates
|
|
# TruncateLog: Shorten freshclam log
|
|
# DupeDB: Delete duplicate signature dbs
|
|
for my $p ( qw{GUICheck TruncateLog DupeDB } ) {
|
|
# on by default
|
|
if ( !exists $pkg{ $p } ) {
|
|
$pkg{ $p } = 1;
|
|
}
|
|
}
|
|
|
|
# dtformat - for date-time-format
|
|
# if ( !exists $pkg{ 'dtformat' } ) {
|
|
# $pkg{ 'dtformat' } = '%m %d %Y';
|
|
# }
|
|
|
|
write_all( %pkg );
|
|
return;
|
|
}
|
|
|
|
sub get_all_prefs {
|
|
# Sometimes it's useful to have all
|
|
# the preferences rather than just one.
|
|
my %pkg;
|
|
my $paths = ClamTk::App->get_path( 'prefs' );
|
|
open( my $F, '<:encoding(UTF-8)', $paths )
|
|
or do {
|
|
warn "Unable to read preferences! $!\n";
|
|
return 0;
|
|
};
|
|
|
|
while ( <$F> ) {
|
|
my ( $k, $v ) = split( /=/ );
|
|
chomp( $v );
|
|
$pkg{ $k } = $v;
|
|
}
|
|
close( $F );
|
|
return %pkg if %pkg;
|
|
}
|
|
|
|
sub legit_key {
|
|
# Sanity check the prefs file's keys.
|
|
my @keys = qw(
|
|
SizeLimit HTTPProxy Heuristic
|
|
LastInfection GUICheck DupeDB
|
|
TruncateLog SaveToLog
|
|
Whitelist Update ScanHidden
|
|
Thorough Recursive Mounted
|
|
);
|
|
return 1 if ( grep { $_[ 0 ] eq $_ } @keys );
|
|
}
|
|
|
|
sub write_all {
|
|
my %loc = @_;
|
|
|
|
my $paths = ClamTk::App->get_path( 'prefs' );
|
|
open( my $F, '>:encoding(UTF-8)', $paths )
|
|
or do {
|
|
warn "Unable to write preferences! $!\n";
|
|
return 0;
|
|
};
|
|
|
|
while ( my ( $k, $v ) = each %loc ) {
|
|
if ( legit_key( $k ) ) {
|
|
print $F "$k=$v\n";
|
|
}
|
|
}
|
|
close( $F );
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub set_preference {
|
|
my ( undef, $wk, $wv ) = @_; # undef = package name
|
|
my $paths = ClamTk::App->get_path( 'prefs' );
|
|
|
|
open( my $F, '<:encoding(UTF-8)', $paths )
|
|
or do {
|
|
warn "Unable to read preferences! $!\n";
|
|
return 0;
|
|
};
|
|
|
|
my %pkg;
|
|
while ( <$F> ) {
|
|
my ( $k, $v ) = split( /=/ );
|
|
chomp( $v );
|
|
$pkg{ $k } = $v;
|
|
}
|
|
close( $F );
|
|
|
|
open( $F, '>:encoding(UTF-8)', $paths )
|
|
or return -1;
|
|
|
|
while ( my ( $k, $v ) = each %pkg ) {
|
|
if ( legit_key( $k ) && ( $k ne $wk ) ) {
|
|
print $F "$k=$v\n";
|
|
}
|
|
}
|
|
print $F "$wk=$wv\n" if ( legit_key( $wk ) );
|
|
close( $F )
|
|
or warn "Couldn't close $paths: $!\n";
|
|
return 1;
|
|
}
|
|
|
|
sub get_preference {
|
|
my ( undef, $wanted ) = @_; # undef = package name
|
|
|
|
my $paths = ClamTk::App->get_path( 'prefs' );
|
|
my %pkg;
|
|
open( my $F, '<:encoding(UTF-8)', $paths )
|
|
or do {
|
|
warn "Unable to read preferences! $!\n";
|
|
return 0;
|
|
};
|
|
|
|
while ( <$F> ) {
|
|
my ( $k, $v ) = split( /=/ );
|
|
chomp( $v );
|
|
$pkg{ $k } = $v;
|
|
}
|
|
close( $F );
|
|
|
|
return unless %pkg;
|
|
return $pkg{ $wanted } || '';
|
|
}
|
|
|
|
sub set_proxy {
|
|
my ( undef, $ip, $port ) = @_; # undef = package name
|
|
|
|
# If the user doesn't set a port, we'll just jot down port 80.
|
|
$port = $port || '80';
|
|
|
|
my $path = ClamTk::App->get_path( 'localfreshclamconf' );
|
|
warn "Prefs set_proxy: path = >$path<\n";
|
|
|
|
# This gets clobbered every time.
|
|
open( my $FH, '>:encoding(UTF-8)', $path )
|
|
or return -1;
|
|
print $FH <<"EOF";
|
|
HTTPProxyServer $ip
|
|
HTTPProxyPort $port
|
|
DatabaseMirror db.local.clamav.net
|
|
DatabaseMirror database.clamav.net
|
|
EOF
|
|
close( $FH )
|
|
or warn "Couldn't close $path/local.conf: $!\n";
|
|
return 1;
|
|
}
|
|
|
|
sub set_local_config {
|
|
my $path = ClamTk::App->get_path( 'localfreshclamconf' );
|
|
return if ( -e $path );
|
|
|
|
# This gets clobbered every time.
|
|
open( my $FH, '>:encoding(UTF-8)', $path )
|
|
or return -1;
|
|
print $FH <<"EOF";
|
|
# Local config
|
|
DatabaseMirror database.clamav.net
|
|
LogSyslog no
|
|
EOF
|
|
close( $FH )
|
|
or warn "Couldn't close $path: $!\n";
|
|
if ( !-e $path ) {
|
|
warn "Couldn't create local freshclam ($path)!\n"
|
|
. "You will be unable to do manual updates.\n";
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
1;
|