#!/usr/bin/python
#
# greportbug - Report a bug in the Debian distribution.
#   Written by Chris Lawrence <lawrencc@debian.org>
#   (C) 1999 Chris Lawrence
#
# This program is freely distributable per the following license:
#
##  Permission to use, copy, modify, and distribute this software and its
##  documentation for any purpose and without fee is hereby granted,
##  provided that the above copyright notice appears in all copies and that
##  both that copyright notice and this permission notice appear in
##  supporting documentation.
##
##  I DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
##  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL I
##  BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
##  DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
##  WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
##  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
##  SOFTWARE.
#
# Version ##VERSION##; see changelog for revision history

MUA = "greportbug ##VERSION##"

import sys, string, getopt, re, os, tempfile, pwd, time, locale, commands

from gtk import *
import GtkExtra

sys.path.append('/usr/lib/reportbug')
import reportbug

wins = {}

# Alias
def ewrite(message):
    sys.stderr.write(message)
    sys.stderr.flush()

def spawn_editor(message, filename):
    editor = os.environ.get('VISUAL', 'sensible-editor')
    editor = os.environ.get('EDITOR', editor)

    edname = string.split(editor)[0]
    
    # Move the cursor for lazy buggers like me; add your editor here...
    lineno = 1
    fp = open(filename)
    for line in fp.readlines():
        lineno = lineno+1
        if line == '\n': break

    if edname in ('vi', 'nvi', 'vim', 'elvis'):
        opts = '-c :%d' % lineno
    elif edname in ('emacs', 'xemacs', 'elvis-tiny', 'gnuclient'):
        opts = '+%d' % lineno
    elif edname in ('jed', 'xjed'):
        opts = '-g %d' % lineno
    else:
        opts = ''

    ewrite("Spawning %s...\n" % edname)
    result = os.system("%s %s '%s'" % (editor, opts, filename))

    if result:
        ewrite('Warning: possible error exit from %s: %d\n' %
               (edname, result))

    if not os.path.exists(filename):
        ewrite('Bug report file %s removed!' % filename)
        sys.exit(1)

    file = open(filename)
    newmessage = file.read()
    file.close()

    if newmessage == message:
        ewrite('No changes were made in the editor.\n')

    return newmessage

def find_package_for(filename):
    ewrite("Finding package for %s\n" % filename)
    (newfilename, packages) = reportbug.find_package_for(filename)
    if newfilename != filename:
        filename = newfilename
        ewrite("Resolved as %s\n" % filename)
    if not packages:
        ewrite("No packages match.  ")
        return (filename, None)
    elif len(packages) > 1:
        ewrite("Multiple packages match:\n")
        packlist = packages.keys()
        packlist.sort()
        for package in packlist:
            ewrite('  %s\n' % package)

        package = ''
        while not package in packlist:
            if package:
                ewrite('Invalid response.  ')
            ewrite('Select one of these packages: ')
            package = raw_input()
            if not package: break
        
        return (filename, package)
    else:
        package = packages.keys()[0]
        ewrite("Using package %s\n" % package)
        return (filename, package)

def get_package_name():
    ewrite("""Please enter the package in which you have found a problem, or
type one of these bug categories:

base             General bugs in the base system
bootdisk         Bugs in the bootdisk
rootdisk         Bugs in the rootdisk
bugs.debian.org  The bug tracking system, @bugs.debian.org
ftp.debian.org   Problems with the main/contrib/non-free FTP site (or mirrors)
nonus.debian.org Problems with the non-us FTP site (or mirrors)
www.debian.org   Problems with the website (or mirrors)
manual           Bugs in the manual
project          Problems related to Project administration
general          General problems (e.g., that many manpages are mode 755)
kernel           Problems with the kernel in general (otherwise: kernel-image)
lists.debian.org The mailing lists (debian-*@lists.debian.org).

Select a package: """)
    package = raw_input()
    return package

def select_options(msg, ok, default=None):
    if not default: default = ok[0]

    ewrite(msg+' ['+string.join(ok, '|')+']? ')
    ch = raw_input()
    if not ch: ch = default
    ch = ch[0]
    if (ch in ok) or (string.upper(ch) in ok):
        return string.lower(ch)
    else:
        ewrite('Invalid selection.\n')
        return select_options(msg, ok, default)

USAGE = ("greportbug - Report a bug in the Debian distribution\n\n"
         "Usage: greportbug\n\n"
         "Please use the console version (reportbug) if you need to specify\n"
         "command line options.\n")

def old_main():
    uid = os.getuid()
    if not uid:
        x = select_options("Running 'greportbug' as root is probably "
                           "insecure!\nContinue", 'yN', 'N')
        if x == 'n':
            ewrite("Aborted.\n")
            sys.exit(1)

    try:
        (opts, args) = getopt.getopt(sys.argv[1:], 'habcdf:H:i:mMno:pqs:S:vxz',
                                     ['help', 'no-config-files', 'debug',
                                      'file=', 'header=', 'maintonly', 'print',
                                      'quiet', 'subject=', 'no-cc', 'mua=',
                                      'include', 'severity=', 'version', 'af',
                                      'mh', 'nmh', 'mutt', 'no-compress',
                                      'output=', 'no-bts-query'])
    except getopt.error, msg:
        ewrite(msg+'\n')
        sys.exit(1)

    sendto = 'Debian Bug Tracking System <submit@bugs.debian.org>'
    searchfor = severity = subject = outfile = mua = ''
    headers = []
    include = []
    nocc = noconf = nocompress = printonly = dontquery = 0

    for option, arg in opts:
        if option in ('-h', '--help'):
            ewrite(USAGE)
            return
        elif option in ('-v', '--version'):
            ewrite(MUA+'\n')
            return
        elif option in ('-c', '--no-config-files'):
            noconf = 1
        elif option in ('-d', '--debug'):
            sendto = 'postmaster@localhost'
        elif option in ('-f', '--filename'):
            searchfor = arg
        elif option in ('-H', '--header'):
            headers.append(arg)
        elif option in ('-m', '--maintonly'):
            sendto = 'Debian BTS - Maintainer <maintonly@bugs.debian.org>'
        elif option in ('-M', '--mutt'):
            mua = 'mutt -H'
        elif option in ('-a', '--af'):
            mua = 'af -EH <'
        elif option in ('-n', '--mh', '--nmh'):
            mua = '/usr/bin/mh/comp -draftmessage'
        elif option == '--mua':
            mua = arg
        elif option in ('-p', '--print'):
            printonly = 1
        elif option in ('-q', '--quiet'):
            sendto = 'Debian BTS - Quiet <quiet@bugs.debian.org>'
        elif option in ('-s', '--subject'):
            subject = arg
        elif option in ('-x', '--no-cc'):
            nocc = 1
        elif option in ('-z', '--no-compress'):
            nocompress = 1
        elif option in ('-o', '--output'):
            outfile = arg
        elif option in ('-i', '--include'):
            include.append(arg)
        elif option in ('-b', '--no-bts-query'):
            dontquery = 1
        elif option in ('-S', '--severity'):
            if arg in reportbug.SEVERITIES.values():
                severity = arg
            elif arg in reportbug.SEVERITIES.keys():
                severity = reportbug.SEVERITIES[arg]
            else:
                ewrite("Ignored bogus severity level %s\n" % arg)

    foundfile = None
    if len(args) == 0 and not searchfor:
        package = get_package_name()
    elif len(args) > 1:
        ewrite("Please report one bug at a time.\n")
        ewrite("[Did you forget to put all switches before the "
               "package name?]\n")
        sys.exit(1)
    elif searchfor:
        (foundfile, package) = find_package_for(searchfor)
    else:
        package = args[0]

    if not package:
        ewrite("Aborted.\n")
        sys.exit(1)

    if not dontquery:
        import debianbts
        
        ewrite('Querying Debian bug tracking system for reports on %s\n'
               % package)
        (count, title, hierarchy) = debianbts.get_reports(package)

        if not count:
            ewrite('No outstanding bugs.\n')
        else:
            r, c = string.split(commands.getoutput('stty size'))
            rows, columns = int(r), int(c)
            ewrite('%d bug report(s) found:\n' % count)
            endcount = catcount = 0
            scount = startcount = 1
            category = hierarchy[0]
            while category:
                scount = scount + 1
                ewrite('  %s %d report(s)\n' % (category[0], len(category[1])))
                for report in category[1]:
                    ewrite('    '+report[:columns-5]+'\n')
                    scount = scount + 1
                    endcount = endcount + 1

                if category == hierarchy[-1] or \
                   (scount >= (rows - len(hierarchy[catcount+1][1]) - 1)):
                    if endcount == count:
                        skipmsg = ''
                    else:
                        skipmsg = ' (s to skip rest)'
                        
                    range = "(%d-%d/%d) " % (startcount, endcount, count)
                    x = select_options(range+"Is the bug you found in the "
                                       "above list"+skipmsg,
                                       'yNs', 'N')
                    if x == 'y':
                        ewrite('Aborted.\n')
                        sys.exit(1)
                    if x == 's':
                        break

                    startcount = endcount+1
                    scount = 0

                if category == hierarchy[-1]: break

                catcount = catcount+1
                category = hierarchy[catcount]
                ewrite('\n')
                scount = scount + 1

    fromaddr = reportbug.get_user_id()
    ewrite("Using '%s' as your from address.\n" % fromaddr)

    ccaddr = os.environ.get('CC')
    bccaddr = os.environ.get('BCC', fromaddr)

    incfiles = ""
    if include:
        for file in include:
            if os.path.exists(file):
                fp = open(file)
                incfiles = '%s\n*** %s\n%s' % (incfiles, file, fp.read())
                fp.close()
            else:
                ewrite("Can't find %s to include!\n" % file)
                sys.exit(1)

    pkgversion = pkgavail = depends = ''
    conffiles = []
    if package not in reportbug.OTHERPKGS:
        ewrite("Getting status for %s...\n" % package)
        (pkgversion, pkgavail, depends, conffiles) = \
                     reportbug.get_package_status(package)

        if not pkgavail:
            response = select_options('This package does not appear to exist; '
                                      'continue', 'yN', 'N')
            if response == 'n':
                ewrite("Aborted.\n")
                sys.exit(1)

        if pkgavail and not pkgversion:
            response = select_options('This package does not appear to be '
                                      'installed; continue', 'yN', 'N')
            if response == 'n':
                ewrite("Aborted.\n")
                sys.exit(1)

    depinfo = ""
    # Grab dependency list, removing version conditions.
    if depends:
        ewrite("Getting dependency information for %s...\n" % package)
        depinfo = reportbug.get_dependency_info(package, depends)

    confinfo = ""
    remonly = 0
    if conffiles:
        ewrite("Getting changed configuration files...\n")
        confinfo, remonly = \
                  reportbug.get_changed_config_files(conffiles, noconf,
                                                     nocompress)

    if noconf or not confinfo:
        pass
    elif len(confinfo) > 20000:
        x = select_options("*** WARNING: Large configuration files detected.\n"
                           "Are you sure you want to send them", 'yN', 'N')
        if x == 'n':
            confinfo = "\nModified configuration files not included.\n"
    elif not remonly:
        x = select_options("*** WARNING: Modified configuration files will be "
                           "sent with this report!\n"
                           "Are you sure you want to do that", 'Yn')
        if x == 'n':
            confinfo = "\nModified configuration files not included.\n"

    if not subject:
        ewrite('Please briefly describe your problem (you can elaborate in '
               'a moment).\n> ')
        subject = raw_input()
        if not subject:
            ewrite("Aborted.\n")
            sys.exit(1)
    
    if not severity:
    	ewrite("""How would you rate the severity of your bug?
critical    makes unrelated software on the system (or the whole system) break,
	    or causes serious data loss, or introduces a security hole on 
	    systems where you install the package.

grave 	    makes the package in question unuseable or mostly so, or causes data
	    loss, or introduces a security hole allowing access to the accounts
            of users who use the package.    

important   any other bug which makes the package unsuitable for release. 

normal      the default value, used for more 'benign' bugs

wishlist    for any feature request, and also for any bugs that are very 
	    difficult to fix due to major design considerations. 
	
fixed       for bugs that are fixed but should not yet be closed. Bugs fixed by 
	    non-maintainer-uploads have their severity set to fixed.
	  
Please select a severity level: """)
	
	severity = raw_input()
	while(severity not in reportbug.SEVERITIES.values()): 
		ewrite("Invalid severity level, please try again: ")
		severity = raw_input()
	
    # Prepare bug report
    
    message = reportbug.generate_blank_report(package, pkgversion, severity, 
                                              depinfo, confinfo, foundfile,
                                              incfiles)

    tempfile.tempdir = os.environ.get('TMPDIR', '/tmp')
    filename = tempfile.mktemp()

    if printonly:
        report = sys.stdout
    elif mua:
        report = open(filename, 'w')
    else:
        message = "Subject: %s\n%s" % (subject, message)
        file = open(filename, 'w')
        file.write(message)
        file.close()

        while 1:
            message = spawn_editor(message, filename)
            if outfile:
                ewrite("Report will be saved as %s\n" % outfile)
            else:
                ewrite("Report will be sent to %s\n" % sendto)
                
            x = select_options('Submit this bug report (e to edit)', 'Yne')
            if x == 'n':
                ewrite('Aborted; report stored as %s.\n' % filename)
                sys.exit(1)
            elif x == 'y':
                break
        
        if not sendto:
            print message,
            # Remove the temporary file
            os.unlink(filename)
            return

        # Handle the subject line
        subjre = re.compile('Subject: ')
        lines = string.split(message, '\n')
        newsubject = message = ''
        for line in lines:
            if subjre.match(line):
                (crud, newsubject) = string.split(line, ': ', 1)
                continue
            message = message + line + '\n'

        if newsubject:
            subject = newsubject

    if ccaddr:
        headers.append('Cc: '+ccaddr)

    if not nocc:
        headers.append('Bcc: '+bccaddr)

    replyto = os.environ.get("REPLYTO")
    if replyto:
        headers.append('Reply-To: '+replyto)

    # Standard headers
    if not mua and not printonly:
        headers = ['X-Mailer: '+MUA,
                   'Date: '+reportbug.rfcdatestr()] + headers

    headers = ['From: '+fromaddr,
               'To: '+sendto,
               'Subject: '+subject] + headers

    usingpipe = 0
    if mua or printonly:
        pipe = report
    elif outfile or not os.path.exists('/usr/sbin/sendmail'):
        msgname = outfile or ('/var/tmp/%s.bug' % package)
        pipe = open(msgname, 'w')
        ewrite('Writing bug report to %s\n' % msgname)
    else:
        pipe = os.popen('/usr/sbin/sendmail -t -oi -oem', 'w')
        usingpipe = 1

    pipe.write(string.join(headers, '\n') + '\n\n' + message)
    if pipe.close():
        msgname = '/var/tmp/%s.bug' % package
        pipe = open(msgname, 'w')
        ewrite('sendmail failed; writing bug report to %s\n' % msgname)
        pipe.write(string.join(headers, '\n') + '\n\n' + message)
        pipe.close()

    if mua:
        ewrite("Spawning %s...\n" % string.split(mua, ' ')[0])
        os.system("%s %s" % (mua, filename))
    elif usingpipe:
        ewrite("\nBug report submitted to: %s\n" % sendto)
        if ccaddr:
            ewrite("  Carbon copy to: %s\n" % ccaddr)
        if not nocc:
            ewrite("  Blind carbon copy to: %s\n" % bccaddr)
            
        ewrite(
"""\nIf you want to submit further information about this bug, please wait to
receive the bug tracking number by email. Then mail any extra information
to XXXXX@bugs.debian.org, where XXXXX is the bug number.\n""")

    # Remove the temporary file (if something hasn't killed it already)
    if os.path.exists(filename):
        os.unlink(filename)

    return

class EasyOptionMenu(GtkVBox):
    def __init__(self, options = None):
        GtkVBox.__init__(self)
        if options is None:
            self.options = []
        else:
            self.options = options
        self.init_widgets()
        self.show()

    def init_widgets(self):
        self.option_menu = GtkOptionMenu()
        self.option_menu.set_menu(self.create_menu())
        self.pack_end(self.option_menu, expand=FALSE)
        self.option_menu.show()

    def create_menu(self):
        menu = GtkMenu()
        group = None
        self.all_items = []			
        for option in self.options:
                menuitem = GtkRadioMenuItem(group, option)
                menuitem.connect('activate', self.menu_cb, option)
                group = menuitem
                menu.append(menuitem)
                self.all_items.append(menuitem)
                menuitem.show()

        return menu
		
    def menu_cb(self, widget, data):
        self.value = str(data)
			
    def set_value(self, value):
        if value >= len(self.options):
            value = len(self.options)-1
        if value < 0:
            value = 0
        self.value = value
        self.all_items[value].set_active(TRUE)
        self.option_menu.set_history(value)

    def get_value(self):
            return self.value
    
def delete_event(win, event=None):
	win.hide()
	# don't destroy window -- just leave it hidden
	return TRUE

def create_main_window():
    win = GtkWindow()
    win.set_name('main window')
    win.connect("destroy", mainquit)
    win.connect("delete_event", mainquit)
    win.set_title("greportbug ##VERSION##")
    win.set_usize(300, 400)

    mainbox = GtkVBox(FALSE, 0)
    win.add(mainbox)
    mainbox.show()

    notebook = GtkNotebook()
    notebook.set_tab_pos(POS_TOP)
    mainbox.pack_start(notebook)
    notebook.show()

    package = GtkHBox()
    package.show()
    # Package selector
    packlab = GtkLabel("Package")
    notebook.append_page(package, packlab)

    filename = GtkHBox()
    filename.show()
    # File selector
    filelab = GtkLabel("File")
    notebook.append_page(filename, filelab)

    frame = GtkFrame("Report options")
    frame.set_border_width(10)
    mainbox.pack_start(frame)
    frame.show()

    table = GtkTable(2, 2, FALSE)
    table.set_row_spacings(1)
    table.set_col_spacings(5)
    table.set_border_width(10)
    frame.add(table)
    table.show()

    label = GtkLabel("Severity:")
    table.attach(label, 0, 1, 0, 1)
    label.show()

    severity = EasyOptionMenu(['Grave', 'Critical', 'Important',
                               'Normal', 'Wishlist', 'Fixed'])
    table.attach(severity, 1, 2, 0, 1)
    severity.set_value(3)
    severity.show()

    label = GtkLabel("Send to:")
    table.attach(label, 0, 1, 1, 2)
    label.show()

    destination = EasyOptionMenu(['Maintainer and bug list',
                                  'Maintainer only',
                                  'Tracking system only'])
    table.attach(destination, 1, 2, 1, 2)
    destination.show()

    box2 = GtkVBox(spacing=10)
    box2.set_border_width(5)
    frame.add(box2)
    frame.show()
    
    box2 = GtkVBox(spacing=10)
    box2.set_border_width(10)
    mainbox.pack_start(box2, expand=FALSE)
    box2.show()

    button = GtkButton("Generate report template")
##     button.connect("clicked", pop_statusbar_test)
    box2.pack_start(button)
    button.set_flags(CAN_DEFAULT)
    button.show()
    
    win.show()

def main():
    create_main_window()
    mainloop()

if __name__ == '__main__':
    main()
