#! /usr/bin/perl -w

# (C) 2007, 2009  Martin Koeppe

use strict;
use File::Find;
use File::Basename;
use POSIX qw(strftime);
use CGI;
#use CGI::Pretty;
#use CGI::Carp 'fatalsToBrowser';
no locale;

my $kbtitles_file = "kbtitles";
my $kbfiles_file = "kbfiles";
my $outfile = "index.html";

my %kbtitles;

my %kbfiles; # kb -> {interix -> {os -> {file -> ver}}}
my %fileskb; # interix -> {file -> {os -> [newest-ver, kb]}}
my %osfixes; # os -> {kb -> 1}
my %itxnames = (
    "35"	=> "SfU 3.5 32bit",
    "52"	=> "SUA 5.2 32bit",
    "52x"	=> "SUA 5.2 x64",
    "52i"	=> "SUA 5.2 IA64",
    "60"	=> "SUA 6.0 32bit",
    "60x"	=> "SUA 6.0 x64",
    "60i"	=> "SUA 6.0 IA64",
    "61"	=> "SUA 6.1 32bit",
    "61x"	=> "SUA 6.1 x64",
    "61i"	=> "SUA 6.1 IA64",
);
my %itxosnames = (
    "35"	=> "Windows 2000/XP/2003 32bit",
    "52"	=> "Windows 2003R2 32bit",
    "52x"	=> "Windows 2003R2 x64",
    "52i"	=> "Windows 2003R2 IA64",
    "60"	=> "Windows Vista/2008 32bit",
    "60x"	=> "Windows Vista/2008 x64",
    "60i"	=> "Windows Vista/2008 IA64",
    "61"	=> "Windows 7 32bit",
    "61x"	=> "Windows 7/2008R2 x64",
    "61i"	=> "Windows 2008R2 IA64",
);
my %osnames = (
    W2K  => "Win2000/XP",
    W2K3 => "Win2003 non-R2",
    W2K3SP1 => "Win2003 R2 SP1",
    W2K3SP2 => "Win2003 R2 SP2",
    W2K8SP1 => "Vista/2008 SP1",
    W2K8SP2 => "Vista/2008 SP2",
);
my %recommended = (
    913030 => { map {$_=>1} (35) },
    921207 => { map {$_=>1} (52) },
    936051 => { map {$_=>1} (52) },
    936529 => { map {$_=>1} (35) },
    939767 => { map {$_=>1} (35) },
    942312 => { map {$_=>1} (35) },
    942868 => { map {$_=>1} (52) },
#    943657 => { map {$_=>1} (60) },
    945449 => { map {$_=>1} (52) },
    945660 => { map {$_=>1} (52) },
    948918 => { map {$_=>1} (52) },
    950098 => { map {$_=>1} (52) },
#    953603 => { map {$_=>1} (60) },
    953698 => { map {$_=>1} (60) },
    955454 => { map {$_=>1} (52) },
    957390 => { map {$_=>1} (52) },
    967484 => { map {$_=>1} (60) },
    973389 => { map {$_=>1} (35, 60) },
    977615 => { map {$_=>1} (35, 52, 60, 61) },
    981194 => { map {$_=>1} (52, 61) },
);


my $q;

sub vercmp {
    my @left = split /\./, $_[0];
    my @right = split /\./, $_[1];

    while (@left and @right) {
	my $r = $left[0] <=> $right[0];
	return $r if $r;
	shift @left;
	shift @right;
    }
    return 1 if @left;
    return -1 if @right;
    return 0;
}

sub format_kb ($$$) {
    my $kb = shift;
    my $i = shift;
    my $lo = shift;
    my $bold = exists $recommended{$kb};
    if ($bold and defined $i) {
	$bold &&= exists $recommended{$kb}{substr($i, 0, 2)};
    }

    return
#    $q->a({href=>"http://support.microsoft.com/default.aspx?scid=kb;en-us;$kb"}, $kb);
    $q->a({href=>($lo ? "#kb$kb" : "http://support.microsoft.com/kb/$kb/en-us")},
	$bold ? $q->b($kb) : $kb
    );
}

sub format_os ($) {
    return exists $osnames{$_[0]} ? $osnames{$_[0]} : "&nbsp;";
}

open FILE, '<', $kbtitles_file or die "couldn't open $kbtitles_file: $!";
{
    while (<FILE>) {
	chomp;
	my ($kb, $t) = split /\t/;
	$kbtitles{$kb} = $t;
    }
    close FILE;
}

open FILE, '<', $kbfiles_file or die "couldn't open $kbfiles_file: $!";
{
    my $lastkb;
    my $lasti;
    while (<FILE>) {
	chomp;
	1 while s/([^\t])\t\t/$1\t/g;

	my ($kb, $i, $f, $v, $os) = split /\t/;

	if (!$kb) { $kb = $lastkb; }
	else      { $lastkb = $kb; }

	if (!$i)  { $i = $lasti; }
	else      { $lasti = $i; }

	if (!$os) { $os = "ANY"; }

	$kbfiles{$kb}{$i}{$f}{$os} = $v;

	if (exists $fileskb{$i}{$f}{$os}) {
	    my ($v2, $kb2) = @{$fileskb{$i}{$f}{$os}};
	    next if vercmp($v, $v2) < 0;
	    next if $kb <= $kb2;
	}
	$fileskb{$i}{$f}{$os} = [$v, $kb];
    }
    close FILE;
}

open FILE, '>', $outfile or die "couldn't open $outfile: $!";
{
    $q = new CGI;
    my @t;

    print FILE
	$q->start_html("Interix hotfixes index"), "\n",
	$q->h1("Interix hotfixes index"), "\n";

    print FILE
	$q->p("
On this site I maintain an index of MS KB articles and hotfixes for
Services&nbsp;for&nbsp;Unix 3.5 (and up) aka Interix, continuing the work of Todd Vierling.
Many thanks to him. The last version of his (now outdated) index can be found
" . $q->a({href=>"tv.html"}, "here") . " for reference."
	),
	$q->p("
Some of the hotfixes for Windows Server 2003 R2 (i.e. Interix 5.2) and higher
are also distributed via the regular Microsoft Update mechanism.
Alternatively, you can look at " .
$q->a({href=>"http://www.interix.ca/"}, "www.interix.ca") . " where another
index for Interix hotfixes exists. You may also follow the " .
$q->a({href=>"http://support.microsoft.com/common/rss.aspx?rssid=3207&ln=en-us&msid=b1db49f1967b9f428ca0a76d137f8b69"},
"MS RSS feed for Interix fixes") . "."
	),
	$q->p("
To obtain an hotfix, go to the MS KB article. There you can either download
the hotfix immediately, or you can request the hotfix by e-mail. If there is
no hotfix request link or if you need several hotfixes at once, you can
write an informal mail to btob\@microsoft.com . Alternatively, you can contact " .
$q->a({href=>"http://support.microsoft.com/contactus/?ws=support"}, "PSS per phone") . ".
You won't get charged for only requesting hotfixes."
	),
	$q->p("
This page has been last updated on: <b>" . gmtime() . " UTC </b><br/>
If you know of any Interix updates/articles not listed here, please contact me:
Martin K&ouml;ppe &lt;mkoeppe 'at' gmx . de&gt;"
	), "\n\n",
	$q->h2("History"),
	$q->ul($q->li([
	    "\n2010-04-28: added 978804 and 981194",
	    "\n2010-02-06: fix html index",
	    "\n2010-01-07: added 977615",
	    "\n2009-10-18: removed all patches for Vista RTM, prepare for Windows 7, added 967168, 967571, 969874",
	    "\n2009-09-19: added 973389",
	    "\n2009-08-22: added 961415 and 971518",
	    "\n2009-06-21: added 958144 and 969274",
	    "\n2009-06-06: added 933712, 942909, 944250, 957390",
	    "\n2009-05-30: added 967484",
	    "\n2009-03-28: added 953602",
	    "\n2009-02-17: added 929143",
	    "\n2009-02-12: added 959543",
	    "\n2008-12-08: added 955454",
	    "\n2008-12-03: added 957796",
	    "\n2008-11-04: added 872958",
	    "\n2008-09-19: added 951150",
	    "\n2008-08-17: added 948918 and 953603",
	    "\n2008-08-02: added 950098",
	    "\n2008-06-30: added 953698",
	    "\n2008-06-11: added 953191",
	    "\n2008-04-02: added 949653 (the first here for SUA 2008)",
	    "\n2008-03-21: added 947979",
	    "\n2008-03-16: added more hotfixes for SUA",
	    "\n2008-03-15: now also list SUA 5.2 and SUA 6.0 hotfixes, 942312 added",
	    "\n2008-02-09: 934326 and 946226 added",
	    "\n2007-12-23: 929141 and 944412 added",
	    "\n2007-12-15: initial version",
	])), "\n\n";


    print FILE
	$q->h2("Current hotfixes"), "\n",
	$q->p("Minimum recommended hotfixes are printed in " . $q->b("BOLD")), "\n";

    foreach my $i (sort keys %fileskb) {
	print FILE
	    $q->h3("$itxnames{$i} ($itxosnames{$i})"), "\n";

	@t = ();
	%osfixes = ();
	foreach my $f (keys %{$fileskb{$i}}) {
	    foreach my $os (keys %{$fileskb{$i}{$f}}) {
		my (undef, $kb) = @{$fileskb{$i}{$f}{$os}};
		$osfixes{$os}{$kb} = 1;
	    }
	}
	foreach my $os (sort keys %osfixes) {
	    push @t,
		($os eq "ANY" ? "" : "additionally for " . format_os($os) . ": ") .
		join(", ", map {format_kb($_, $i, 1)} (sort keys %{$osfixes{$os}}));
	}
	print FILE
	    $q->ul($q->li(\@t)),
	    "\n\n";
    }


    print FILE
	$q->h2("Hotfixes"), "\n";

    @t = ();
    push @t, "\n".$q->th(["KB&nbsp;#", "description", "SfU/SUA", "file", "version", "OS", "replaced by"]);

    foreach my $kb (sort keys %kbfiles) {
	my $n = @t;
	foreach my $i (sort keys %{$kbfiles{$kb}}) {
	    my $m = @t;
	    foreach my $f (sort keys %{$kbfiles{$kb}{$i}}) {
		foreach my $os (sort keys %{$kbfiles{$kb}{$i}{$f}}) {
		    my $v = $kbfiles{$kb}{$i}{$f}{$os};
		    my ($nv, $nkb) = @{$fileskb{$i}{$f}{$os}};

		    push @t, "\n".$q->td([
			$f,
			$v,
			format_os($os),
			$kb ne $nkb ? format_kb($nkb, $i, 1) : "&nbsp;",
		    ]);
		}
	    }
	    # prepend column 3
	    my $count3 = @t - $m;
	    next unless $count3;

	    $t[$m] = "\n" . $q->td({rowspan=>$count3}, [
		$itxnames{$i},
	    ]) . $t[$m];
	}

	# prepend column 1 and 2
	my $count12 = @t - $n;
	next unless $count12;

	my $title;
	if ($kbtitles{$kb}) {
	    $title = $kbtitles{$kb};
	} else {
	    $title = "";
	    print "Warning: no title found for KB $kb\n";
	}
	$t[$n] = "\n" . $q->td({rowspan=>$count12}, [
	    $q->a({name=>"kb$kb"}) . format_kb($kb, undef, 0),
	    $title,
	]) . $t[$n];
    }

    print FILE
    	$q->table({border=>1, width=>"100\%"},
	    $q->colgroup(
	    	$q->col({width=>"1*"}),
	    	$q->col({width=>"4*"}),
	    	$q->col({width=>"2*"}),
	    	$q->col({width=>"3*"}),
	    	$q->col({width=>"2*"}),
	    	$q->col({width=>"2*"}),
	    	$q->col({width=>"1*"}),
	    ),
	    $q->Tr(\@t),
	), "\n\n";


    foreach my $i (sort keys %fileskb) {
	print FILE
	    $q->h2("Current file versions on $itxnames{$i} ($itxosnames{$i})"), "\n";

	@t = ();
	push @t, "\n".$q->th(["file", "version", "OS", "KB&nbsp;#"]);

	foreach my $f (sort keys %{$fileskb{$i}}) {
	    foreach my $os (sort keys %{$fileskb{$i}{$f}}) {
		my ($v, $kb) = @{$fileskb{$i}{$f}{$os}};

	    push @t, "\n".$q->td([
		$f,
	        $v,
		format_os($os),
		format_kb($kb, $i, 1),
	    ]);
	    }
	}

	print FILE
    	    $q->table({border=>1, width=>"100\%"},
		$q->colgroup(
	    	    $q->col({width=>"2*"}),
	    	    $q->col({width=>"1*"}),
	    	    $q->col({width=>"1*"}),
	    	    $q->col({width=>"1*"}),
		),
		$q->Tr(\@t),
	    ), "\n\n";
    }

    print FILE
	$q->h2("Other MS KB articles about Interix"), "\n";

    @t = ();
    push @t, "\n".$q->th(["KB&nbsp;#", "Description"]);

    foreach my $kb (sort keys %kbtitles) {
	next if exists $kbfiles{$kb};

	push @t, "\n".$q->td([
	    format_kb($kb, undef, 0),
	    $kbtitles{$kb},
	]);
    }

    print FILE
        $q->table({border=>1, width=>"100\%"},
	    $q->colgroup(
	        $q->col({width=>"1*"}),
	        $q->col({width=>"9*"}),
	    ),
	    $q->Tr(\@t),
	), "\n\n";


    print FILE
	$q->hr,
	$q->p("(C) 2007, " . ((localtime)[5]+1900) . " Martin K&ouml;ppe &lt;mkoeppe 'at' gmx . de&gt;"), "\n",
	$q->p(
	    $q->a({href=>"hotfixes"}, "source for the script generating this page") .
	    " | " . $q->a({href=>"kbtitles"}, "MS KB titles") .
	    " | " . $q->a({href=>"kbfiles"}, "MS KB files")
	),
	$q->end_html, "\n";

    close FILE;
}
system "gzip", "-9f", $outfile;

