#!/usr/bin/perl -w
use strict;

my $dn = `dirname $0`;chomp($dn);
my $pwd = `pwd`;chomp($pwd);
if ($dn !~ /^\//) { $dn = $pwd . "/" . $dn; }
push @INC,$dn;

use DateTime;

require CanDBReader;
require ModuleContained;
require UpdateInfoReader;
UpdateInfoReader->import_product_updates();
require SMASHData;
&SMASHData::read_all_cached_issues();

my %products = (
	'sles12-sp5'   => 'SUSE Linux Enterprise Server 12 SP5',
);


my %cve2packages = ();
my %cve2unfixed = ();
my %cve2rating = ();
my %cve2time = ();
my %cve2epoch = ();

sub
cvsscmp($$) {
	my ($a,$b) = @_;
	my @a = @{$a};
	my @b = @{$b};
	print STDERR "comparing " . $a[2] . " and " . $b[2] . "\n";
	return $b[2] cmp $a[2];
}

sub
getscore($) {
	my ($cve) = @_;

	my $basescore = "unknown";
	die "cve is $cve in getscore " unless ($cve =~ /^CVE/);
	&SMASHData::read_smash_issue($cve);

	if (defined($SMASHData::cvssv3{$cve}))  {
		my %entry = %{$SMASHData::cvssv3{$cve}};
		my %score;
		if (defined($entry{'SUSE'})) {
			%score = %{$entry{'SUSE'}};
			$basescore = "$score{'base_score'} (SUSE)";
		} else {
			if (defined($entry{'National Vulnerability Database'})) {
				%score = %{$entry{'National Vulnerability Database'}};
				$basescore = "$score{'base_score'}";
			}
		}
	}
	return $basescore;
}

sub
get_source_by_smash($$) {
	my ($prod,$cve) = @_;

	&SMASHData::read_smash_issue($cve);
	return () unless ($SMASHData::pkgstate{$cve});

	my %src = ();

	foreach my $xprod (keys %{$SMASHData::pkgstate{$cve}}) {
		next unless ($xprod =~ /$prod/);
		foreach my $src (keys %{$SMASHData::pkgstate{$cve}->{$xprod}}) {
			$src{$src}=1;
		}
	}
	return sort keys %src;
}

my $countedcvss = ();	# product -> { absolute(nr) or "unknown" -> count }
my $productcves = ();	# product -> { cve-> 1 }

foreach my $codename (keys %products) {
	my $marketingname = $products{$codename};

	print "$marketingname\n";

	my %patches = ();
	foreach my $xprod (grep (/$marketingname/,keys %UpdateInfoReader::patches)) {

		next if ($xprod =~ /Development-Tools-OBS/);

		my %xpatches = %{$UpdateInfoReader::patches{$xprod}};
		foreach my $patch (keys %xpatches) {
			next if ($patch =~ /Development-Tools-OBS/);
			$patches{$patch} = 1;
		}
	}
	# my %patches = %{$UpdateInfoReader::patches{$marketingname}};

	#open(PATCHES,">$codename-patches.csv");
	%cve2packages = ();
	%cve2time = ();

	my %havepatch = ();
	foreach my $patch (keys %patches) {
		next if ($UpdateInfoReader::patchinqa{$patch});

		next if ($patch =~ / GA /);
		next if ($UpdateInfoReader::patchtitle{$patch} =~ / GA /);

		my $intpatch = $patch;

		$intpatch =~ s/^SUSE-.*-(\d+-\d+)$/SUSE-$1/;;
		next if ($havepatch{$intpatch});

		#$havepatch{$intpatch} = 1;

		my $issued = $UpdateInfoReader::patchissued{$patch};
		my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($issued);

		$year+= 1900;
		$mon ++;
		my $xtime = sprintf("%d-%02d-%02d",$year,$mon,$mday);
		my @references = sort grep (/CVE/, keys %{$UpdateInfoReader::patchreferences{$patch}});

		my %packages = %{$UpdateInfoReader::patchpackages{$patch}};
		my @packages = ();

		foreach my $pkg (sort keys %packages) {
			push @packages, "$pkg-$packages{$pkg}",
		}

		next unless (@references);

		my $basescore = 0;

		foreach my $cve (@references) {
			my @xpkgs = ();
			if (defined($cve2packages{$cve})) {
				@xpkgs = @{$cve2packages{$cve}};
			}
			push @xpkgs,@packages;
			$cve2packages{$cve} = \@xpkgs;

			$cve2time{$cve}		= $xtime;
			$cve2unfixed{$cve}	= 0;
			if (defined($cve2epoch{$cve})) {
				# if already released, update it only if "newer" ... we want the LAST released update as indicator
				$cve2epoch{$cve}	= $issued if ($cve2epoch{$cve} < $issued)
			} else {
				$cve2epoch{$cve}	= $issued;
			}
			$cve2rating{$cve} = $UpdateInfoReader::patchseverity{$patch};
			#print "\t$xtime:$cve:" . join(",", @packages) . "\n";
			my $newscore = getscore($cve);
			if ($newscore ne "unknown") {
				if ($basescore < $newscore) {
					$basescore = $newscore;
				}
			}
		}
		#print PATCHES "$intpatch,$xtime,$basescore,\"" . join(",", @references) . "\",\"" . join(",", @packages) . "\"\n";
	}
	#close(PATCHES);

	foreach my $cve (keys %SMASHData::pkgstate) {
		my %state = %{$SMASHData::pkgstate{$cve}};

		next unless (defined($state{$marketingname}));
		my %prodstate = %{$state{$marketingname}};
		foreach my $pkg (keys %prodstate) {
			if ($prodstate{$pkg} eq "Affected") {
				my @xpkgs = ();
				if (defined($cve2packages{$cve})) {
					@xpkgs = @{$cve2packages{$cve}};
				}
				push @xpkgs,"affected $pkg";
				$cve2packages{$cve} = \@xpkgs;

				# $cve2epoch{$cve} = time;
				$cve2epoch{$cve} = 0;
				$cve2unfixed{$cve} = 1;
			}
		}
	}

	open(CVES,">huawei-$codename-cves.csv");
	print CVES "#cve,day opened,day fix in kernel git,day of release,suse score,unfixed,packages\n";
	foreach my $cve (keys %cve2packages) {
		my $basescore = "unknown";
		my $nvdbasescore = "unknown";
		&SMASHData::read_smash_issue($cve);
		if (defined($SMASHData::cvssv3{$cve}))  {
			my %entry = %{$SMASHData::cvssv3{$cve}};
			my %score;
			if (defined($entry{'National Vulnerability Database'})) {
				%score = %{$entry{'National Vulnerability Database'}};
				$nvdbasescore = $score{'base_score'};
			}
			if (defined($entry{'SUSE'})) {
				%score = %{$entry{'SUSE'}};
				$basescore = $score{'base_score'};
			} else {
				if (defined($entry{'National Vulnerability Database'})) {
					%score = %{$entry{'National Vulnerability Database'}};
					$basescore = $score{'base_score'};
				}
			}
		}
		#next if ($basescore =~ /^[0-6]/);
		#next if ($basescore =~ /^unknown/);
		my $integerbase;

		if ($basescore =~ /^(\d*)/) {
			$integerbase = $1;
		} else {
			$integerbase = $basescore;
		}

#		foreach my $xprod (grep (/$marketingname/,keys %UpdateInfoReader::patches)) {
#			next if ($xprod =~ /Development-Tools-OBS/);
#
#			$productcves->{$xprod}->{$cve} = 1;
#		}

		if (!defined($CanDBReader::firstdate{$cve})) {
			print STDERR "no bug for $cve, skipping.\n";
			next;
		}

		my ($year,$month,$day) = ($CanDBReader::firstdate{$cve} =~ /(\d\d\d\d)(\d\d)(\d\d)/);
		print STDERR "FIRST: $cve: $CanDBReader::firstdate{$cve}, year $year month $month day $day\n";

		next if ($year < 2024);	# we started this only with issues in 2024.

		my $rt;
		my $released;
		if ($cve2epoch{$cve} == 0) {
			$released = "";
		} else {
			$rt = DateTime->from_epoch(epoch => $cve2epoch{$cve});
			$released = sprintf("%04d-%02d-%02d",$rt->year,$rt->month,$rt->day);
		}

		my @pkgs = ();

		my $firstdate = $CanDBReader::firstdate{$cve};
		my $committed = $CanDBReader::firstdate{$cve};
		if (defined($CanDBReader::kernelcvedates{"12-SP5"}->{$cve})) {
			$committed = $CanDBReader::kernelcvedates{"12-SP5"}->{$cve};
			$committed = $CanDBReader::firstdate{$cve} if ($committed eq "20240528")
		}
		$committed =~ s/(\d\d\d\d)(\d\d)(\d\d)/$1-$2-$3/;
		$firstdate =~ s/(\d\d\d\d)(\d\d)(\d\d)/$1-$2-$3/;

		print STDERR "COMMITTED: $cve: $committed, year $year month $month day $day\n";

		my $haveone = 0;
		foreach my $src (get_source_by_smash($marketingname,$cve)) {
			#printf CVES ("$cve,$firstdate,$committed,$released,$basescore,$nvdbasescore,$src\n");
			push @pkgs,$src;
			$haveone = 1;
		}
		if (!$haveone) {
			#printf CVES ("$cve,$firstdate,$committed,$released,$basescore,$nvdbasescore,\"$cve2packages{$cve}\"\n");
			@pkgs = @{$cve2packages{$cve}};
		}
		if ($#pkgs > 0) {
			printf CVES ("$cve,$firstdate,$committed,$released,$basescore,%d,\"" . join(",",@pkgs) . "\"\n", $cve2unfixed{$cve});
		} else {
			printf CVES ("$cve,$firstdate,$committed,$released,$basescore,%d," . join(",",@pkgs) . "\n", $cve2unfixed{$cve});
		}
	}
	close(CVES);
}
