#!/usr/bin/python
#
# reportbug - Report a bug in the Debian distribution.
#   Written by Chris Lawrence <lawrencc@debian.org>
#   (C) 1999-2000 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 = "reportbug ##VERSION##"

import sys, string, getopt, re, os, tempfile, pwd, time, locale, commands
import rfc822, smtplib, reportbug, cStringIO, socket, readline

# Alias
def ewrite(message, *args):
    if commands.getoutput('tty') == 'not a tty':
        return

    if args:
        sys.stderr.write(message % args)
    else:
        sys.stderr.write(message)

# Safe open, prevents filename races in shared tmp dirs
# Based on python-1.5.2/Lib/tempfile.py
def open_write_safe(filename, mode='w+b', bufsize=-1):
    fd = os.open(filename, os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0600)
    try:
        return os.fdopen(fd, mode, bufsize)
    except:
        os.close(fd)
        raise

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

    edname = os.path.basename(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 = ''

    if '&' in editor:
        ewrite("Spawning %s in background; please press Enter when done "
               "editing.\n", edname)
    else:
        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)

    if '&' in editor: return (None, 1)

    newmessage = open(filename).read()

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

    return (newmessage, newmessage != message)

def find_package_for(filename, notatty=0):
    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)

        if notatty:
            print "Please rerun reportbug selecting one of these packages:"
            for package in packlist:
                print "  "+package
            sys.exit(1)
            
        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(system='debian'):
    others = reportbug.SYSTEMS[system]['otherpkgs']
    if others:
        ewrite("Please enter the name of the package in which you have found "
               "a problem,\nor type one of these bug categories:\n\n")
        olist = others.keys()
        olist.sort()
        for package in olist:
            ewrite("%-16.16s  %-62.62s\n", package, others[package])
        ewrite("\nEnter a package: ")
    else:
        ewrite("Please enter the name of the package in which you have found "
               "a problem:\n> ")
    package = raw_input()
    return package

def select_options(msg, ok, help=None, allow_numbers=0):
    for option in ok:
        if option in string.uppercase:
            default=option
            break
    
    if not help: help = {}

    if '?' not in ok: ok = ok+'?'

    ewrite(msg+' ['+string.join(ok, '|')+']? ')
    ch = raw_input()
    # Allow entry of a bug number here
    if allow_numbers:
        while ch and ch[0] == '#': ch = ch[1:]
        try:
            return str(int(ch))
        except ValueError:
            pass
    if not ch: ch = default
    ch = ch[0]
    if ch=='?':
        help['?'] = 'Display this help.'
        for ch in ok:
            if ch in string.uppercase:
                desc = '(default) '
            else:
                desc = ''

            ch = string.lower(ch)
            desc = desc + help.get(ch, 'No help for this option.')
            ewrite('%s - %s\n' % (ch, desc))
        return select_options(msg, ok, help)
    elif (string.lower(ch) in ok) or (string.upper(ch) in ok):
        return string.lower(ch)
    else:
        ewrite('Invalid selection.\n')
        return select_options(msg, ok, help)

def handle_bts_query(package, system, use_ldap, mirrors=None,
                     http_proxy=''):
    import debianbts
    
    try:
        r, c = string.split(commands.getoutput('stty size'))
    except ValueError:
        # Bypass if we don't have a tty.
        return

    ewrite('Querying %s bug tracking system for reports on %s\n'
           % (reportbug.SYSTEMS[system]['name'], package))

    try:
        (count, title, hierarchy) =debianbts.get_reports(package, system,
                                                         ldap_ok=use_ldap,
                                                         mirrors=mirrors,
                                                         http_proxy=http_proxy)

        if reportbug.SYSTEMS[system].has_key('namefmt'):
            package2 = reportbug.SYSTEMS[system]['namefmt'] % package
            (count2, title2, hierarchy2) = \
                     debianbts.get_reports(package2, system, ldap_ok=use_ldap,
                                           mirrors=mirrors,
                                           http_proxy=http_proxy)
            count = count+count2
            for entry in hierarchy2:
                hierarchy.append( (package2+' '+entry[0], entry[1]) )

        if not count:
            ewrite('No outstanding bugs.\n')
        else:
            rows, columns = int(r), int(c)
            # Reasonable defaults
            rows = rows or 24
            columns = columns or 79
            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)'

                    options = 'yNmi'
                    if skipmsg: options = options + 's'

                    range = "(%d-%d/%d) " % (startcount, endcount, count)
                    while 1:
                        x = select_options(range+"Is the bug you found "
                                           "listed above"+skipmsg,
                                           options,
                                           {'y' : 'Problem already '
                                            'reported; exit reportbug.',
                                            'n' : 'Problem not listed '
                                            'above; possibly check more.',
                                            'm' : 'Get more information '
                                            'about a particular bug.',
                                            's' : 'Skip remaining '
                                            'problems; file a new report '
                                            'immediately.'},
                                           allow_numbers=1)
                        if x == 'n':
                            break
                        elif x == 's':
                            category = hierarchy[-1]
                            break
                        elif x == 'y':
                            ewrite('Enter the number of the bug report '
                                   'you want to give more info on,\n'
                                   'or press ENTER to exit: #')
                            number = raw_input()
                            while number and number[0] == '#':
                                number=number[1:]
                            if number:
                                try:
                                    number = str(int(number))
                                    return number
                                except:
                                    ewrite('Invalid report number: %s\n' %
                                           number)
                                    pass
                            else:
                                ewrite('Nothing new to report; exiting.\n')
                                sys.exit(1)
                        else:
                            if x == 'm':
                                ewrite('Please enter the number of the bug '
                                       'you would like more info on: #')
                                number = raw_input()
                            else:
                                number = x
                            
                            while number and number[0] == '#':
                                number=number[1:]
                            if number:
                                try:
                                    number = int(number)
                                    info = debianbts.get_report\
                                           (number, system, ldap_ok=use_ldap,
                                            mirrors=mirrors,
                                            http_proxy=http_proxy)
                                    if info:
                                        (title, body) = info
                                        pos = string.find(body,
                                                          '-- System Info')
                                        if pos >= 0:
                                            body = body[:pos]

                                        ewrite(title+'\n\n'+body)
                                except:
                                    ewrite('No report available: #%s\n' %
                                           number)
                        
                    startcount = endcount+1
                    scount = 0

                if category == hierarchy[-1]: break

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

        debianbts.close_ldap_conn()

    except IOError:
        ewrite('Unable to connect to BTS.\n')

USAGE = ("reportbug - Report a bug to a debbugs bug tracking system.\n\n"
         "Usage: reportbug [options] <package>\n"
         "Supported options (see man page for long forms):\n"
         "  -a: Use af instead of an editor for filing the report.\n"
         "  -b: Don't query the BTS for existing bug reports.\n"
         "  -B: Specify an alternate debbugs BTS. *\n"
         "  -c: Don't include config files in bug reports.\n" 
         "  -d: Send bug report to postmaster@localhost, not the BTS.\n"
         "  -f: Report a bug in the specified file, looking up the package.\n"
         "  -g: Sign the bug report with GnuPG.\n"
         "  -i: Include specified file in bug report.\n"
         "  -h: Display this help message.\n"
         "  -H: Add a header to the outgoing email.\n"
         "  -l: Disable LDAP access to BTSes that support it.\n"
         "  -m: Send email to the maintainer only.\n"
         "  -M: Use mutt instead of an editor for filing the report.\n"
         "  -n: Use nmh/mh instead of an editor for filing the report.\n"
         "  -o: Output bug report to a file.\n"
         "  -p: Don't send any email; write bug template to standard output.\n"
         "  -P: Sign the bug report with PGP.\n"
         "  -q: Register bug in BTS; no email to maintainer [Don't do this!]\n"
         "  -s: Specify the summary of the report on the command line.\n"
         "  -S: Specify the severity of the bug. *\n"
         "  -v: Show the version number of this program.\n"
         "  -x: Don't send a copy of the report to yourself.\n"
         "  -z: Don't compress the configuration files.\n"
         "\nOptions marked * take the word 'help' to list allowed options.\n"
         )

def main():
    sendto = 'submit'
    searchfor = severity = subject = outfile = sign = mua = ''
    http_proxy = realname = email = replyto = smtphost = ''
    headers = []
    include = []
    mirrors = []
    nocc = exinfo = noconf = nocompress = printonly = dontquery = notatty = 0
    system = 'debian'
    use_ldap = 1

    if commands.getoutput('tty') == 'not a tty':
        dontquery = 1
        severity = 'normal'
        notatty = printonly = 1

    args = reportbug.parse_config_files()
    for option, arg in args.items():
        if option == 'sendto':
            sendto = arg
        elif option == 'severity':
            severity = arg
        elif option == 'mua':
            mua = arg
        elif option == 'headers':
            headers = arg
        elif option == 'nocc':
            nocc = arg
        elif option == 'noconf':
            noconf = arg
        elif option == 'nocompress':
            nocompress = arg
        elif option == 'printonly':
            printonly = arg
        elif option == 'dontquery':
            dontquery = arg
        elif option == 'ldap':
            use_ldap = arg
        elif option == 'system':
            system = arg
        elif option == 'mirrors':
            mirrors = arg
        elif option == 'sign':
            sign = arg
        elif option == 'http_proxy':
            http_proxy = arg
        elif option == 'realname':
            realname = arg
        elif option == 'email':
            email = arg
        elif option == 'replyto':
            replyto = arg
        elif option == 'smtphost':
            smtphost = arg

    try:
        (opts, args) = getopt.getopt(sys.argv[1:],
                                     'habB:cdf:gH:i:lmMno:pPqs: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',
                                      'no-ldap', 'output=', 'no-bts-query',
                                      'bts=', 'ldap', 'gpg', 'gnupg', 'pgp',
                                      'email=', 'replyto=', 'reply-to=',
                                      'smtphost=', 'bts-query',
                                      'http_proxy=', 'proxy=', 'realname='])
    except getopt.error, msg:
        ewrite(msg+'\n')
        sys.exit(1)

    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 = 'lawrencc@debian.org'
        elif option in ('-f', '--filename'):
            searchfor = arg
        elif option in ('-g', '--gnupg', '--gpg'):
            sign = 'gpg'
        elif option in ('-H', '--header'):
            headers.append(arg)
        elif option in ('-m', '--maintonly'):
            sendto = 'maintonly'
        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 ('-P', '--pgp'):
            sign = 'pgp'
        elif option in ('-q', '--quiet'):
            sendto = 'quiet'
        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
            printonly = 0
        elif option in ('-i', '--include'):
            include.append(arg)
        elif option in ('-b', '--no-bts-query'):
            dontquery = 1
        elif option == '--bts-query':
            dontquery = 0
        elif option in ('-l', '--no-ldap'):
            use_ldap = 0
        elif option == '--ldap':
            use_ldap = 1
        elif option in ('--proxy', '--http_proxy'):
            http_proxy = arg
        elif option == '--email':
            email = arg
        elif option == '--realname':
            realname = arg
        elif option in ('--replyto', '--reply-to'):
            replyto = arg
        elif option == '--smtphost':
            smtphost = arg
        elif option in ('-B', '--bts'):
            if arg in reportbug.SYSTEMS.keys():
                system = arg
            elif arg == 'help':
                ewrite('Permitted arguments to --bts:\n')
                names = reportbug.SYSTEMS.keys()
                names.sort()
                for bsys in names:
                    ewrite(' %-11.11s %s\n' %
                           (bsys, reportbug.SYSTEMS[bsys]['name']))
                sys.exit(0)
            else:
                ewrite("Ignoring unknown BTS server %s\n" % arg)
        elif option in ('-S', '--severity'):
            if arg in reportbug.SEVERITIES.values():
                severity = arg
            elif arg in reportbug.SEVERITIES.keys():
                severity = reportbug.SEVERITIES[arg]
            elif arg == 'help':
                ewrite('Permitted severity levels: ')
                ewrite(string.join(reportbug.SEVERITIES.values())+'\n')
                sys.exit(0)
            else:
                ewrite("Ignored bogus severity level %s\n" % arg)

    # Disable signatures when in printonly or mua mode
    # (since they'll be bogus anyway)
    if (mua or printonly) and sign:
        sign = ''

    # Disable smtphost if mua is set
    if mua and smtphost:
        smtphost = ''

    sysinfo = reportbug.SYSTEMS[system]

    uid = os.getuid()
    if not uid:
        if notatty: 
            ewrite("reportbug will not run as root non-interactively.\n")
            sys.exit(1)
            
        x = select_options("Running 'reportbug' as root is probably "
                           "insecure!\nContinue", 'yN',
                           {'y': 'Continue with reportbug.',
                            'n': 'Exit.'})
        if x == 'n':
            ewrite("reportbug stopped.\n")
            sys.exit(1)

    foundfile = None
    if len(args) == 0 and not searchfor and not notatty:
        package = get_package_name(system)
    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, notatty)
    else:
        package = args[0]

    if not package:
        ewrite("No package specified; stopping.\n")
        sys.exit(1)

    if not dontquery:
        exinfo = handle_bts_query(package, system, use_ldap, mirrors,
                                  http_proxy)

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

    ccaddr = os.environ.get('CC')
    # Ignore this if someone's specifying a compiler
    if ccaddr and '@' not in ccaddr: ccaddr = None
    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 = maintainer = ''
    conffiles = []
    isvirtual = package in sysinfo['otherpkgs'].keys()
    if sysinfo['query-dpkg']:
        ewrite("Getting status for %s...\n" % package)
        (pkgversion, pkgavail, depends, conffiles, maintainer) = \
                     reportbug.get_package_status(package)

        if not pkgavail and not notatty and not isvirtual:
            response = select_options('This package does not appear to exist; '
                                      'continue', 'yN',
                                      {'y': 'Ignore this problem and '
                                       'continue.',
                                       'n': 'Exit without filing a report.'})
            if response == 'n':
                ewrite("Package does not exist; stopping.\n")
                sys.exit(1)
        elif not pkgversion and not notatty and not isvirtual:
            response = select_options('This package does not appear to be '
                                      'installed; continue', 'yN',
                                      {'y': 'Ignore this problem and '
                                       'continue.',
                                       'n': 'Exit without filing a report.'})
            if response == 'n':
                ewrite("Package not installed; stopping.\n")
                sys.exit(1)

    if not pkgversion:
        pkgversion = time.strftime("N/A; reported %Y-%m-%d",
                                   time.localtime(time.time())) 

    if maintainer:
        ewrite("Maintainer for %s is %s\n" % (package, maintainer))

    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 = []
    conftext = ''
    remonly = 0
    if conffiles:
        ewrite("Getting changed configuration files...\n")
        confinfo, changed = reportbug.get_changed_config_files(conffiles,
                                                               nocompress)

    if not conffiles:
        pass
    elif noconf and changed:
        for file in changed:
            confinfo[file] = 'changed [not included]'
    elif changed and not notatty:
        while 1:
            x = select_options("*** WARNING: The following configuration "
                               "files have been modified:\n"+
                               string.join(changed, '\n')+
                               "\nSend modified configuration files", 'Ynd',
                               {'y':'Send your modified configuration files.',
                                'n':"Don't send modified configuration files.",
                                'd':'Display modified configuration files.'})
            if x == 'n':
                for file in changed:
                    confinfo[file] = 'changed [not included]'
                break
            elif x == 'd':
                os.system('/usr/bin/pager '+string.join(files, ' '))
            else:
                break   

    if confinfo:
        conftext = '\n-- Configuration Files:\n'
        files = confinfo.keys()
        files.sort()
        for file in files:
            conftext = conftext + '%s %s\n' % (file, confinfo[file])

    if exinfo:
        ewrite('Provide a subject for your response below (no subject '
               'will stop reportbug).\n> ')
        subject = raw_input()
        if not subject:
            ewrite("No subject specified; stopping.\n")
            sys.exit(1)
    elif not subject and not notatty:
        ewrite('Please briefly describe your problem (you can elaborate in '
               'a moment).\n> ')
        subject = raw_input()
        if not subject:
            ewrite("No description specified; stopping.\n")
            sys.exit(1)
    
    if not severity and not notatty and not exinfo:
    	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. 
	  
Please select a severity level [normal]: """)
	
	severity = raw_input()
        if not severity: severity='normal'
	while(severity not in reportbug.SEVERITIES.values()): 
		ewrite("Invalid severity level, please try again [normal]: ")
		severity = raw_input()
                if not severity: severity='normal'
	
    # Prepare bug report
    message = reportbug.generate_blank_report(package, pkgversion, severity, 
                                              depinfo, conftext, foundfile,
                                              incfiles, system)

    # Substitute server email address
    if '@' not in sendto:
        if exinfo:
            if sendto != 'submit':
                sendto = '%d-%s' % (exinfo, sendto)
            else:
                sendto = str(exinfo)
        
        sendto = sysinfo['email'] % sendto
        name = reportbug.quote_if_needed(sysinfo['name']+
                                         ' Bug Tracking System')
        sendto = '%s <%s>' % (name, sendto)

    tempfile.tempdir = os.environ.get('TMPDIR', '/tmp')
    tempfile.template = 'reportbug.'+`os.getpid()`+'.'
    filename = tempfile.mktemp()

    if printonly:
        report = sys.stdout
    elif mua:
        report = open_write_safe(filename, 'w')
    else:
        dmessage = "Subject: %s\n%s" % (subject, message)
        open_write_safe(filename, 'w').write(dmessage)
        message = None
        while 1:
            (message, changed) = spawn_editor(message or dmessage, filename)
            if not message:
                x = ''
                while x != 'y':
                    x = select_options('Done editing', 'Ynq',
                                       {'y': 'Continue (editing done).',
                                        'n': "Don't continue yet.",
                                        'q': 'Exit without sending report.'})
                    if x == 'q':
                        ewrite('Aborted; report stored as %s.\n' % filename)
                        sys.exit(1)
                
                message = file.open(filename).read()
                changed = 1
            
            if outfile:
                ewrite("Report will be saved as %s\n" % outfile)
            else:
                ewrite("Report will be sent to %s\n" % sendto)

            options = "Yne"
            if not changed: options = "ynE"
            
            x = select_options('Submit this bug report (e to edit)', options,
                               {'y': 'Submit the bug report via email.',
                                'n': "Don't submit the bug report; instead, "
                                'save it in a temporary file.',
                                'e': 'Re-edit the bug report.'})
            if x == 'n':
                ewrite('Stopped; report stored as %s.\n' % filename)
                sys.exit(1)
            elif x == 'y':
                if message == dmessage:
                    options = 'nE'
                    x = select_options('Report is unchanged.  Edit '
                                       'this report or quit',
                                       'qE',
                                       {'q': "Don't submit the bug report; "
                                        'instead, save it in a temporary file '
                                        'and quit.',
                                        'e': 'Re-edit the bug report.'})
                    if x == 'q':
                        ewrite('Stopped; report stored as %s.\n' % filename)
                        sys.exit(1)
                        break
                else:
                    break

        message = open(filename).read()
        
        if not sendto:
            print message,
            # Remove the temporary file
            if os.path.exists(filename): os.unlink(filename)
            return

        # Handle non-pseduo-headers
        headerre = re.compile(r'^([^:]*):\s+(.*)$', re.I)
        lines = string.split(message, '\n')
        newsubject = message = ''
        parsing = lastpseudo = 1
        pseudoheaders = {}
        for line in lines:
            if not line and parsing: parsing = 0
            elif parsing:
                mob = headerre.match(line)
                if mob and mob.group(1) not in reportbug.PSEUDOHEADERS:
                    if string.lower(mob.group(1)) == 'subject':
                        # Continuation subject lines probably due to reporter
                        # being clueless
                        newsubject = mob.group(2)
                        lastpseudo = 1
                    else:
                        headers.append('%s: %s' % mob.groups())
                        lastpseudo = 0
                    continue
                elif mob:
                    # Normalize pseudo-header
                    lastpseudo = 0
                    key, value = mob.groups()
                    key = string.capitalize(key)
                    pseudoheaders[key] = value
                elif not lastpseudo:
                    # Assume we have a continuation line
                    headers[-1] = headers[-1] + '\n' + line
                    continue
            else:
                message = message + line + '\n'

        ph = ''
        for header in reportbug.PSEUDOHEADERS:
            if pseudoheaders.has_key(header):
                ph = ph + '%s: %s\n' % (header, pseudoheaders[header])

        if ph:
            message = ph + '\n' + message

        if newsubject:
            subject = newsubject

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

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

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

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

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

    failed = 0
    usingpipe = 0
    msgname = ''
    if mua or printonly:
        pipe = report
    elif outfile or not os.path.exists('/usr/sbin/sendmail'):
        msgname = outfile or ('/var/tmp/%s.bug' % package)
        if os.path.exists(msgname): os.rename(msgname, msgname+'~')
        pipe = open_write_safe(msgname, 'w')
    elif not smtphost:
        pipe = os.popen('/usr/sbin/sendmail -t -oi -oem', 'w')
        usingpipe = 1

    if sign:
        ewrite('Passing message to %s for signature...\n' % sign)
        tmpfile2 = tempfile.mktemp()
        open_write_safe(tmpfile2, 'w').write(message)
        if sign == 'gpg':
            os.system("gpg --clearsign '"+tmpfile2+"'")
        elif sign == 'pgp':
            os.system("pgp -sat '"+tmpfile2+"'")

        ascfile = tmpfile2+'.asc'
        if not os.path.exists(ascfile):
            ewrite('gpg/pgp failed; input file in %s\n' % tmpfile2)
            os.exit(1)

        message = open(ascfile).read()
        os.unlink(ascfile)
        os.unlink(tmpfile2)

    if smtphost:
        message = string.join(headers, '\n') + '\n\n' + message
        sio = cStringIO.StringIO(message)
        msg = rfc822.Message(sio)
        alist = msg.getaddrlist('To')
        alist = alist + msg.getaddrlist('Cc')
        alist = alist + msg.getaddrlist('Bcc')
        
        toaddrs = map(lambda x: x[1], alist)
        # print toaddrs

        ewrite("Connecting to %s...\n" % smtphost)
        try:
            conn = smtplib.SMTP(smtphost)
            conn.sendmail(fromaddr, toaddrs, message)
            conn.quit()
        except (socket.error, smtplib.SMTPException), x:
            failed = 1
            msgname = '/var/tmp/%s.bug' % package
            ewrite('SMTP send failure: %s\n' % x)
            ewrite('Writing bug report to %s\n' % msgname)
            if os.path.exists(msgname): os.rename(msgname, msgname+"~")
            open_write_safe(msgname, 'w').write(message)

        del msg
        sio.close()
    else:
        pipe.write(string.join(headers, '\n') + '\n\n' + message)

        if msgname:
            ewrite("Bug report written as %s\n" % msgname)
    
        if pipe.close() and usingpipe:
            failed = 1
            msgname = '/var/tmp/%s.bug' % package
            if os.path.exists(msgname): os.rename(msgname, msgname+"~")
            ewrite('sendmail failed; writing bug report to %s\n' % msgname)
            open_write_safe(msgname, 'w').write(string.join(headers, '\n') +
                                                '\n\n' + message)

    if mua:
        ewrite("Spawning %s...\n" % string.split(mua, ' ')[0])
        os.system("%s %s" % (mua, filename))
    elif not failed and (usingpipe or smtphost):
        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)
            
        if not exinfo:
            ewrite(
"""\nIf you want to submit further information about this bug, please wait to
receive the bug tracking number via email.  Then send 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

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        ewrite("reportbug: exiting due to user interrupt.\n")
