# ClamTk, copyright (C) 2004-2016 Dave M # # This file is part of ClamTk (https://dave-theunsub.github.io/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 ); } 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 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; } } # Single click or double click in the main iconview. # Since we started with double, stick with it by # default for less aggravation # 1 = single, 2 = double # Clickings = short for Click Settings. I think. if ( !exists $pkg{ Clickings } ) { $pkg{ Clickings } = 2; } 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 LastInfection GUICheck DupeDB TruncateLog SaveToLog Whitelist Update ScanHidden Thorough Recursive Mounted Clickings ); 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( 'db' ); # This gets clobbered every time. # Doesn't need to be utf-8 friendly. I think. open( my $FH, '>', "$path/local.conf" ) 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; } 1;