#!/usr/bin/env python2
#
# Author: Kees Cook <kees@ubuntu.com>
# Author: Jamie Strandboge <jamie@ubuntu.com>
# Copyright (C) 2005-2012 Canonical Ltd.
#
# This script is distributed under the terms and conditions of the GNU General
# Public License, Version 2 or later. See http://www.gnu.org/copyleft/gpl.html
# for details.
#
# Reports a terse numeric of CVEs that need attention for supported packages.
import subprocess, optparse, re
import cve_lib


parser = optparse.OptionParser()
parser.add_option("--prose", help="Report in prose instead of just numbers.", action='store_true')
parser.add_option("--prose-date", dest="prose_date", help="Date to use in prose text", metavar="YYYY-MM-DD")
parser.add_option("-E", "--skip-embargoed", help="Do not add embargoed CVEs to the numbers.", action="store_true")
parser.add_option("-u", "--show-unique-sources", help="Show unique source packages.", action="store_true")
parser.add_option("--skip-low", help="Don't show stats for low CVEs too.", action="store_true")
parser.add_option("--skip-packages", help="Comma-separated list of packages to not count.", action="store")
parser.add_option("--only-packages", help="Comma-separated list of the only packages to count.", action="store")
(opt, args) = parser.parse_args()

item=dict() # CVE counts
srcpkg_item=dict() # srcpkg with vulns counts
p=subprocess.Popen(['./scripts/ubuntu-table','-s']+args,stdout=subprocess.PIPE)
table = p.communicate()[0]
if p.returncode != 0:
    raise ValueError, "ubuntu-table returned non-zero: %d" % (p.returncode)

if opt.prose and opt.prose_date:
    print "As of %s:" % opt.prose_date

priorities = ['critical','high','medium']
if not opt.skip_low:
    priorities.append('low')

skipped_packages = []
if opt.skip_packages:
    skipped_packages = re.split(',', opt.skip_packages)

only_packages = []
if opt.only_packages:
    only_packages = re.split(',', opt.only_packages)

for category in ['supported','partner','universe']:
    item[category] = dict()
    srcpkg_item[category] = dict()

    output = []
    pkgs = set()
    for prio in priorities:
        item[category][prio] = []
        srcpkg_item[category][prio] = dict()

        for line in table.splitlines():
            line = line.strip()
            # Filter based on support type
            if opt.skip_embargoed and 'EMBARGOED' in line:
                continue
            if 'SUPPORTED' in line:
                if category != 'supported':
                    continue
            elif 'PARTNER' in line:
                if category != 'partner':
                    continue
            elif category != 'universe':
                continue

            # Do not count linux topic branches
            pkg = line.split(' ')[1].split(':')[0]
            if pkg in cve_lib.kernel_topic_branches:
                continue

            if pkg in skipped_packages:
                continue
            elif len(only_packages) > 0 and pkg not in only_packages:
                continue
            #if pkg not in pkgs:
            #    print("DEBUG: adding %s to %s" % (pkg, category))
            pkgs.add(pkg)

            # Filter based on priority
            if not line.endswith(' %s' % (prio)):
                continue

            item[category][prio].append(line)

            if not srcpkg_item[category][prio].has_key(pkg):
                srcpkg_item[category][prio][pkg] = 1
            else:
                srcpkg_item[category][prio][pkg] += 1

        count = len(item[category][prio])
        if not opt.prose and count:
            output.append('%d %s' % (count, prio))

    if not opt.prose:
        if len(pkgs) > 0:
            if opt.show_unique_sources:
                total = 0
                for prio in priorities:
                    total += len(srcpkg_item[category][prio].keys())
                if total == 0:
                    print ' * CVEs pending: none in %s packages!' % (category)
                    continue

            plural='s'
            if len(pkgs)==1:
                plural=''
            print ' * CVEs pending: %s, in %d %s package%s' % \
                (", ".join(output), len(pkgs), category, plural),

            if not opt.show_unique_sources:
                print ""
            else:
                out = "("
                for prio in priorities:
                    if not len(srcpkg_item[category][prio].keys()):
                        continue
                    out += "%d %s" % (len(srcpkg_item[category][prio].keys()), prio)
                    if prio == "low" or (prio == "medium" and opt.skip_low):
                        out += " unique source packages)"
                    else:
                        out += ", "
                print out
        else:
            print ' * CVEs pending: none in %s packages!' % (category)
    else:
        if category == 'supported':
            print " * For all the supported packages in Ubuntu, there",
        elif category == 'partner':
            print " * For all partner packages in Ubuntu, there",
        elif category == 'universe':
            print " * For all community-supported packages in Ubuntu, there",
        if opt.show_unique_sources:
            total = 0
            for prio in priorities:
                total += len(srcpkg_item[category][prio].keys())
            if total == 0:
                print "no issues open!"
                continue

            phrases = []
            for prio in priorities:
                if len(srcpkg_item[category][prio].keys()) == 0:
                    continue
                if len(phrases) == 0:
                    plural = "s"
                    plural_verb = "are"
                    if len(srcpkg_item[category][prio].keys()) == 1:
                        plural = ""
                        plural_verb = "is"
                    phrases.append("%s %d source package%s with at least one %s-priority issue that needs to be fixed" % (plural_verb, len(srcpkg_item[category][prio].keys()), plural, prio))
                else:
                    phrases.append("%d with %s" % (len(srcpkg_item[category][prio].keys()), prio))

            out = phrases[0]
            for phrase in phrases[1:-1]:
                out += ", %s" % phrase

            if len(phrases) > 1:
                out += " and %s" % phrases[-1]
            out += "."
            print out
        else:
            plural='are'
            for prio in priorities:
                if len(item[category][prio])>0:
                    if len(item[category][prio])==1:
                        plural='is'
                    break
            print plural,
            if len(pkgs) == 0:
                print "no issues open!"
            else:
                cats = []
                for prio in priorities:
                    count=len(item[category][prio])
                    if count==0:
                        continue
                    plural='s'
                    if count==1:
                        plural=''
                    cats.append('%d %s-priority issue%s' % (count, prio, plural))

                count = len(cats)
                if count > 1:
                    cats[count-1] = "and %s" % (cats[count-1])
                if count == 2:
                    print " ".join(cats),
                else:
                    print ", ".join(cats),

                plural='s'
                if len(pkgs)==1:
                    plural=''
                print "that need to be fixed in %d package%s." % (len(pkgs), plural)
            #print ""
