#!/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;
my $cverepobase=`dirname $dn`;
chomp($cverepobase);

use XML::Bare;
use JSON;
use Data::Dumper;

my $jsoncoder = JSON->new->allow_nonref;
$jsoncoder->canonical(1);


require UpdateInfoReader;
# fetches things on demand
require SMASHData;
require PInt;

use POSIX qw/strftime setlocale LC_TIME/;

my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
my $curtime = POSIX::strftime("%Y-%m-%dT%H:%M:%S",$sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst );


my $osc = "osc -A https://api.suse.de/";

my $baseurl = "http://download.suse.de/ibs/";

my $outputbase = "$cverepobase/images/";

my %project2package2fixes = ();
my %project2package2patches = ();

my %allcontainers = ();
my @allimages = ();

my %affectedby = ();

my $mailsent = 0;

# { package => [ version-release	=> [CVE, CVE], ]

#<buildinfo project="SUSE:SLE-15:Update:CR" repository="images" package="sles15-image" downloadurl="http://download.suse.de/ibs">
#  <arch>x86_64</arch>
#  <debuginfo>0</debuginfo>
#  <imagetype>docker</imagetype>
#  <bdep name="ca-certificates" noinstall="1" version="2+git20170807.10b2785" release="7.3.3" arch="noarch" project="SUSE:SLE-15:Update" repository="standard"/>
#  <path project="SUSE:SLE-15:Update:CR" repository="images" url="http://download.suse.de/ibs/SUSE:/SLE-15:/Update:/CR/images/"/>
#  <path project="SUSE:SLE-15:Update" repository="standard" url="http://download.suse.de/ibs/SUSE:/SLE-15:/Update/standard/"/>
# <path project="SUSE:SLE-15:GA" repository="standard" url="http://download.suse.de/ibs/SUSE:/SLE-15:/GA/standard/"/>
#</buildinfo>

my %containertag = ();
my %projects = ();
my %prevpatches = ();
my %prevpatcheshtml = ();
my %prevpackages = ();
my %htmlcontent = ();
my %ids = ();

sub
read_updateids() {
	if (open(IDS,"<$cverepobase/data/containerid")) {
		while (<IDS>) {
			chomp;
			my ($container,$id) = split(/;/);
			$ids{$id} = $container;
			$ids{$container} = $id;
		}
		close(IDS);
	}
}

sub
get_id($) {
	my ($container) = @_;
	my $suffix = "CU";

	$container =~ s/.*\///;

	return $ids{$container} if (defined($ids{$container}));

	my $year;
	# containers end with a .timestamp , and also start / are fully lowercase.
	# non-container images currently start with uppercase
	if (	($container !~ /^[A-Z]/ || ($container =~ /SLE-Micro-Rancher/)) &&
		($container =~ /\.(\d\d\d\d)\d*$/)
	) {
		$suffix = "CU";
		$year = $1;
	} else {
		$suffix = "IU";
		if ($container =~ /v(\d\d\d\d)\d\d\d/) {
			$year = $1;
		} else {
			if ($container =~ /\.(\d\d\d\d)\d*/) {
				$year = $1;
			} else {
				$year = 2000;
			}
		}
	}
	my @curids = grep (/SUSE-$suffix-$year:/,sort keys %ids);
	my $newid = $#curids + 1;

	$newid = 1 if ($newid < 1);

	while (defined($ids{"SUSE-$suffix-$year:$newid-1"})) {
		print STDERR "SUSE-$suffix-$year:$newid-1 exists?\n";
		$newid++;
	}
	$newid = "SUSE-$suffix-$year:$newid-1";
	$ids{$newid} = $container;
	$ids{$container} = $newid;
	open(IDS,">>$cverepobase/data/containerid");
	print IDS "$container;$newid\n";
	close(IDS);
	return $newid;
}

sub
xcompare($$) {
	my ($a,$b) = @_;

	my @a;
	my @b;

	return 0 if ($a eq $b);

	do {
		#print "CMP at $a $b\n";

		# Emptied one string? than its larger
		return  1 if ($a ne "" && $b eq "");
		return -1 if ($a eq "" && $b ne "");

		my $vera;
		my $verb;

		# Number
		if ($a =~ /^(\d+)/) {
			$vera = $1;
			$a =~ s/\d*//;

			$b =~ /^(\d+)/;
			$verb = $1;
			$b =~ s/\d*//;

			#print "CMP number $vera $verb\n";

			return -1 if ($vera < $verb);
			return  1 if ($vera > $verb);
		} else {
			# alpha
			$a =~ s/^([^\d]+)//;
			$vera = $1;
			$b =~ s/^([^\d]+)//;
			$verb = $1;
			my $ret = $vera cmp $verb;
			#print "CMP alphanumeric $vera $verb is $ret\n";
			return -1 if ($ret < 0);
			return  1 if ($ret > 0);
		}
	} while (($a ne "") || ($b ne ""));
	#print "SAME\n";
	return 0;
}

sub
versioncompare($$) {
	my ($a,$b) = @_;
	my @a;
	my @b;

	return 0 if ($a eq $b);

	@a = split(/-/,$a);
	@b = split(/-/,$b);

	foreach my $subvera (@a) {
		my $subverb = shift @b;
		my $ret = xcompare($subvera,$subverb);
		return $ret if ($ret < 0);
		return $ret if ($ret > 0);
	}
	return 0;
}

# convert text to html ...
sub txt2html($) {
	my ($txt) = @_;

	my @lines = split /\n/ , $txt;

	my $inenum = 0;
	foreach my $line (@lines) {
		if ($line =~ /^[^-*o+ ]/) {
			if ($inenum) {
				$line = "</li></ul>$line";
			}
			next;
		}
		if ($line =~ /^[-*+o] (.*)/) {      # enum start or element
			if ($inenum) {
				$line = "</li>\n<li>$1";
			} else {
				$line = "<ul><li>$1";
				$inenum = 1;
			}
			next;
		}
		if ($line =~ /^$/) {
			if ($inenum) {
				$line = "</li></ul>";
				$inenum = 0;
			}
			$line .= "<br/>";
			next;
		}
		# dont modify line
	}
	if ($inenum) {
		push @lines,"</li></ul>";
	}
	return "<p>" . join("\n", @lines) . "</p>";
}

my %alljsons = ();
my @alljsons = ();

# parse build deps into a structure
sub parse_builddep($$$$$$$$) {
	my ($project,$containerinfo,$xml,$outputprefix,$incremental,$version,$first,$sendemail) = @_;
        my $xmlparser = new XML::Bare( text => $xml );

	if (!$xmlparser) {
		warn "parse_builddep: XML: $xml";
		return;
	}

        my $xmlroot = $xmlparser->parse()||die;
        my $bdeps = XML::Bare::forcearray($xmlroot->{'report'}->{'binary'});

	my $containertag;
	my @tags;
	my %containerinfo;
	my $containername;
	if (defined($containerinfo) && ($containerinfo ne "")) {
		my $contref = decode_json($containerinfo);
		%containerinfo = %{$contref};
		@tags = sort @{$containerinfo{'tags'}};

		# find the best tag to specify this container
		foreach my $tag (@tags) {
			next if ($tag =~ /latest$/);
			next if ($tag =~ /^\d*\.\d*$/); # just two digits, assume 15.0 or so
			$containertag = $tag;

		}
		$containername = $containertag;
		$containername =~ s/:.*//;
	}
	my %patches;

	#print Dumper(\$bdeps);

	open(INPUTCVES, "<$outputbase/$outputprefix.cves.txt");
	open(OUTPUTCVES, ">$outputbase/$outputprefix.cves.txt.new");

	my %pkgcve = ();
	# remember which ones we had 
	while (<INPUTCVES>) {
		print OUTPUTCVES $_;
		chomp;

		next if (/^VERSION .*/);
		my ($pkgname,$cve,$cvss) = split(/,/);

		$pkgcve{"$pkgname.$cve"} = 1;

	}
	close(INPUTCVES);

	my $outputcves = "";

	my $outputpkgchange = "";

	my %curpackages = ();

	foreach my $bdep (@{$bdeps}) {
		#print Dumper(\$bdep);

		my $version = $bdep->{'version'}->{'value'};
		my $release = $bdep->{'release'}->{'value'};
		my $name = $bdep->{'name'}->{'value'};
		my $project = $bdep->{'project'}->{'value'};

		if (!defined($project)) {
			print STDERR "no project ... \n" if -t STDERR;
			next;
		}

		$curpackages{$name} = "$version-$release";
		if (!defined($prevpackages{$name})) {
			$outputpkgchange .= "- $name-$version-$release added\n";
			delete $prevpackages{$name};
		} else {
			if ($prevpackages{$name} ne "$version-$release") {
				$outputpkgchange .= "- $name-$version-$release updated\n";
			}
			delete $prevpackages{$name};
		}

		$projects{$project} = 1;

		#print "$project: $name - $version - $release\n";
		next if ($project =~ /:GA/);
		next if ($project =~ /:CASP40$/);

		allupdates($project);
		# print STDERR "project $project\n" unless ($project eq "SUSE:SLE-15:Update");
		if (defined($project2package2fixes{$project})) {
			#print "we have: $name $version-$release\n";
			my $package2fixes = $project2package2fixes{$project};
			if (defined($package2fixes->{$name})) {
				my %version2cve = %{$package2fixes->{$name}};

				foreach my $xver (sort keys %version2cve) {
					#print "checking: $xver\n";
					if (versioncompare("$version-$release",$xver) >= 0) {
						#print "LARGER OR SAME: $version-$release >= $xver\n";
						#print OUTPUTCVES "$name - $version-$release (fixed in $xver): " . join(",",@{$version2cve{$xver}}) . "\n";
						my @cves = grep(/CVE/,@{$version2cve{$xver}});
						if ($#cves > -1) {
							foreach my $cve (sort @cves) {
								&SMASHData::read_smash_issue($cve,0);
								my %cvssv3 = ();
								if (defined($SMASHData::cvssv3{$cve})) {
									%cvssv3 = %{$SMASHData::cvssv3{$cve}};
								}
								my %cvss = ();
								my $vector = "none";
								my $score = "0";

								if (defined($cvssv3{'SUSE'})) { %cvss = %{$cvssv3{'SUSE'}}; }
								if (defined($cvssv3{'National Vulnerability Database'})) { %cvss = %{$cvssv3{'National Vulnerability Database'}}; }

								if (defined($cvss{'base_vector'})) { $vector = $cvss{'base_vector'}; }
								if (defined($cvss{'base_score'})) { $score = $cvss{'base_score'}; }

								next if (defined($pkgcve{"$name.$cve"})); # skip existing entries
								$outputcves .= "$name,$cve,$vector,$score\n";
							}
						}
						my $packages2patches = $project2package2patches{$project};
						if (!defined($packages2patches->{$name})) {
							print STDERR "no packages2patches $xver on $name\n";
						}
						$patches{$packages2patches->{$name}->{$xver}} = 1;
					} else {
						#print "SMALLER: $version-$release > $xver\n";
					}
				}
			}
		}
	}
	foreach my $pkg (sort keys %prevpackages) {
		$outputpkgchange .= "- $pkg-$prevpackages{$pkg} removed\n";
	}
	%prevpackages = %curpackages;
	if ($outputcves ne "") {
		print OUTPUTCVES "VERSION,$version,$curtime\n";
		print OUTPUTCVES $outputcves;
	}
	close(OUTPUTCVES)||die "$outputbase/$outputprefix.cves.txt.new:$!";
	if (system("diff -u $outputbase/$outputprefix.cves.txt $outputbase/$outputprefix.cves.txt.new")) {
		rename("$outputbase/$outputprefix.cves.txt.new","$outputbase/$outputprefix.cves.txt")||die "rename outputbase/$outputprefix.cves.txt.new outputbase/$outputprefix.cves.txt:$!";
	} else {
		unlink("$outputbase/$outputprefix.cves.txt.new");
	}

	open(OUTPUTPATCHES, ">$outputbase/$outputprefix.patches.txt.new");
	open(INPUTPATCHES, "<$outputbase/$outputprefix.patches.txt");
	my %havepatches = ();
	while (<INPUTPATCHES>) {
		print OUTPUTPATCHES $_;
		chomp;

		if (/^Patch: (.*)$/) { $havepatches{$1} = 1; }
	}
	close(INPUTPATCHES);
	my $outputpatches = "";
	foreach my $patch (sort { $UpdateInfoReader::patchissued{$a} <=> $UpdateInfoReader::patchissued{$b} } keys %patches) {
		# already in content
		next if ($havepatches{$patch});
		$outputpatches .= "-----------------------------------------\n";
		$outputpatches .= "Patch: $patch\n";
		$outputpatches .= "Released: " . localtime($UpdateInfoReader::patchissued{$patch}) . "\n";
		$outputpatches .= "Summary: " . $UpdateInfoReader::patchtitle{$patch} . "\n";
		$outputpatches .= "Severity: " . $UpdateInfoReader::patchseverity{$patch} . "\n";
		$outputpatches .= "References: " . join(",",sort keys %{$UpdateInfoReader::patchreferences{$patch}}) . "\n";
		$outputpatches .= "Description:\n" . $UpdateInfoReader::patchdescription{$patch} . "\n";
		$outputpatches .= "\n";
	}
	if ($outputpatches ne "") {
		print OUTPUTPATCHES "-----------------------------------------\n";
		print OUTPUTPATCHES "Version $version $curtime\n\n";
		print OUTPUTPATCHES $outputpatches;
	}
	close(OUTPUTPATCHES)||die "$outputbase/$outputprefix.patches.txt.new:$!";
	if (system("diff -u $outputbase/$outputprefix.patches.txt $outputbase/$outputprefix.patches.txt.new")) {
		rename("$outputbase/$outputprefix.patches.txt.new","$outputbase/$outputprefix.patches.txt")||die "rename $outputbase/$outputprefix.patches.txt.new $outputbase/$outputprefix.patches.txt:$!";
	} else {
		unlink("$outputbase/$outputprefix.patches.txt.new");
	}

	# Generate incremental diffs ... for sending out container update notices "SUSE-CU".
	# this relies on sorted order, it reuses the previous incremental runs via %prevpatches
	if ($incremental) {
		open(OUTPUTNEWPATCHES, ">$outputbase/$outputprefix.newpatches.txt.new");

		my $type = "recommended";
		my $severity = "low";

		my $patchtexts = "";
		my @allrefs = ();
		my $havenewpatches = 0;

		foreach my $patch (sort { $UpdateInfoReader::patchissued{$a} <=> $UpdateInfoReader::patchissued{$b} } keys %patches) {
			next if (defined($prevpatches{$patch}));
			$prevpatches{$patch} = 1;

			$havenewpatches = 1;

			my $susesa = $patch;
			my $prefix = $UpdateInfoReader::patchtype{$patch};
			$prefix = "SU" if ($prefix eq "security");
			$prefix = "RU" if ($prefix eq "recommended");
			$prefix = "OU" if ($prefix eq "optional");

			$type = "security" if ($UpdateInfoReader::patchtype{$patch} eq "security");

			$severity = "critical" if ($UpdateInfoReader::patchseverity{$patch} eq "critical");
			$severity = "important" if (($UpdateInfoReader::patchseverity{$patch} eq "important") && ($severity ne "critical"));
			$severity = "moderate" if (($UpdateInfoReader::patchseverity{$patch} eq "moderate") && ($severity ne "critical") && ($severity ne "important"));

			# synthesize the advisory id.
			$susesa =~ s/SUSE-(\d*)-(\d*)/SUSE-$prefix-$1:$2-1/;

			$patchtexts .= "-----------------------------------------------------------------\n";
			#$patchtexts .= "Patch:       $patch\n";
			$patchtexts .= "Advisory ID: $susesa\n";
			$patchtexts .= "Released:    " . localtime($UpdateInfoReader::patchissued{$patch}) . "\n";
			$patchtexts .= "Summary:     " . $UpdateInfoReader::patchtitle{$patch} . "\n";
			$patchtexts .= "Type:        " . $UpdateInfoReader::patchtype{$patch} . "\n";
			$patchtexts .= "Severity:    " . $UpdateInfoReader::patchseverity{$patch} . "\n";
			$patchtexts .= "References:  " . join(",",sort keys %{$UpdateInfoReader::patchreferences{$patch}}) . "\n";
			push @allrefs, keys %{$UpdateInfoReader::patchreferences{$patch}};
			$patchtexts .= $UpdateInfoReader::patchdescription{$patch} . "\n";
		}
		foreach my $prevcontainer (keys %affectedby) {
			foreach my $reference (@allrefs) {
				next unless ($reference =~ /CVE-/);

				my %details = ();

				my %cvssv3 = ();
				if (defined($SMASHData::cvssv3{$reference})) {
					%cvssv3 = %{$SMASHData::cvssv3{$reference}};
				}
				my %cvss = ();
				my $vector = "none";
				my $score = "0";

				if (defined($cvssv3{'SUSE'})) { %cvss = %{$cvssv3{'SUSE'}}; }
				if (defined($cvssv3{'National Vulnerability Database'})) { %cvss = %{$cvssv3{'National Vulnerability Database'}}; }

				if (defined($cvss{'base_vector'})) { $vector = $cvss{'base_vector'}; }
				if (defined($cvss{'base_score'})) { $score = $cvss{'base_score'}; }

				$details{"base_vector"} = $vector;
				$details{"base_score"} = $score;

				$affectedby{$prevcontainer}->{"unfixedcves"}->{$reference} = \%details;
			}
		}

		my $ctype = "Container";
		my $id = get_id($outputprefix);

		if ($id =~ /CU/) {
			$ctype = "Container";
			print OUTPUTNEWPATCHES "SUSE $ctype Update Advisory: $containername\n";
			print OUTPUTNEWPATCHES "-----------------------------------------------------------------\n";
			print OUTPUTNEWPATCHES "Container Advisory ID : " . get_id($outputprefix) . "\n";
			print OUTPUTNEWPATCHES "Container Tags        : " . join(" , ", sort @tags) . "\n";
			print OUTPUTNEWPATCHES "Container Release     : " . $containerinfo{'release'} . "\n";
			print OUTPUTNEWPATCHES "Severity              : $severity\n";
			print OUTPUTNEWPATCHES "Type                  : $type\n";
		} else {
			$ctype = "Image";
			print OUTPUTNEWPATCHES "SUSE Image Update Advisory: $containername\n";
			print OUTPUTNEWPATCHES "-----------------------------------------------------------------\n";
			print OUTPUTNEWPATCHES "Image Advisory ID : " . get_id($outputprefix) . "\n";
			print OUTPUTNEWPATCHES "Image Tags        : " . join(" , ", sort @tags) . "\n";
			print OUTPUTNEWPATCHES "Image Release     : " . $containerinfo{'release'} . "\n";
			print OUTPUTNEWPATCHES "Severity          : $severity\n";
			print OUTPUTNEWPATCHES "Type              : $type\n";
		}

		# wrap the long reference lines 
		my @wrappedrefs = ();
		my $reftext = "";

		@allrefs = sort @allrefs;
		foreach my $ref (@allrefs) {
			$reftext .= "$ref ";
			if (length($reftext) > 60) {
				$reftext =~ s/ $//; # delete final space again
				push @wrappedrefs,$reftext;
				$reftext = "";
			}
		}
		if ($reftext ne "") {
			push @wrappedrefs,$reftext;
		}

		if ($ctype eq "Container") {
			print OUTPUTNEWPATCHES "References            : " . join("\n" . " " x 24,@wrappedrefs) . "\n";
		} else { # Image
			print OUTPUTNEWPATCHES "References        : " . join("\n" . " " x 24,@wrappedrefs) . "\n";
		}

		print OUTPUTNEWPATCHES "-----------------------------------------------------------------\n";
		print OUTPUTNEWPATCHES "\n";
		print OUTPUTNEWPATCHES "The container $containername was updated. The following patches have been included in this update:\n";
		print OUTPUTNEWPATCHES "\n";
		print OUTPUTNEWPATCHES $patchtexts;
		if ($outputpkgchange ne "") {
			print OUTPUTNEWPATCHES "\n";
			print OUTPUTNEWPATCHES "The following package changes have been done:\n\n$outputpkgchange";
		}
		close(OUTPUTNEWPATCHES)||die "$outputbase/$outputprefix.newpatches.txt.new:$!\n";

		#Create json output form the header
		my $json = {
                        container_name => $containername,
                        ctype          => $ctype,
                        type           => $type,
                        advisory_id    => get_id($outputprefix),
                        tags           => \@tags,
                        release        => $containerinfo{'release'},
                        severity       => $severity,
                        references     => \@allrefs
                };
		open(OUTPUTJSON, ">$outputbase/$outputprefix.newpatches.json.new");
		print OUTPUTJSON $jsoncoder->encode($json);
		close(OUTPUTJSON) ||die "$outputbase/$outputprefix.newpatches.json.new:$!\n";

		push @alljsons,$json;

		if (! -f "$outputbase/$outputprefix.newpatches.txt") {
			if (!$first && $havenewpatches && defined($containername) && ($containername ne "")) {
				if ((get_id($outputprefix) !~ /2019/) && $sendemail) { # do not announce the 2019 container revisions


					# open(SENDMAIL,"|/usr/sbin/sendmail -r meissner\@suse.de -oi -t");
					# print SENDMAIL "From: sle-updates\@lists.suse.com\n";
					# print SENDMAIL "To: sle-updates\@lists.suse.com\n";
					# print SENDMAIL "Approved: RVJbrjwXs9JSThsbF3C6\n";
					# print SENDMAIL "Subject: " . get_id($outputprefix) . ": \u$type update of $containername\n";
					# print SENDMAIL "\n";
					# open(NEWPATCH,"<$outputbase/$outputprefix.newpatches.txt.new");
					# while (<NEWPATCH>) { print SENDMAIL $_; }
					# close(NEWPATCH);
					# close(SENDMAIL);

					open(SENDMAIL,"|/usr/sbin/sendmail -r meissner\@suse.de -oi -t");
					print SENDMAIL "From: sle-container-updates\@lists.suse.com\n";
					print SENDMAIL "To: sle-container-updates\@lists.suse.com\n";
					print SENDMAIL "Approved: atheuY7hmeGhi5Ug\n";
					print SENDMAIL "Subject: " . get_id($outputprefix) . ": \u$type update of $containername\n";
					print SENDMAIL "\n";
					open(NEWPATCH,"<$outputbase/$outputprefix.newpatches.txt.new");
					while (<NEWPATCH>) { print SENDMAIL $_; }
					close(NEWPATCH);
					close(SENDMAIL);

					open(SENDMAIL,"|/usr/sbin/sendmail -r meissner\@suse.de -oi -t");
					print SENDMAIL "From: meissner\@suse.com\n";
					print SENDMAIL "To: proj-sle-container-up-aaaaespeftw2aeezjmny5zdtyi\@suse.slack.com\n";	# slack#proj-sle-container-updates
					print SENDMAIL "Subject: " . get_id($outputprefix) . ": \u$type update of $containername\n";
					print SENDMAIL "\n";
					open(NEWPATCH,"<$outputbase/$outputprefix.newpatches.txt.new");
					while (<NEWPATCH>) { print SENDMAIL $_; }
					close(NEWPATCH);
					close(SENDMAIL);

					# if ($type eq "security") {
					# 	open(SENDMAIL,"|/usr/sbin/sendmail -r meissner\@suse.de -oi -t");
					# 	print SENDMAIL "From: sle-security-updates\@lists.suse.com\n";
					# 	print SENDMAIL "To: sle-security-updates\@lists.suse.com\n";
					# 	print SENDMAIL "Approved: ecYV8VNza9dn4p2pCDSp\n";
					# 	print SENDMAIL "Subject: " . get_id($outputprefix) . ": \u$type update of $containername\n";
					# 	print SENDMAIL "\n";
					# 	open(NEWPATCH,"<$outputbase/$outputprefix.newpatches.txt.new");
					# 	while (<NEWPATCH>) { print SENDMAIL $_; }
					# 	close(NEWPATCH);
					# 	close(SENDMAIL);
					# }
					$mailsent++;
					die "too many mails sent" if ($mailsent > 20);
				} else {
					print STDERR "skipped email: getid not 2019, sendemail=$sendemail\n";
				}
			} else {
				print STDERR "skipped notify email: first=$first, havenewpatches=$havenewpatches, containername=$containername\n";
			}
		}

		if (system("diff -u $outputbase/$outputprefix.newpatches.txt $outputbase/$outputprefix.newpatches.txt.new")) {
			rename("$outputbase/$outputprefix.newpatches.txt.new","$outputbase/$outputprefix.newpatches.txt")||die "rename $outputbase/$outputprefix.newpatches.txt.new $outputbase/$outputprefix.newpatches.txt:$!";
		} else {
			unlink("$outputbase/$outputprefix.newpatches.txt.new");
		}
		if (system("diff -u $outputbase/$outputprefix.newpatches.json $outputbase/$outputprefix.newpatches.json.new")) {
			rename("$outputbase/$outputprefix.newpatches.json.new","$outputbase/$outputprefix.newpatches.json")||die "rename $outputbase/$outputprefix.newpatches.json.new $outputbase/$outputprefix.newpatches.json:$!";
		} else {
			unlink("$outputbase/$outputprefix.newpatches.json.new");
		}
	}

	# Incremental HTML generation
	if ($incremental) {
		my $type = "recommended";
		my $severity = "low";

		my $patchtexts = "";
		my @allrefs = ();
		foreach my $patch (sort { $UpdateInfoReader::patchissued{$a} <=> $UpdateInfoReader::patchissued{$b} } keys %patches) {
			next if (defined($prevpatcheshtml{$patch}));
			$prevpatcheshtml{$patch} = 1;
			my $susesa = $patch;
			my $prefix = $UpdateInfoReader::patchtype{$patch};
			$prefix = "SU" if ($prefix eq "security");
			$prefix = "RU" if ($prefix eq "recommended");
			$prefix = "OU" if ($prefix eq "optional");

			$type = "security" if ($UpdateInfoReader::patchtype{$patch} eq "security");

			$severity = "critical" if ($UpdateInfoReader::patchseverity{$patch} eq "critical");
			$severity = "important" if (($UpdateInfoReader::patchseverity{$patch} eq "important") && ($severity ne "critical"));
			$severity = "moderate" if (($UpdateInfoReader::patchseverity{$patch} eq "moderate") && ($severity ne "critical") && ($severity ne "important"));

			# synthesize the advisory id.
			$susesa =~ s/SUSE-(\d*)-(\d*)/SUSE-$prefix-$1:$2-1/;

			$patchtexts .= "<hr>\n";
			$patchtexts .= "<table border=\"1\">\n";
			#$patchtexts .= "Patch:       $patch\n";
			$patchtexts .= "<tr><td>Advisory ID</td><td>$susesa\n";
			$patchtexts .= "<tr><td>Released</td><td>" . localtime($UpdateInfoReader::patchissued{$patch}) . "</td></tr>\n";
			$patchtexts .= "<tr><td>Summary</td><td>" . $UpdateInfoReader::patchtitle{$patch} . "</td></tr>\n";
			$patchtexts .= "<tr><td>Type</td><td>" . $UpdateInfoReader::patchtype{$patch} . "</td></tr>\n";
			$patchtexts .= "<tr><td>Severity</td><td>" . $UpdateInfoReader::patchseverity{$patch} . "</td></tr>\n";
			$patchtexts .= "<tr><td>References</td><td>" . join(",",sort keys %{$UpdateInfoReader::patchreferences{$patch}}) . "</td></tr>\n";
			push @allrefs, sort keys %{$UpdateInfoReader::patchreferences{$patch}};
			$patchtexts .= "</table>\n";
			$patchtexts .= "Description:<p>\n\n" . txt2html($UpdateInfoReader::patchdescription{$patch}) . "\n";
		}
		$htmlcontent{$outputprefix} .= "<hr/>\n";
		$htmlcontent{$outputprefix} .= "<h2>" . get_id($outputprefix) . "</h2>\n";
		$htmlcontent{$outputprefix} .= "<table border=\"1\">\n";
		$htmlcontent{$outputprefix} .= "<tr><td>Container Advisory ID</td><td>" . get_id($outputprefix) . "</td></tr>\n";
		$htmlcontent{$outputprefix} .= "<tr><td>Container Tags</td><td>" . join(" , ", sort @tags) . "</td></tr>\n";
		$htmlcontent{$outputprefix} .= "<tr><td>Container Release</td><td>" . $containerinfo{'release'} . "</td></tr>\n";
		#$htmlcontent{$outputprefix} .= "<tr><td>Severity</td><td>$severity</td></tr>\n";
		#$htmlcontent{$outputprefix} .= "<tr><td>Type</td><td>$type</td></tr>\n";
		#$htmlcontent{$outputprefix} .= "<tr><td>References</td><td>" . join(" ",@allrefs) . "</td></tr>\n";
		$htmlcontent{$outputprefix} .= "</table>\n";

		$htmlcontent{$outputprefix} .= "The following patches have been included in this update:\n";
		$htmlcontent{$outputprefix} .= $patchtexts;

		$containertag{$outputprefix} = $containername;
	}
}

# find out all CVEs vs package-version-release from a project

# parse http://download.suse.de/ibs/$project/repodata/*updateinfo.xml?

sub my_parse_update($$$) {
	my ($project,$upd, $package2fixes) = @_;

	#print Dumper(\$upd);

	my %cve = ();
	my $havecve = 0;

	if (defined($upd->{'references'}->{'reference'})) {
		#   <reference href="https://bugzilla.novell.com/show_bug.cgi?id=815236" id="815236" title="bug number 815236" type="bugzilla"/>
		#   <reference href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-2944" id="CVE-2013-2944" title="CVE-2013-2944" type="cve"/>
		my $arr = XML::Bare::forcearray($upd->{'references'}->{'reference'});

		foreach my $ref (@{$arr}) {
			if ($ref->{'type'}->{'value'} eq "cve") {
				$havecve = 1;
				my $cve = $ref->{'id'}->{'value'};
				$cve =~ s/^\s*//;
				$cve =~ s/\s*$//;
				$cve =~ s/:$//;
				if ($cve =~ /^201/) {
					print STDERR "adjusting CVE id $cve\n" if (-t STDERR);
					$cve{"CVE-$cve"} = 1;
				} else {
					unless ($cve =~ /^CVE-\d\d\d\d-\d\d\d\d\d*$/) {
						print STDERR "incorrect CVE name $cve\n" if (-t STDERR);
						next;
					}
					$cve{$cve} = 1;
				}
			}
			#if ($ref->{'type'}->{'value'} eq "bugzilla") {
			#	$cve{$ref->{'id'}->{'value'}} = 1;
			#}
			#print "\treference $j: id=" . $ref->{'id'}->{'value'} . ", type=" . $ref->{'type'}->{'value'} . "\n";
		}
	} else {
		#print STDERR "no references in " . $upd->{'title'}->{'value'} . "\n" if (-t STDERR);
		return;
	}

	return if (keys %cve == 0);

	if (defined($upd->{'pkglist'}->{'collection'}->{'package'})) {
		my $arr = XML::Bare::forcearray($upd->{'pkglist'}->{'collection'}->{'package'});
		foreach my $pkg (@{$arr}) {
			my $xpkg = $pkg->{'name'}->{'value'};
			my $xversionrelease = $pkg->{'version'}->{'value'} . "-" . $pkg->{'release'}->{'value'};

			#print "\tpackage $j name=" . $pkg->{'name'}->{'value'} . ", version=" . $pkg->{'version'}->{'value'} . " - " . $pkg->{'release'}->{'value'} . "." . $pkg->{'arch'}->{'value'} ."\n";

			my @cve = sort keys %cve;
			$package2fixes->{$xpkg}->{$xversionrelease} = \@cve;
		}
	} else {
		die "no pkglist?\n";
	}
}

sub allupdates($) {
	my ($project) = @_;

	if (defined($project2package2fixes{$project})) {
		# we fetched it already
		return;
	}

	my %package2fixes = ();
	my %package2patches = ();

	# First fetch the whole update codestream updateinfo.
	my $xproj = $project;
	$xproj =~ s/:/:\//g;

	my $uiurl;

	my $xml = &UpdateInfoReader::get_updateinfo_from_host("/$xproj/standard/","updateinfo","$baseurl");

        if (!defined($xml)) {
		print STDERR "no updateinfo in $baseurl/$xproj/standard/repodata/repomd.xml?\n";
		$project2package2fixes{$project} = {};
		return;
        }

	print STDERR "reading updateinfo for $project\n";
	&UpdateInfoReader::parse_updateinfo($project,$xml,0);

	# flip the data structure so we can use it better.

	if (!defined($UpdateInfoReader::patches{$project})) {
		warn "no security patches read from $project\n";
		return;
	}
	foreach my $patch (keys %{$UpdateInfoReader::patches{$project}}) {
		my %packages = %{$UpdateInfoReader::patchpackages{$patch}};
		my @cves = sort keys %{$UpdateInfoReader::patchreferences{$patch}};

		my $package;
		my $version;

		foreach my $package (keys %packages) {
			$version = $packages{$package};

			$package2patches{$package}->{$version} = $patch;
			$package2fixes{$package}->{$version} = \@cves;
		}
	}

	# Parse to XML

        #my $xmlparser = new XML::Bare( text => $xml ) || die "XML: $xml";
        #my $xmlroot = $xmlparser->parse()||die;
        #my $updates = XML::Bare::forcearray($xmlroot->{'updates'}->{'update'});
	#
	#foreach my $update (@{$updates}) {
	#	my_parse_update($project,$update,\%package2fixes);
        #}
	#print Dumper(\%package2fixes);
	$project2package2fixes{$project} = \%package2fixes;
	$project2package2patches{$project} = \%package2patches;
}

read_updateids();

my @images = ();

open(CONTAINERPROJECTS,"$osc ls |grep ^SUSE:Containers:|")||die "$osc ls |grep ^SUSE:Containers|: $!\n";
while (my $containerproject = <CONTAINERPROJECTS>) {
	chomp $containerproject;

	# BETA... REMOVE AFTER SHIPMENT
	#next if ($containerproject eq "SUSE:Containers:SES:7");
	
	# EOL
	next if ($containerproject eq "SUSE:Containers:SUSE-CAASP:2");
	next if ($containerproject eq "SUSE:Containers:SUSE-CAASP:3");
	next if ($containerproject eq "SUSE:Containers:SLE-SERVER:12");
	next if ($containerproject eq "SUSE:Containers:SUSE-CAASP:5"); # was renamed 

	my %containers = ();
	open(CONTAINERS,"$osc ls $containerproject|")|| die"$osc ls $containerproject:$!\n";
	while (my $container = <CONTAINERS>) {
		chomp $container;

		next if ($container =~ /^15/); # weird 15_0 entries
		next if ($container =~ /^patchinfo/); # no patchinfos here
		next if ($container =~ /^kured/); # no kured package
		next if ($container eq "sles12sp4-image"); # sles12sp4-image without timestamp

		if ($container =~ /^(.*)\.(\d*)$/) {	# usualy container-image.timestamp entries, translate to 
			$containers{"^$1\\.(.*)"} = $1;

			print STDERR "container $container matched into $1\n";
		} else {
			print STDERR "non timestamped container $containerproject $container?\n" if -t STDERR;
			$containers{$container} = $container;
		}
	}
	close(CONTAINERS);
	foreach my $container (sort keys %containers) {
		print STDERR "added $containerproject $container containers x86_64,$containers{$container}\n" ; #if -t STDERR;
		push @images,"$containerproject $container containers x86_64,$containers{$container}";
	}
}
close(CONTAINERPROJECTS);

open(CONTAINERPROJECTS,"$osc ls |grep ^SUSE.*PubCloud.*Released|")||die "$osc ls |grep SUSE..PubCloud.*Released|: $!\n";
while (my $containerproject = <CONTAINERPROJECTS>) {
	chomp $containerproject;

	my %containers = ();
	open(CONTAINERS,"$osc pr --xml $containerproject -r images -a x86_64|")|| die"$osc ls $containerproject:$!\n";

	while (my $container = <CONTAINERS>) {
		chomp $container;

	    	# <status package="SLES15-SP2-BYOS.20200901140232" code="disabled"/>
		# <status package="SLES15-SP2-BYOS.20200901140232:EC2-HVM" code="disabled"/>

		next unless ($container =~ /package="([^"]*)"/);

		my $package = $1;

		if ($package =~ /^(.*)\.(\d*)$/) {	# usually container-image.timestamp entries
			$containers{"$1\\.(.[^:]*)"} = $1;	# avoid matching multbuild containers
			next;
		}

		if ($package =~ /^(.*)\.(\d*):(.+)$/) {	# usually container-image.timestamp:multibuild entries
			$containers{"$1\\.(.*):$3"} = "$1:$3";
			next;
		}

		print STDERR "non timestamped container $containerproject $package?\n" if -t STDERR;
		# $containers{"$package\$"} = $package;
	}
	close(CONTAINERS);
	foreach my $container (sort keys %containers) {
		print STDERR "added $containerproject $container images x86_64,$containers{$container}\n" ;#if -t STDERR;
		push @images,"$containerproject $container images x86_64,$containers{$container}" if ($container =~ /CHOST/);
	}
	# added SUSE:SLE-15-SP2:Update:PubClouds:Released SLES15-SP2.*:GCE images x86_64,SLES15-SP2:GCE

}
close(CONTAINERPROJECTS);

# read some projects in a wildcard fashion
open(IMAGEPROJECTS,"<data/imageprojects") ||die "data/imageprojects: $!";
while (<IMAGEPROJECTS>) {
	chomp;
	my ($project, $repo, $arch) = split(/ /);
	# isc pr SUSE:SLE-15-SP1:Update:PubClouds -r images -a x86_64 --xml|grep succeeded

		print STDERR "$osc pr $project -r $repo -a $arch --xml\n" if -t STDERR;

	open(IMAGES,"$osc pr $project -r $repo -a $arch --xml|");
	while (<IMAGES>) {
		#print STDERR "debug: $_" if -t STDERR;
		chomp;
		next unless /succeeded/;

		#<status package="SLES15-SP1-Manager-4-0-EC2-HVM-BYOS:Server" code="succeeded"/>
		if (/status package="([^"]*)" code/) {
			my $pkg = $1;
			my $name = $1;

			$name =~ s/:/-/; # avoid :
			push @images,"$project $pkg $repo $arch,$name";
			print STDERR "added $project $pkg $repo $arch,$name\n" if -t STDERR;
		}

	}
	close(IMAGES);
}
close(IMAGEPROJECTS);

# rest projects we read by hand
open(IMAGES,"data/images")||die  "data/images: $!";
while (my $image = <IMAGES>) {
	chomp $image;
	push @images,$image;
}
close(IMAGES);

# Pubcloud stuff via pint info...

my @pintimages = <smash/pint/*>;

foreach my $image (@pintimages) {
	print STDERR "pint image: $image\n";

	next if ($image =~/CHOST.*Ali/);	# has a duplicate name compared to amazon.

	my @images = ();

	@allimages = <$image/*>;

	unless (@allimages) {
		print STDERR "empty image dir for $image\n";
		next;
	}

	# only do CHOST for now
	next unless ($image =~ /CHOST/i);

	my $lastimage;

	foreach my $ximage (sort @allimages) {
		chomp $ximage;
		#print STDERR "checking $ximage\n";
		next unless ($ximage =~ /\/[^\/]*$/);

		push @images,$ximage;
		$lastimage = $ximage;
	}
	%prevpackages = ();
	%prevpatches = ();
	%prevpatcheshtml = ();

	$lastimage =~ /\/([^\/]*)$/;

	my $output = $1;

	if (-f "$outputbase/$output.newpatches.txt") {
		print STDERR "already generated all for $output container...\n";
		next;
	}

	%htmlcontent = ();
	my $first = 1;
	foreach my $ximage (@images) {
		next unless ($ximage =~ /\/([^\/]*)$/);

		my $index = $1;

		my $version = "unknown";
		if ($ximage =~ /-v(\d*)/) {
			$version = $1;
			print STDERR "version is $version\n";
		}

		open(BUILDINFO,"<$ximage") || die "$ximage:$!";
		my @text = <BUILDINFO>;
		close(BUILDINFO);

		print STDERR "$ximage\n";

		# we synthesize a container info out of the internal name: version
		parse_builddep($index,"{\"tags\":[\"$index:$version\"]}",join("\n",@text), $index,1,$version,$first,1);
		$first = 0;
	}

	# write out all HTML in 1 file
	open(OUTPUTHTML,">$outputbase/$output.html");

	print OUTPUTHTML "<html><head><title>Image summary for $output</title></head>\n";
	print OUTPUTHTML "<body>\n";
	print OUTPUTHTML "<h1>Image summary for $output</h1>\n";

	foreach my $container (reverse sort keys %htmlcontent) {
		print OUTPUTHTML $htmlcontent{$container};
	}

	print OUTPUTHTML "</body></html>\n";
	close(OUTPUTHTML);

	#$allcontainers{$output} = $containertag;
}

# Quarterly Updates
foreach my $image ("SUSE:SLE-15-SP5") {
	print STDERR "QU image: $image\n";

	my $lastimage = "$image:GA";
	my @quimages = ($lastimage);

	open(OSC,"$osc ls|grep \"^$image:Update:QR:QU[1-9]\"|");
	# note all of them are created before usage, so are still empty ...
	while (<OSC>) {
		print STDERR $_;
		chomp;
		push @quimages, $_;
		$lastimage = $_;
	}
	# push as last also the current build.
	push @quimages, "$image:Update:QR";
	close(OSC);

	%prevpackages = ();
	%prevpatches = ();
	%prevpatcheshtml = ();

	my $output = $lastimage;

	if (-f "$outputbase/$output.newpatches.txt") {
		print STDERR "already generated all for $output container...\n";
		# next;
	}

	%htmlcontent = ();
	my $first = 1;
	my $index = 0;
	foreach my $ximage (@quimages) {
		#if ($ximage =~ /:QU(\d*)$/) {
		#	$index = $1;
		#} else {
		#	$index = 0; # GA
		#}
		my $version = $index;

		my $ximagefn = "$cverepobase/smash/buildinfo/$ximage/unknown";

		my @text;
		if (open(BUILDINFO,"<$ximagefn")) {
			@text = <BUILDINFO>;
			close(BUILDINFO);
		} else {
			next;
		}

		print STDERR "QU: $ximagefn, ximage: $ximage, index: $index\n";

		# we synthesize a container info out of the internal name: version
		parse_builddep($index,"{\"tags\":[\"$image:$index\"]}",join("\n",@text),$ximage,1,$version,$first,0);
		$first = 0;
		$index++;
	}

	# write out all HTML in 1 file
	open(OUTPUTHTML,">$outputbase/$output.html");

	print OUTPUTHTML "<html><head><title>Image summary for $image quarterly updates</title></head>\n";
	print OUTPUTHTML "<body>\n";
	print OUTPUTHTML "<h1>Image summary for $output</h1>\n";

	foreach my $container (reverse sort keys %htmlcontent) {
		print OUTPUTHTML $htmlcontent{$container};
	}

	print OUTPUTHTML "</body></html>\n";
	close(OUTPUTHTML);
}

# Service Packs
foreach my $image ("SUSE:SLE-15-SP6:GA") {
	print STDERR "SP image: $image\n";

	%prevpackages = ();
	%prevpatches = ();
	%prevpatcheshtml = ();

	if (-f "$outputbase/$image.newpatches.txt") {
		print STDERR "already generated all for $image container...\n";
		# next;
	}

	%htmlcontent = ();
	my $ximagefn = "$cverepobase/smash/buildinfo/$image/unknown";

	my @text;
	if (open(BUILDINFO,"<$ximagefn")) {
		@text = <BUILDINFO>;
		close(BUILDINFO);
	}
	# we synthesize a container info out of the internal name: version
	parse_builddep(0,"{\"tags\":[\"$image\"]}",join("\n",@text),$image,1,"xversion",0,0);
}

print STDERR "images:\n" . join("\n", @images);

foreach my $image (@images) {
	my ($buildinfo,$output) = split(/,/,$image);

	my ($project,$package,$repo,$arch) = split(/ /,$buildinfo);

	my $filename;
	my $containerinfo;

	# added SUSE:SLE-15-SP2:Update:PubClouds:Released SLES15-SP2.*:GCE images x86_64,SLES15-SP2:GCE

	if ($package =~ /\.[*(]/) {	# contains .* or .(
		my @images = ();


		#open(ALLIMAGES,"$osc ls $project|");
		my @allimages = UpdateInfoReader::cached_osc_call("pr --xml $project -r $repo -a $arch");

		@alljsons = ();
		%prevpackages = ();
		%prevpatches = ();
		%prevpatcheshtml = ();

		%affectedby = ();	# track what old containers are affected by

		my $lastimage;

		foreach my $ximage (@allimages) {
			chomp $ximage;

			next unless ($ximage =~ /status package="([^"]*)" code/);

			$ximage = $1;

			next unless ($ximage =~ /$package$/);

			push @images,$ximage;
			$lastimage = $ximage;
		}

		$lastimage =~ /$package$/;

		my $index = $1;

		print STDERR "extracted timestamp $index from $lastimage\n";
		print STDERR "project $project\n";

		my $prefix = "";
		$prefix ="caasp4/"	if ($project =~ /SUSE:Containers:SUSE-CAASP:4$/);
		$prefix ="caasp45/"	if ($project =~ /SUSE:Containers:SUSE-CAASP:4.5$/);

		if (-f "$outputbase/$prefix$output.$index.newpatches.txt") {
			print STDERR "already generated all for $output container (last index is $index)...\n";
			next;
		}
		%htmlcontent = ();
		my $containertag = "unknown";
		my $first = 1;
		foreach my $ximage (@images) {
			next unless ($ximage =~ /$package$/);
			my $index = $1;

			print STDERR "$osc api /build/$project/$repo/$arch/$ximage\n" if -t STDERR;

			undef $containerinfo;
			my @lines = UpdateInfoReader::filecached_osc_call("api /build/$project/$repo/$arch/$ximage");
			foreach (@lines) {
				if (/<binary filename="(.*report)" size=/) {
					$filename = $1;
				}
				if (/<binary filename="(.*containerinfo)" size=/) {
					$containerinfo = $1;
				}
			}
			next unless defined($filename);	# empty release (happens in public cloud

			print STDERR "filename of $buildinfo is $filename\n" if -t STDERR;
			print STDERR "filename of containerinfo is $containerinfo\n" if -t STDERR;

			my $version = "unknown";
			if ($filename =~ /\.$arch-(.*).report$/) {
				$version = $1;
				print STDERR "version is $version\n" if -t STDERR;
			}

			# get the file
			my @text = UpdateInfoReader::filecached_osc_call("api /build/$project/$repo/$arch/$ximage/$filename");

			print STDERR "$buildinfo $ximage\n" if -t STDERR;

			my @cinfo;
			if (defined($containerinfo)) {
				# get containerinfo ... it is a perl map already, so use it....
				@cinfo = UpdateInfoReader::filecached_osc_call("api /build/$project/$repo/$arch/$ximage/$containerinfo");
				my $contref = decode_json(join("",@cinfo));
				$affectedby{"$prefix$output.$index"}->{"containerinfo"} = $contref;	# for registry.suse.com usage to associate
			}
			parse_builddep($buildinfo,join("",@cinfo),join("\n",@text), "$prefix$output.$index",1,$version,$first,1);
			my %affected = ();

			$affectedby{"$prefix$output.$index"}->{"unfixedcves"} = \%affected;	# start empty, fill in the incremental runs.
			$first = 0;
			$containertag = $containertag{"$output.$index"};
		}
		# write out all HTML in 1 file
		open(OUTPUTHTML,">$outputbase/$prefix$output.html");

		print OUTPUTHTML "<html><head><title>Container summary for $containertag</title></head>\n";
		print OUTPUTHTML "<body>\n";
		print OUTPUTHTML "<h1>Container summary for $containertag</h1>\n";

		foreach my $container (reverse sort keys %htmlcontent) {
			print OUTPUTHTML $htmlcontent{$container};
		}

		print OUTPUTHTML "</body></html>\n";
		close(OUTPUTHTML);

		$allcontainers{$output} = $containertag;
		$alljsons{$output} = \@alljsons;

		foreach my $affectedcontainer (sort keys %affectedby) {
			open(OUTPUTJSON,">$outputbase/$affectedcontainer.xjson");
			print OUTPUTJSON $jsoncoder->encode($affectedby{$affectedcontainer});
			close(OUTPUTJSON);
		}
	} else {
		@alljsons = ();
		print STDERR "$osc api /build/$project/$repo/$arch/$package\n" if -t STDERR;

		undef $containerinfo;
		# unversioned, so read it always
		open(BUILDINFO,"$osc api /build/$project/$repo/$arch/$package|");
		while (<BUILDINFO>) {
			if (/<binary filename="(.*report)" size=/) {
				$filename = $1;
			}
			if (/<binary filename="(.*containerinfo)" size=/) {
				$containerinfo = $1;
			}
		}
		close(BUILDINFO);
		print STDERR "filename of $buildinfo is $filename\n" if -t STDERR;

		# get the file ... filename should be unique I hope, so can be cached.
		my @text = UpdateInfoReader::filecached_osc_call("api /build/$project/$repo/$arch/$package/$filename");

		#SLES15-SP1-CHOST-BYOS.x86_64-1.0.5-GCE-Build2.3.report

		my $version = "unknown";
		if ($filename =~ /\.$arch-(.*).report$/) {
			$version = $1;
			print STDERR "version is $version\n" if -t STDERR;
		}

		# save a copy of the buildinfo
		mkdir("$cverepobase/smash/buildinfo/$package");
		open(SAVEDBUILDINFO,">$cverepobase/smash/buildinfo/$package/$version.new");
		print SAVEDBUILDINFO @text;
		close(SAVEDBUILDINFO);
		if (-f "$cverepobase/smash/buildinfo/$package/$version.new") {
			if (!system("diff -u $cverepobase/smash/buildinfo/$package/$version $cverepobase/smash/buildinfo/$package/$version.new")) {
                                unlink("$cverepobase/smash/buildinfo/$package/$version.new");
			} else {
				rename("$cverepobase/smash/buildinfo/$package/$version.new","$cverepobase/smash/buildinfo/$package/$version");
			}
		} else {
			rename("$cverepobase/smash/buildinfo/$package/$version.new","$cverepobase/smash/buildinfo/$package/$version");
		}

		my @cinfo;
		if (defined($containerinfo)) {
			# get containerinfo ... it is a perl map already, so use it....
			@cinfo = UpdateInfoReader::filecached_osc_call("api /build/$project/$repo/$arch/$package/$containerinfo");
		}

		print STDERR "$buildinfo $image\n" if -t STDERR;

		%prevpatches = ();
		%prevpatcheshtml = ();
		parse_builddep($buildinfo,join("",@cinfo),join("\n",@text), $output,0,$version,0,0);

		push @allimages,"<li>$output <a href=\"$output.patches.txt\">patches</a> <a href=\"$output.cves.txt\">CVEs</a></li>\n"
	}
}

# get ids assigned for all not yet handled pints images.
foreach my $pint (sort keys %PInt::image2url) {
	next if ($pint =~ /chost/i);	# chost should be handled above, avoid skipping them if the buildinfo is not there yet
	get_id($pint);
}


open(OUTPUTHTML,">$outputbase/__index.html");
print OUTPUTHTML "<head><title>Container Update Information</title>\n";
print OUTPUTHTML "<body>\n";
print OUTPUTHTML "<h1>Container Update Information</h1>\n";

# first SLE base container
print OUTPUTHTML "<h2>SUSE Linux Enterprise Base Container</h2>\n";
print OUTPUTHTML "<ul>\n";
foreach my $cname (sort keys %allcontainers) {
	next unless ($cname =~ /^sles[^-]*-image/);
	print OUTPUTHTML "<li><a href=\"$cname.html\">" . $allcontainers{$cname} . "</a></li>\n";
	delete $allcontainers{$cname};
}
print OUTPUTHTML "</ul>\n";

print OUTPUTHTML "<h2>SUSE CaaSP v4 Containers</h2>\n";
print OUTPUTHTML "<ul>\n";
foreach my $cname (sort keys %allcontainers) {
	next unless ($allcontainers{$cname} =~ /caasp\/v4/);
	print OUTPUTHTML "<li><a href=\"$cname.html\">" . $allcontainers{$cname} . "</a></li>\n";
	delete $allcontainers{$cname};
}
print OUTPUTHTML "</ul>\n";
print OUTPUTHTML "<h2>SUSE Enterprise Storage 6 (Beta) Containers</h2>\n";

print OUTPUTHTML "<ul>\n";
foreach my $cname (sort keys %allcontainers) {
	next unless ($allcontainers{$cname} =~ /ses\/6/);
	print OUTPUTHTML "<li><a href=\"$cname.html\">" . $allcontainers{$cname} . "</a></li>\n";
	delete $allcontainers{$cname};
}
print OUTPUTHTML "</ul>\n";

print OUTPUTHTML "<h2>SUSE Enterprise Storage 7 Containers</h2>\n";

print OUTPUTHTML "<ul>\n";
foreach my $cname (sort keys %allcontainers) {
	next unless ($allcontainers{$cname} =~ /ses\/7/);
	print OUTPUTHTML "<li><a href=\"$cname.html\">" . $allcontainers{$cname} . "</a></li>\n";
	delete $allcontainers{$cname};
}
print OUTPUTHTML "</ul>\n";

print OUTPUTHTML "<h2>Other Containers</h2>\n";

print OUTPUTHTML "<ul>\n";
foreach my $cname (sort keys %allcontainers) {
	print OUTPUTHTML "<li><a href=\"$cname.html\">" . $allcontainers{$cname} . "</a></li>\n";
}
print OUTPUTHTML "</ul>\n";

print OUTPUTHTML "<h2>Images</h2>\n";
print OUTPUTHTML "<ul>\n";
print OUTPUTHTML join("\n", @allimages);
print OUTPUTHTML "</ul>\n";

print OUTPUTHTML "</body>\n";
print OUTPUTHTML "</html>\n";
close(OUTPUTHTML);

foreach my $cname (sort keys %alljsons) {
	open(OUTPUTJSON,">$outputbase/json-summary-$cname.xjson");
	print OUTPUTJSON $jsoncoder->encode($alljsons{$cname});
	close(OUTPUTJSON);
}

print "SUCCESS\n";
1;
