#!/usr/bin/perl -w
#
# Copyright (C) 2008-2009, Joshua D. Abraham (jabra@spl0it.org)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
# use strict;
#
#-----------------------------------------------------------------------
# .07
#   Add icon check for line: Exec=sh -c "COMMAND1;COMMAND2"
#
# .06
#   Added Wordlists
#
# .05
#   Removed Enumeration, since we can use Discovery.
#   Added Ruby section
#
# .04
#   packages now contain version and bt revision.
#
#
#
use strict;
use Getopt::Long;
use vars qw( $PROG );
( $PROG = $0 ) =~ s/^.*[\/\\]//;    # Truncate calling path from the prog name
my $AUTH    = 'Joshua D. Abraham';  # author
my $EMAIL   = 'jabra@spl0it.org';   # email
my $VERSION = '0.07';               # version
my %options;                        # getopt option hash
my $deb;
my $errors   = 0;
my $warnings = 0;
my $verbose  = 0;
my $file;
my $dir;

my @sections = qw(Tunneling
    Bruteforce
    Spoofing
    Passwords
    Wireless
    Discovery
    Cisco
    Web
    Forensics
    Fuzzers
    Bluetooth
    Misc
    Sniffers
    VOIP
    Debuggers
    Penetration
    Database
    RFID
    Python
    Perl
    Ruby
    Drivers
    Wordlists
    GPU
    );

#
# help:
# display help information
#
sub help {
    print "Usage: $PROG [Input Option] [Option] 
    -d  --deb [str]         Debian package to check
    -m  --maintainer [str]  Check package for a certain maintainer
    -r  --remove            Remove directory after checking
    -f  --force             Force remove
    -v  --verbose           Verbose information
    -h  --help              Display this information
Send Comments to $AUTH ( $EMAIL )\n";
    exit;
}

#
# print_version:
# displays version
#
sub print_version {
    print "$PROG version $VERSION by $AUTH ( $EMAIL )\n";
    exit;
}

sub error {
    my ($str) = @_;
    print "Error: $str\n";
    $errors++;
}

sub quit {
    my ($str) = @_;
    print "Error: $str\n";
    exit 1;
}

sub warnings {
    my ($str) = @_;
    print "Warning: $str\n";
    $errors++;
}

sub check_icons {
    my ($pkg_dir) = @_;
    print "* Checking icons files\n" if ( $verbose == 1 );
    my $location = "$pkg_dir/usr/local/share/applications";
    if (-d $location) {
        opendir(DIR, $location);
        for (readdir(DIR)) {
            if (/\.desktop/){
                error("menu item exists in the wrong location. Please move the icons to /opt/kde3/share/applications/external");
            }
        }
        close(DIR);
    }

    $location = "$pkg_dir//opt/kde3/share/applications/external";
    if (-d $location) {
        opendir(DIR, $location);
        for my $file (readdir(DIR)) {
            if ($file =~ /\.desktop$/){
                open(IN,"$location/$file") or die("cant open $location/$file");
                my @lines=<IN>;
                foreach my $line (@lines) {
                    chomp($line);
                    if ($line =~ /Exec=/ and $line !~ /Exec=\s*?sh -c\s+?\".*\"/) {
                        error("$file does not contain Exec=sh -c \"COMMAND1;COMMAND2\"");
                    }
                }
            }
        }
        close(DIR);
    }
}

sub check_maintainer {
    my ($debian) = @_;
    open( MAINT, "<$debian/control" ) or quit("Can't open $debian/control");
    my $r = 0;
    foreach (<MAINT>) {
        if (/Maintainer:(.*)/) {
            my $m = $1;
            $m =~ s/^\s+//g;
            if ( $m ne $options{maintainer} ) {
                $r = 0;
            }
            else {
                $r = 1;
            }
            last;
        }
    }
    close(MAINT);
    return $r;
}

sub check_changelog {
    my ($debian) = @_;

    print "* Checking changelog file \n" if ( $verbose == 1 );
    my ( $name, $version );
    open( CONTROL, "<$debian/control" ) or quit("Can't open $debian/control");
    foreach (<CONTROL>) {
        if ( defined($name) and defined($version) ) {
            last;
        }
        if (/Package:(.*)/) {
            $name = $1;
        }
        elsif (/Version:(.*)/) {
            $version = $1;
        }
        else { }
    }
    close(CONTROL);

    open( CHANGELOG, "<$debian/changelog" )
        or quit("Can't open $debian/changelog");
    my @in = <CHANGELOG>;
    if ( $in[0] =~ /(.*)\s(.*) pwnsauce; urgency=low/ ) {
        my $changelog_name    = $1;
        my $changelog_version = $2;
        $changelog_name    =~ s/^\s+|\s+$//g;
        $changelog_version =~ s/\(|\)//g;

        $name    =~ s/^\s+|\s+$//g;
        $version =~ s/^\s+|\s+$//g;

        chomp($name);
        chomp($version);
        chomp($changelog_name);
        chomp($changelog_version);

        if ( $changelog_name ne $name ) {
            error("name in the changelog different from the control");
        }
        if ( $changelog_version ne $version ) {
            error("version in the changelog different from the control");
        }
    }
    else {
        error("First line of the changelog is incorrect");
    }
    close(CHANGELOG);
}

sub check_copyright {
    my ($debian) = @_;
    print "* Checking copyright file\n" if ( $verbose == 1 );
    open( CHANGELOG, "<$debian/copyright" )
        or quit("Can't open $debian/copyright");
    my @in = <CHANGELOG>;
    my ( $copyright, $download, $author );
    foreach (@in) {
        if (/Copyright:/) {
            $copyright++;
        }
        if ( /It was downloaded from/ and /(ftp|http(s?)):\/\// ) {
            $download++;
        }
        if (/Upstream Author\(s\):/) {
            $author++;
        }
    }
    if ( defined($download) and $download == 1 ) {

    }
    else {
        error('copyright file missing download information');
    }
    if ( defined($author) and $author == 1 ) {

    }
    else {
        error('copyright file missing author information');
    }
    if ( defined($copyright) and $copyright == 1 ) {

    }
    else {
        error('copyright file missing copyright information');
    }
}

sub check_control {
    my ($debian) = @_;
    print "* Checking control file\n" if ( $verbose == 1 );
    open( IN, "<$debian/control" ) or quit("Can't open $debian/control");
    my ($package, $version);
    foreach (<IN>) {
        chomp;
        if (/Package:(.*)/) {
            if ( $1 =~ /\.\d+$/ ) {
                error("Name contains version information");
            }
            $package = $1;
            $package =~ s/\s+//g;
        }
        if (/Version:(.*)/) {
            my $version = $1;
            $version =~ s/\s+//g;
            if ( $version !~ /bt\d+$/ ) {
                error("Version information does not contain -btVERSION");
            }
            if ( "$package-$version" ne lc($dir) ) {
                error('Package name should include the package name and version');
            }
        }
        if (/Section:(.*)/) {
            my $section = $1;
            $section =~ s/^\s+|\s+$//g;
            my $found = 0;
            foreach my $s (@sections) {
                my $bt_s;
                if ($s =~ /GPU/) {
                   $bt_s = "BackTrack-$s";
                }
                else {
                    $bt_s = "BackTrack - $s";
                }
                if ( $found == 0 and $section eq $bt_s ) {
                    $found = 1;
                    last;
                }
            }

            if ( $found == 0 ) {
                error(
                    "Section not defined in the Policy. Please review Packaging-Policy.txt in svn"
                );
            }
        }

        if (/Depends:(.*)/) {
            if ( $1 !~ /\w+/ ) {
                error(
                    "Depends is blank (remove line or add a depends package)"
                );
            }
        }
        if (/Recommends:(.*)/) {
            if ( $1 !~ /\w+/ ) {
                error(
                    "Recommends is blank (remove line or add a recommends package)"
                );
            }
        }
    }
    close(IN);
}

sub results {
    if ( $verbose == 1 ) {
        print "* Package Checking Results: ";
        if ( $warnings > 0 or $errors > 0 ) {
            print "Failed\n";
            print "    Warnings: $warnings\n";
            print "    Errors: $errors\n";
        }
        else {
            print "Okay\n";
        }
    }
    if ( !$options{force} and $errors != 0 ) {
        exit;
    }
}

if ( @ARGV == 0 ) {
    help;
    exit;
}
GetOptions(
    \%options,
    'deb|d=s', 'remove|r', 'force|f', 'maintainer|m=s',
    'help|h'    => sub { help(); },
    'verbose|v' => sub { $verbose = 1; },
) or exit 1;

if ( $options{deb} ) {
    $deb = $options{deb};
    if ( -r $deb ) {
        $dir  = uc($deb);
        $file = $deb;
        $dir =~ s/\.deb//ig;
        system("dpkg-deb -x $file $dir > /dev/null 2>&1");
        system("dpkg-deb -e $file $dir/DEBIAN > /dev/null 2>&1");
        if ( -e $dir and -e "$dir/DEBIAN" ) {
            if ( $options{maintainer} ) {
                if ( check_maintainer("$dir/DEBIAN") == 0 ) {
                    if ( $options{remove} ) {
                        system("rm -Rf $dir");
                        exit;
                    }
                }
            }
            check_control("$dir/DEBIAN");
            check_copyright("$dir/DEBIAN");
            check_changelog("$dir/DEBIAN");
            check_icons("$dir");
            results();
            if ( $options{remove} ) {
                system("rm -Rf $dir");
            }
            if ( $verbose == 0 and $errors != 0 ) {
                exit 1;
            }

        }
        else {
            error("Unable to extract package");
        }

    }
    else {
        help();
    }
}
else {
    help();
}

