#!/usr/bin/ruby
#
# apt-listbugs: retrieves bug reports and lists them
#
# Copyright (C) 2002       Masato Taruishi <taru@debian.org>
# Copyright (C) 2006-2008  Junichi Uekawa <dancer@debian.org>
# Copyright (C) 2008-2009  Francesco Poli <frx@firenze.linux.it>
# Copyright (C) 2009       Ryan Niebur <ryan@debian.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 with
#  the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL;
#  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
#  Suite 330, Boston, MA  02111-1307  USA
#
#
=begin

== NAME

apt-listbugs - Lists critical bugs before each apt upgrade/installation

== SYNOPSIS

apt-listbugs [options] <command> [arguments]

== DESCRIPTION

apt-listbugs is a tool which retrieves bug reports from the Debian
Bug Tracking System and lists them. Especially, it is intended to
be invoked before each upgrade by apt, or other similar package managers,
in order to check whether the upgrade/installation is safe. 

== USAGE

apt-listbugs [-h] [-v] [-s <severities>] [-T <tags>] [-S <stats>] [-B <bug#>] [-D] [-H <hostname>] [-p <port>] [-P <priority>] [-E <title>] [-q] [-C <apt.conf>] [-y] [-n] [-d] <command> [arguments]

== OPTIONS

* -h | --help

  Print usage help and exit

* -v | --version

  Print version number and exit

* -s <severities> | --severity <severities>

  Severities you want to see separated by comma, possible values are critical, grave, serious, important, normal, minor and wishlist. Default: [critical,grave,serious]
  You can specify '--severity all' to specify all severities.

* -T <tags> | --tag <tags>

  Tags you want to see separated by comma. 

* -S <stats> | --stats <stats>

  Status you want to see separated by comma. Default: [forwarded,done,pending,pending-fixed]

  Note that 'pending' does not mean 'tagged pending', but 'still open, pending to be fixed'.

* -B <bug#> | --bugs <bug#>

  By default, apt-listbugs will consider all bugs.
  This option adds a restriction to the bug numbers you want to see separated by commas
  (e.g. 123456,567890,135792).  If this option is specified, all other
  bugs will be ignored.

* -D | --show-downgrade

  Shows bugs of downgraded packages. (apt mode only)

* -H <hostname> | --hostname <hostname>

  Specifies the hostname of Debian Bug Tracking System [bugs.debian.org].

* -p <port> | --port <port>

  Specifies the port number of the web interface of Debian Bug Tracking System [80].

* -P <priority> | --pin-priority <priority>

  Specifies Pin-Priority value [1000].

* -E <title> | --title <title>

  Specifies the title of rss output.

* -q | --quiet

  Don't display progress bar. This option is assumed if stdout is not a 
  terminal.

* -C <apt.conf> | --aptconf <apt.conf>

  Specifies the apt configuration file to use.

* -y | --force-yes 

  Assumes that you select yes for all questions.

* -n | --force-no

  Assumes that you select no for all questions.  This option is
  assumed if stdout is not a terminal.

* -d | --debug

  Give extra debug output, important for debugging problems. Please
  include -d when reporting problems.

== COMMANDS

* apt

  Reads filenames from standard input (typically provided by apt).

* list [<package1[/version]> <package2[/version]>...]

  Reads package names from the arguments and simply lists bugs of
  these packages. Package versions may be specified with a slash, for
  example, like apt/1.0

* rss [<package1> <package2>...]

  Reads package names from the arguments and lists bugs of these packages
  in rss format.

== ENVIRONMENT VARIABLES

* APT_LISTBUGS_FRONTEND

  If this variable is set to "none" apt-listbugs will not execute at all, 
  this might be useful if you would like to script the use of a program that
  calls apt-listbugs.

* http_proxy, soap_use_proxy=on

  If HTTP_PROXY is set and SOAP_USE_PROXY is set, the value is used for HTTP Proxy.
  The default is to use the same value set for apt.

== CONFIGURATION FILE

apt-listbugs understands APT configuration file (see apt.conf). The notable configuration options are 

* Acquire::HTTP::Proxy

  The default HTTP Proxy setting.

  Special keyword 'DIRECT' will disable proxy configuration.

* Acquire::HTTP::Proxy::bugs.debian.org

  HTTP Proxy setting, overrides the default HTTP Proxy setting. Useful
  for setting HTTP proxy for apt-listbugs.

* AptListbugs::IgnoreRegexp

  Bugs to ignore when in apt mode. I would suggest setting this to
  "FTBFS" since those bugs tend to not affect the user, but this
  defaults to nothing. This will be evaluated using ruby regular
  expressions.

== OUTPUT EXAMPLE

  [bug severity] bugs of [package] ([current version] -> [package version to be installed]) <[status of bug report]>
   [bug #] - [bug title] [(Fixed: fixed version, if it's fixed in a future version)]

  e.g.


  Reading package fields... Done
  Reading package status... Done
  Retrieving bug reports... Done
  Parsing Found/Fixed information... Done
  important bugs of apt-listbugs (0.0.57 -> ) <pending>
   #332442 - apt-listbugs: Apt-listbugs doesn't actually download any bug reports
   #389903 - apt-listbugs: Does not offer to exit if timeout occurs fetching reports
  Summary:
   apt-listbugs(2 bugs)



== EXIT CODE

* 0 

  Success

* 1

  When something is wrong.

* 10 

  To warn APT not to proceed.

== AUTHORS

apt-listbugs was originally written by Masato Taruishi
<taru@debian.org>, and rewritten by Junichi Uekawa
<dancer@debian.org> in 2006 to handle BTS Versioning features and the
SOAP interface. The --bugs option was added by Francesco Poli
<frx@firenze.linux.it> in 2008. apt-listbugs is currently maintained by
Francesco Poli and Ryan Niebur <ryan@debian.org>.

Latest source-code is available from
http://git.debian.org/?p=apt-listbugs/apt-listbugs.git

== SEE ALSO

apt.conf(5), sensible-browser(1), www-browser(1), querybts(1)

=end

if File.expand_path(__FILE__).match(/^\/usr\/s?bin\//)
  $LOAD_PATH.unshift("/usr/share/apt-listbugs")
  $VERSION = `dpkg-query -W -f='${Version}' apt-listbugs`
else
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "lib"))
  $VERSION = `dpkg-parsechangelog | grep ^Version: | cut -d ' ' -f 2`
end

require 'getoptlong'
require 'debian'
require 'debian/bug'
require 'debian/bts'
require 'thread'
require 'tempfile'
require 'gettext'
require 'rss/maker'
require 'apt-listbugs/logic'
include GetText

GetText::bindtextdomain("apt-listbugs")

# ad-hoc
require 'debian/mytempfile'
class HtmlTempfile < MyTempfile
  def _tmpname(basename,tmpdir,n)
    sprintf('%s/%s%d.%d.html', tmpdir, basename, $$, n)
  end
end

## main from here

trap("INT") { $stderr.puts "Interrupted"; exit(0) }

# Drop out as early as possible if this env var is set.
if ENV["APT_LISTBUGS_FRONTEND"] == "none" 
  exit 0
end
# handle options
config = AppConfig.new
config.parse_options
Factory.config = config

# handle arguments
pkgnames = []
holdpkgs = {}
case config.command
when "apt"
  # parse apt VERSION 2 input.
  state=1
  STDIN.each { |pkg|
    pkg=pkg.rstrip
    case state
    when 1
      # the version header, only one line.
      if pkg == "VERSION 2"
        state=2
      else
        $stderr.print _("E: apt Pre-Install-Pkgs is not giving me expected 'VERSION 2' string.\n")
        exit 1
      end
    when 2
      # apt configuration lines
      case pkg
      when ""
        state=3
      when /^quiet=(.*)/
        if $1.to_i > 0 
          config.quiet=true
        end
      end
    when 3
      # package filenames
      filename=pkg.split(" ")[4]
      case filename
      when "**CONFIGURE**"
        # none
      when "**REMOVE**"
        # none
      else
        pkgnames << filename
      end
    end
  }
when "list"
  ARGV.each { |pkg|
    # pkg may be frozen string
    pkgnames << "#{pkg}"
  }
when "rss"
  ARGV.each { |pkg|
    pkgnames << "#{pkg}"
  }
end

exit 0 if pkgnames.size == 0

# creating new packages database
new_pkgs = Factory::PackageFactory.create(pkgnames) { |msg, val|
  config.frontend.progress(msg, val) if config.quiet == false
}

Factory::PackageFactory.delete_ignore_pkgs(new_pkgs) if config.command == "apt"
# exitting if no new packages is found
exit 0 if new_pkgs.size == 0

# creating current packages database
cur_pkgs = Factory::StatusFactory.create(new_pkgs) { |msg, val|
  config.frontend.progress(msg, val) if config.quiet == false
}
if ( config.show_downgrade == false ) and ( config.command == "apt" )
  Factory::StatusFactory.delete_downgraded(cur_pkgs, new_pkgs)
end

# reading bug reports
begin
  bugs = Factory::BugsFactory.create(new_pkgs, cur_pkgs) { |msg, val|
    config.frontend.progress(msg, val) if config.quiet == false
  }
rescue
  config.frontend.puts " ... E: #{$!}"
  exit 10
end

Factory::BugsFactory.delete_ignore_bugs(bugs) if config.command == "apt"
Factory::BugsFactory.delete_regexp_bugs(bugs, config.ignore_regexp) if config.command == "apt" and config.ignore_regexp
Factory::BugsFactory.delete_uninteresting_bugs(bugs) if config.fbugs
Factory::BugsFactory.delete_unwanted_tag_bugs(bugs) if config.tag
begin
  Factory::BugsFactory.delete_irrelevant_bugs(bugs, cur_pkgs, new_pkgs) { |msg, val|
    config.frontend.progress(msg, val) if config.quiet == false
  }
rescue
  config.frontend.puts " ... E: #{$!}"
  exit 1
end

exit 0 if config.command != "rss" && bugs.size == 0

# read done. now starting viewer
viewer = nil
case config.command
when "apt"
  viewer = Viewer::SimpleViewer.new(config)
when "list"
  viewer = Viewer::SimpleViewer.new(config)
when "rss"
  viewer = Viewer::RSSViewer.new(config)
end
if viewer.view(new_pkgs, cur_pkgs, bugs) == false
  ErrorWarning =  _("****** Exit with an error by force in order to stop the installation. ******")
  ErrorWarningHeader = "*" * ErrorWarning.length
  config.frontend.puts ErrorWarningHeader
  config.frontend.puts ErrorWarning
  config.frontend.puts ErrorWarningHeader
  config.frontend.close
  exit 10
end
config.frontend.close
