Samx Here
n1udSecurity


Server : Apache
System : Linux ks5.tuic.fr 6.1.0-18-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.76-1 (2024-02-01) x86_64
User : pragmatice ( 1003)
PHP Version : 8.2.24
Disable Function : NONE
Directory :  /bin/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : //bin/reportbug
#!/usr/bin/python3
# -*- python -*-
# reportbug - Report a bug in the Debian distribution.
#   Written by Chris Lawrence <lawrencc@debian.org>
#   Copyright (C) 1999-2008 Chris Lawrence
#   Copyright (C) 2008-2022 Sandro Tosi <morph@debian.org>
#
# 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.

import sys

import os
import optparse
import re
import locale
import requests
import subprocess
import shlex
import email
import gettext
import textwrap
from contextlib import suppress
# for blogging of attachments file
from glob import glob

from reportbug import utils
from reportbug import (
    VERSION,
    VERSION_NUMBER,
    COPYRIGHT,
    LICENSE
)
from reportbug.utils import (
    MODE_EXPERT, MODE_ADVANCED, MODE_NOVICE, MODE_STANDARD,
)
from reportbug.tempfiles import (
    TempFile,
    tempfile_prefix,
    cleanup_temp_file,
)
from reportbug.exceptions import (
    UINotImportable, UINotImplemented,
    NoNetwork, NoPackage, NoBugs, NoReport, QuertBTSError,
)
from reportbug import mailer
from reportbug import submit
from reportbug import checkversions
from reportbug import debbugs
from reportbug import checkbuildd
import reportbug.ui.text_ui as ui

from reportbug.ui import (
    AVAILABLE_UIS, UNAVAILABLE_UIS
)

with suppress(OSError):
    gettext.install('reportbug')

DEFAULT_BTS = 'debian'

# Magic constant time
MIN_USER_ID = 250
quietly = False


# Cheat for now.
# ewrite() may put stuff on the status bar or in message boxes depending on UI
def ewrite(*args):
    return quietly or ui.log_message(*args)


def efail(*args):
    ui.display_failure(*args)
    sys.exit(1)


def check_attachment_size(attachfile, maxsize):
    """Check if the attachment size is bigger than max allowed"""
    statinfo = os.stat(attachfile)
    attachsize = statinfo[6]
    return attachsize >= maxsize


def include_file_in_report(message, message_filename,
                           attachment_filenames, package_name,
                           include_filename, charset, inline=False, draftpath=None):
    """ Include a file in the report.

        :parameters:
            `message`
                The current text of the message.
            `message_filename`
                The current message filename.
            `attachment_filenames`
                List of current attachment filenames.
            `package_name`
                Name of the package for this report.
            `include_filename`
                Full pathname of the file to be included.
            `inline`
                If True, include the message inline with the message
                text. Otherwise, add the file path to the attachments.

        :return value:
            Tuple (`message`, `message_filename`, `attachments`) of
            values as modified during the process of including the new
            file.

        """
    if inline:
        try:
            with open(include_filename, errors='backslashreplace') as fp:
                message += '\n*** {}\n{}'.format(
                    include_filename,
                    fp.read())
            fp, temp_filename = TempFile(
                prefix=tempfile_prefix(package_name), dir=draftpath)
            fp.write(message)
            fp.close()
            os.unlink(message_filename)
            message_filename = temp_filename
        except OSError as exc:
            ui.display_failure('Unable to attach file %s\n%s\n',
                               include_filename, str(exc))
    else:
        attachment_filenames.append(include_filename)

    return (message, message_filename, attachment_filenames)


def handle_editing(filename, dmessage, options, sendto, attachments, package,
                   severity, mode, is_followup, is_source, editor=None,
                   charset='utf-8', tags='', resumed=False):
    if not editor:
        editor = options.editor
    editor = utils.which_editor(editor)
    message = None
    patch = False
    skip_editing = False
    # is this report just to be saved on a file ?
    justsave = False
    while True:
        if not skip_editing:
            (message, changed) = ui.spawn_editor(message or dmessage, filename,
                                                 editor, charset)
        skip_editing = False

        prompt = 'Submit this report on %s (e to edit)' % package

        if options.kudos:
            prompt = 'Send this message (e to edit)'
            ewrite("Message will be sent to %s\n", sendto)
        elif options.outfile:
            ewrite("Report will be saved as %s\n", options.outfile)
        else:
            ewrite("Report will be sent to %s\n", sendto)

        if attachments:
            ewrite('Attachments:\n')
            for name in attachments:
                ewrite(' %s\n', name)

        subject = re.search('^Subject: ', message, re.M | re.I)
        if not subject:
            ui.long_message('No subject found in message.  Please edit again.\n')

        menuopts = "Ynaceilmpqdt"

        if not changed or not subject:
            menuopts = "ynacEilmpqdt"

        # cfr Debian BTS #293361
        if package == 'wnpp':
            for itp_line in debbugs.itp_template.rsplit('\n'):
                # if the line is not empty and it's in the message the user wrote
                if itp_line in message and itp_line != '':
                    ewrite("Wrong line: %s\n", itp_line)
                    menuopts = "Eq"
                    prompt = ("ERROR: you have composed a WNPP bug report with fields "
                              "unchanged from the template; this will NOT be submitted. "
                              "Please edit all fields so they contain correct values (e to edit)")

        if options.outfile:
            yesmessage = 'Save the report into %s .' % options.outfile
        else:
            yesmessage = 'Submit the bug report via email.'

        x = ui.select_options(prompt, menuopts,
                              {'y': yesmessage,
                               'n': "Don't submit the bug report; instead, "
                                    "save it in a temporary file (exits reportbug).",
                               'q': "Save it in a temporary file and quit.",
                               'a': "Attach a file.",
                               'd': "Detach an attachment file.",
                               'i': "Include a text file.",
                               'c': "Change editor and re-edit.",
                               'e': 'Re-edit the bug report.',
                               'l': 'Pipe the message through the pager.',
                               'p': 'Print message to stdout.',
                               't': 'Add tags.',
                               'm': "Choose a mailer to edit the report."})

        if x in ('a', 'i'):
            invalid = True
            while invalid:
                if x == 'i':
                    attachfile = ui.get_filename('Choose a text file to include: ')
                else:
                    attachfile = ui.get_filename('Choose a file to attach: ')
                if attachfile:
                    # expand vars & glob the input string
                    attachfile = os.path.expanduser(attachfile)
                    attachfglob = sorted(glob(attachfile), key=str.casefold)
                    # check if the globbing returns any result
                    if not attachfglob:
                        ui.display_failure("Can't find %s to include!\n", attachfile)
                    # loop over the globbed 'attachfile', you can specify wildcards now
                    for attachf in attachfglob:
                        if os.access(attachf, os.R_OK) and os.path.isfile(attachf):
                            if check_attachment_size(attachf, options.max_attachment_size):
                                ewrite('The attachment file %s size is bigger than the maximum of %d bytes: '
                                       'reduce its size else the report cannot be sent\n' %
                                       (attachf, options.max_attachment_size))
                            else:
                                invalid = False
                                inline = (x == 'i')
                                (message, filename, attachments) = include_file_in_report(
                                    message, filename, attachments, package,
                                    attachf, charset, inline=inline, draftpath=options.draftpath)
                                if not inline:
                                    skip_editing = True
                        else:
                            ui.display_failure("Cannot include %s!\n", attachf)
                else:
                    break
        elif x == 'd':
            skip_editing = True
            if not attachments:
                ewrite('No attachment file to detach.\n')
            else:
                detachprompt = 'Choose an attachment file to detach (an empty line will exit): '
                myattachments = []
                myattachments = [(x, '') for x in attachments]
                filetodetach = ui.menu(detachprompt, myattachments,
                                       'Select the file:', default='', empty_ok=True)
                # only if selection is not empty and the file is in the attachment list
                if filetodetach != '' and filetodetach in attachments:
                    attachments.remove(filetodetach)
                    ewrite('Attachment file "%s" successfully detached.\n\n', filetodetach)
                else:
                    ewrite('Nothing to detach.\n\n')
        elif x == 'c':
            ed = ui.get_filename('Choose editor: ', default=options.editor)
            if ed:
                editor = ed
        elif x == 'm':
            skip_editing = True
            mailers = [(x, '') for x in sorted(mailer.MUA.keys())
                       if mailer.mua_exists(x) and mailer.mua_can_run(x)]
            if not mailers:
                ui.display_failure('No mailers supported by reportbug found on this system.\n')
                continue
            mailprog = ui.menu('Choose a mailer for your report', mailers,
                               'Select mailer: ', default='', empty_ok=True)
            if mailprog and mailprog != -1:
                # get the MUA
                mailprog = mailer.MUA.get(mailprog)
                # if there are no attachments, directly go to the mailer
                if not attachments:
                    options.mua = mailprog
                    break

                # otherwise notify that they may be lost
                if ui.yes_no(
                        'Editing the report might lose all attachments: are you sure you want to continue?',
                        'Yes, please',
                        'No, thanks',
                        True):
                    # if ok, go into the MUA
                    options.mua = mailprog
                    break
                # else go back to the menu
                else:
                    pass
        elif x in ('n', 'q'):
            justsave = True
            break
        elif x in ('l', 'p'):
            skip_editing = True
            if x == 'l':
                pager = os.environ.get('PAGER', 'sensible-pager')
                with os.popen(pager, 'w') as p:
                    p.write(message)
            else:
                sys.stdout.write(message)
        elif x == 't':
            newtaglist = []
            skip_editing = True
            ntags = debbugs.get_tags(severity, mode)
            newtaglist = ui.select_multiple(
                'Do any of the following apply to this report?', ntags,
                'Please select tags: ')
            if newtaglist:
                tags_prefix = 'Control: tags -1 ' if is_followup else 'Tags: '
                if tags:
                    oldtags = tags_prefix + tags
                    newtaglist += tags.split()
                    # suppress twins in the tag list
                    newtaglist = list(set(newtaglist))
                    newtags = tags_prefix + ' '.join(newtaglist)
                else:
                    package_prefix = 'Source: ' if is_source else 'Package: '
                    oldtags = package_prefix + package + '\n'
                    newtags = oldtags + tags_prefix + ' '.join(newtaglist) + '\n'
                if 'patch' in newtaglist:
                    patch = True
                message = message.replace(oldtags, newtags)
                with open(filename, 'w', errors='backslashreplace') as f:
                    f.write(message)
        elif x == 'y':
            if message == dmessage and not resumed:
                x = ui.select_options(
                    'Report is unchanged.  Edit this report or quit', 'Eqs',
                    {'q': "Don't submit the bug report; instead, save it "
                          "in a temporary file and quit.",
                     'e': 'Re-edit the bug report.',
                     's': 'Send report anyway.'})
                if x == 'q':
                    justsave = True
                    break
                elif x == 's':
                    ewrite('Sending unmodified report anyway...\n')
                    break
            else:
                break

    return open(filename, errors='backslashreplace').read(), patch, justsave


def find_package_for(filename, notatty=False, pathonly=False):
    ewrite("Finding package for '%s'...\n", filename)
    (newfilename, packages) = utils.find_package_for(filename, pathonly)
    if newfilename != filename:
        filename = newfilename
        ewrite("Resolved as '%s'.\n", filename)
    if not packages:
        ewrite("No packages match.\n")
        return (filename, None)
    elif len(packages) > 1:
        packlist = list(packages.items())
        packlist.sort()

        if notatty:
            print("Please re-run reportbug selecting one of these packages:")
            for pkg, files in packlist:
                print("  " + pkg)
            sys.exit(1)

        packs = []
        for pkg, files in packlist:
            if len(files) > 3:
                files[3:] = ['...']
            packs.append((pkg, ', '.join(files)))

        package = ui.menu("Multiple packages match: ", packs, 'Select one '
                                                              'of these packages: ', any_ok=True)
        # for urwid, when pressing 'Cancel' in the menu
        if package == -1:
            package = None
        return (filename, package)
    else:
        package = list(packages.keys())[0]
        filename = packages[package][0]
        ewrite("Using package '%s'.\n", package)
        return (filename, package)


def validate_package_name(package):
    if not re.match(r'^(src:)?[a-z0-9][a-z0-9+.-]+$', package):
        ui.long_message("%s is not a valid package name.", package)
        package = None
    return package


def get_other_package_name(others):
    """Displays the list of pseudo-packages and returns the one selected."""

    result = ui.menu("Please enter the name of the package in which you "
                     "have found a problem, or choose one of these bug "
                     "categories:", others, "Enter a package: ", any_ok=True,
                     default='')
    if result:
        return result
    else:
        return None


def get_package_name(bts='debian', mode=MODE_EXPERT):
    others = debbugs.SYSTEMS[bts].get('otherpkgs')
    prompt = "Please enter the name of the package in which you have found " \
             "a problem"
    if others:
        prompt += ", or type 'other' to report a more general problem."
    else:
        prompt += '.'
    prompt += " If you don't know what package the bug is in, " \
              "please contact debian-user@lists.debian.org for assistance."

    options = []
    pkglist = subprocess.getoutput('apt-cache pkgnames')
    if pkglist:
        options += pkglist.split()
    if others:
        options += list(others.keys())

    package = None
    while package is None:
        package = ui.get_string(prompt, options, force_prompt=True)
        if not package:
            return
        if others and package and package == 'other':
            package = get_other_package_name(others)
        if not package:
            return
        package = validate_package_name(package)

    if package in ('kernel', 'linux-image'):
        ui.long_message(
            "Automatically selecting the package for the running kernel")
        package = utils.get_running_kernel_pkg()

    if mode < MODE_STANDARD:
        if package == 'reportbug':
            if not ui.yes_no('Is "reportbug" actually the package you are '
                             'having problems with?',
                             'Yes, I am actually experiencing a problem with '
                             'reportbug.',
                             'No, I really meant to file a bug report on '
                             'another package.'):
                return get_package_name(bts, mode)

    if mode < MODE_EXPERT:
        if package in ('bugs.debian.org', 'debbugs'):
            if ui.yes_no('Are you reporting a problem with this program (reportbug)?',
                         'Yes, this is actually a bug in reportbug.',
                         'No, this is really a problem in the bug tracking system itself.'):
                package = 'reportbug'

        if package in ('general', 'project', 'debian-general'):
            ui.long_message(
                "If you have a general problem, please do consider using "
                'the available Debian support channels to narrow the problem '
                'down. This will help us together to resolve the problem '
                'quicker. See https://www.debian.org/support')
            if not ui.yes_no(
                    "Are you sure this bug doesn't apply to a specific package?",
                    'Yes, this bug is truly general.',
                    'No, this is not really a general bug.', False):
                return get_package_name(bts, mode)

        if package == 'wnpp':
            if not ui.yes_no(
                    'Are you sure you want to file a WNPP report?',
                    'Yes, I am a developer or know what I\'m doing.',
                    'No, I am not a developer and I don\'t know what wnpp means.',
                    False):
                return get_package_name(bts, mode)

        if package in ('ftp.debian.org', 'release.debian.org'):
            if not ui.yes_no('Are you sure you want to file a bug on %s?' % (package),
                             'Yes, I am a developer or know what I\'m doing.',
                             'No, I am not a developer and I don\'t know what %s is.' % (package),
                             False):
                return get_package_name(bts, mode)

        if package in ('installation-report', 'upgrade-report'):
            package += 's'

    return package


def special_prompts(package, bts, ui, fromaddr, timeout, online, http_proxy):
    prompts = debbugs.SYSTEMS[bts].get('specials')
    if prompts:
        pkgprompts = prompts.get(package)
        if pkgprompts:
            return pkgprompts(package, bts, ui, fromaddr, timeout, online, http_proxy)
    return


def offer_configuration(options):
    charset = locale.nl_langinfo(locale.CODESET)
    # It would be nice if there were some canonical character set conversion
    if charset.lower() == 'ansi_x3.4-1968':
        charset = 'us-ascii'
    ui.charset = charset

    if not options.configure:
        ui.long_message('Welcome to reportbug!  Since it looks like this is '
                        'the first time you have used reportbug, we are '
                        'configuring its behavior.  These settings will be '
                        'saved to the file "%s", which you will be free to '
                        'edit further.\n\n', utils.USERFILE)
    mode = ui.menu('Please choose the default operating mode for reportbug.',
                   utils.MODES, 'Select mode: ', options.mode,
                   order=utils.MODELIST)

    if options.configure or not options.interface:
        # if there is only one UI available, the it's 'text', else ask
        if len(AVAILABLE_UIS) == 1:
            interface = 'text'
        else:
            interface = ui.menu(
                'Please choose the default interface for reportbug.', AVAILABLE_UIS,
                'Select interface: ', options.interface, order=['text'])
    else:
        interface = options.interface

    online = ui.yes_no('Will reportbug often have direct '
                       'Internet access?  (You should answer yes to this '
                       'question unless you know what you are doing and '
                       'plan to check whether duplicate reports have been '
                       'filed via some other channel.)',
                       'Yes, reportbug should assume it has access to the '
                       'network always.',
                       'No, I am only online occasionally to send and '
                       'receive mail.',
                       default=(not options.offline))

    def_realname, def_email = utils.get_email()

    try:
        if options.realname:
            realname = options.realname
        else:
            realname = def_realname
    except UnicodeDecodeError:
        realname = ''

    realname = ui.get_string('What real name should be used for sending bug '
                             'reports?', default=realname, force_prompt=True)
    realname = realname.replace('"', '\\"')

    is_addr_ok = False
    while not is_addr_ok:
        from_addr = ui.get_string(
            'Which of your email addresses should be used when sending bug '
            'reports? (Note that this address will be visible in the bug tracking '
            'system, so you may want to use a webmail address or another address '
            'with good spam filtering capabilities.)',
            default=(options.email or def_email), force_prompt=True)
        is_addr_ok = utils.check_email_addr(from_addr)
        if not is_addr_ok:
            ewrite('Your email address is not valid; please try another one.\n')
    stupidmode = not ui.yes_no(
        'Do you have a "mail transport agent" (MTA) like Exim, Postfix or '
        'SSMTP configured on this computer to send mail to the Internet?',
        'Yes, I can run /usr/sbin/sendmail without horrible things happening. '
        'If you can send email from this machine without setting an SMTP Host '
        'in your mailer, you should choose this answer.',
        'No, I need to use an SMTP Host or I don\'t know if I have an MTA.',
        (not options.smtphost) if options.smtphost else False)

    if stupidmode:
        opts = []
        if options.smtphost:
            opts += [options.smtphost]
        smtphost = ui.get_string(
            'Please enter the name of your SMTP host.  Usually it\'s called '
            'something like "mail.example.org" or "smtp.example.org". '
            'If you need to use a different port than default, use the '
            '<host>:<port> alternative format.\n\n'
            'Just press ENTER if you don\'t have one or don\'t know, and '
            'so a Debian SMTP host will be used.',
            options=opts, empty_ok=True, force_prompt=True)
        if smtphost:
            stupidmode = False
    else:
        smtphost = ''

    if smtphost:
        smtpuser = ui.get_string(
            ('If you need to use a user name to send email via "%s" on your '
             'computer, please enter that user name.  Just press ENTER if you '
             'don\'t need a user name.' % smtphost), empty_ok=True, force_prompt=True)
    else:
        smtpuser = ''

    if smtphost and not smtphost.endswith(':465'):
        smtptls = ui.yes_no(
            'Do you want to encrypt the SMTP connection with TLS (only '
            'available if the SMTP host supports STARTTLS)?', 'Yes', 'No',
            default=False)
    else:
        smtptls = False

    http_proxy = ui.get_string(
        'Please enter the name of your proxy server.  It should only '
        'use this parameter if you are behind a firewall. '
        'The PROXY argument should be  formatted as a valid HTTP URL,'
        ' including (if necessary) a port number;'
        ' for example, http://192.168.1.1:3128/. '
        'Just press ENTER if you don\'t have one or don\'t know.',
        empty_ok=True, force_prompt=True)

    if os.path.exists(utils.USERFILE):
        try:
            os.rename(utils.USERFILE, utils.USERFILE + '~')
        except OSError:
            ui.display_failure('Unable to rename %s as %s~\n', utils.USERFILE,
                               utils.USERFILE)

    try:
        fd = os.open(utils.USERFILE, os.O_WRONLY | os.O_TRUNC | os.O_CREAT,
                     0o600)
    except OSError:
        efail('Unable to save %s; most likely, you do not have a '
              'home directory.  Please fix this before using '
              'reportbug again.\n', utils.USERFILE)

    fp = os.fdopen(fd, 'w', errors='backslashreplace')
    print('# reportbug preferences file', file=fp)
    print('# character encoding: %s' % charset, file=fp)
    print('# Version of reportbug this preferences file was written by', file=fp)
    print('reportbug_version "%s"' % VERSION_NUMBER, file=fp)
    print('# default operating mode: one of:', end=' ', file=fp)
    print(', '.join(utils.MODELIST), file=fp)
    print('mode %s' % mode, file=fp)
    print('# default user interface', file=fp)
    print('ui %s' % interface, file=fp)
    print('# offline disables querying information over the network', file=fp)
    if not online:
        print('offline', file=fp)
    else:
        print('#offline', file=fp)
    print('# name and email setting (if non-default)', file=fp)
    rn = 'realname "%s"'
    em = 'email "%s"'
    email_addy = (from_addr or options.email or def_email)
    email_name = (realname or options.realname or def_realname)

    if email_name != def_realname:
        print(rn % email_name, file=fp)
    else:
        print('# ' + (rn % email_name), file=fp)

    if email_addy != def_email:
        print(em % email_addy, file=fp)
    else:
        print('# ' + (em % email_addy), file=fp)

    uid = os.getuid()
    if uid < MIN_USER_ID:
        print('# Suppress user ID check for this user', file=fp)
        print('no-check-uid', file=fp)

    if smtphost:
        print('# Send all outgoing mail via the following host', file=fp)
        print('smtphost "%s"' % smtphost, file=fp)
        if smtpuser:
            print('smtpuser "%s"' % smtpuser, file=fp)
            print('#smtppasswd "my password here"', file=fp)
        else:
            print('# If you need to enter a user name and password:', file=fp)
            print('#smtpuser "my username here"', file=fp)
            print('#smtppasswd "my password here"', file=fp)
        if smtptls:
            print('# Require STARTTLS for the SMTP host connection', file=fp)
            print('smtptls', file=fp)
        else:
            print('# Enable this to use STARTTLS for the SMTP host connection', file=fp)
            print('#smtptls', file=fp)

    if http_proxy:
        print('# Your proxy server address', file=fp)
        print('http_proxy "%s"' % http_proxy, file=fp)

    if stupidmode:
        print('# Disable fallback mode by commenting out the following:', file=fp)
        print('no-cc', file=fp)
        print('list-cc-me', file=fp)
        print('smtphost reportbug.debian.org', file=fp)
    else:
        print('# If nothing else works, remove the # at the beginning', file=fp)
        print('# of the following three lines:', file=fp)
        print('#no-cc', file=fp)
        print('#list-cc-me', file=fp)
        print('#smtphost reportbug.debian.org', file=fp)

    print('# You can add other settings after this line.  See', file=fp)
    print('# /etc/reportbug.conf for a full listing of options.', file=fp)
    fp.close()
    if options.configure:
        ui.final_message('Default preferences file written.  To reconfigure, '
                         're-run reportbug with the "--configure" option.\n')
    else:
        ui.long_message('Default preferences file written.  To reconfigure, '
                        're-run reportbug with the "--configure" option.\n')


def verify_option(option, opt, value, parser, *args):
    heading, valid = args
    if value == 'help':
        ewrite('%s:\n %s\n' % (heading, '\n '.join(valid)))
        sys.exit(1)
    if opt in ['-u', '--interface', '--ui'] and value == 'gtk2':
        value = 'gtk'
    if value in valid:
        setattr(parser.values, option.dest, value)
    elif opt in ['-u', '--interface', '--ui'] and value in UNAVAILABLE_UIS:
        ewrite('Cannot use UI %s because of "%s", defaulting to "text"\n' % (value, UNAVAILABLE_UIS[value]))
    else:
        ewrite('Ignored bogus setting for %s: %s\n' % (opt, value))


def verify_append_option(option, opt, value, parser, *args):
    heading, valid = args
    # special case --tag: in valid we pass a function reference
    # as get_tags is dependent on the user mode, so we also have to convert
    # the mode to the integer value expected... FIXME
    if opt == '--tag' or opt == '-T':
        valid = sorted(valid(mode=utils.MODELIST.index(parser.values.mode)).keys()) + ['none']
    if value == 'help':
        ewrite('%s:\n %s\n' % (heading, '\n '.join(valid)))
        sys.exit(1)
    elif value in valid:
        try:
            getattr(parser.values, option.dest).append(value)
        except AttributeError:
            setattr(parser.values, option.dest, [value])
    else:
        ewrite('Ignored bogus setting for %s: %s\n' % (opt, value))


def main():
    global quietly, ui

    try:
        locale.setlocale(locale.LC_ALL, '')
    except locale.Error as x:
        print('*** Warning:', x, file=sys.stderr)

    charset = locale.nl_langinfo(locale.CODESET)
    # It would be nice if there were some canonical character set conversion
    if charset.lower() == 'ansi_x3.4-1968':
        charset = 'us-ascii'

    defaults = dict(sendto="submit", mode="novice", mta="/usr/sbin/sendmail",
                    check_available=True, query_src=True, debconf=True,
                    editor='', offline=False, verify=True, check_uid=True,
                    testmode=False, attachments=[], keyid='', body=None,
                    resume_saved=None,
                    bodyfile=None, smtptls=False, smtpuser='', smtppasswd='',
                    paranoid=False, mbox_reader_cmd=None)

    # Convention: consider `option.foo' names read-only; they always contain
    # the original value as determined by the cascade of command-line options
    # and configuration files.  When we need to adjust a value, we first say
    # "foo = options.foo" and then refer to just `foo'.
    args = utils.parse_config_files()
    for option, arg in list(args.items()):
        if option in utils.CONFIG_ARGS:
            defaults[option] = arg
        else:
            sys.stderr.write('Warning: untranslated token "%s"\n' % option)

    parser = optparse.OptionParser(
        usage='%prog [options] <package | filename>', version=VERSION)
    parser.set_defaults(**defaults)
    parser.add_option('-c', '--no-config-files', action="store_true",
                      dest='noconf', help='do not include conffiles in report')
    parser.add_option('-C', '--class', action='callback', type='string',
                      callback=verify_option, dest="klass", metavar='CLASS',
                      callback_args=('Permitted report classes',
                                     debbugs.CLASSLIST),
                      help='specify report class for GNATS BTSes')
    parser.add_option('-d', '--debug', action='store_true', default=False,
                      dest='debugmode', help='send report only to yourself')
    parser.add_option('--test', action="store_true", default=False,
                      dest="testmode",
                      help="operate in test mode (maintainer use only)")
    parser.add_option('-e', '--editor', dest='editor',
                      help='specify an editor for your report')
    parser.add_option('-f', '--filename', dest='searchfor',
                      help='report the bug against the package containing the specified file')
    parser.add_option('--from-buildd', dest='buildd_format',
                      help='parse information from buildd format: $source_$version')
    parser.add_option('--path', dest='pathonly', action="store_true",
                      default=False, help='only search the path with -f')
    parser.add_option('-g', '--gnupg', '--gpg', action='store_const',
                      dest='sign', const='gpg',
                      help='sign report with GNU Privacy Guard (GnuPG/gpg)')
    parser.add_option('-G', '--gnus', action='store_const', dest='mua',
                      const=mailer.MUA['gnus'],
                      help='send the report using Gnus')
    parser.add_option('--pgp', action='store_const', dest='sign',
                      const='pgp',
                      help='sign report with Pretty Good Privacy (PGP)')
    parser.add_option('-K', '--keyid', type="string", dest="keyid",
                      help="key ID to use for PGP/GnuPG signatures")
    parser.add_option('-H', '--header', action='append', dest='headers',
                      help='add a custom RFC2822 header to your report')
    parser.add_option('-P', '--pseudo-header', action='append', dest='pseudos',
                      help='add a custom pseudo-header to your report')
    parser.add_option('--license', action='store_true', default=False,
                      help='show copyright and license information')
    parser.add_option('-m', '--maintonly', action='store_const',
                      dest='sendto', const='maintonly',
                      help='send the report to the maintainer only')
    parser.add_option('-M', '--mutt', action='store_const', dest='mua',
                      const=mailer.MUA['mutt'],
                      help='send the report using mutt')
    parser.add_option('--mirror', action='append', help='add a BTS mirror',
                      dest='mirrors')
    parser.add_option('-n', '--mh', '--nmh', action='store_const', dest='mua',
                      help='send the report using mh/nmh',
                      const=mailer.MUA['mh'])
    parser.add_option('-N', '--bugnumber', action='store_true',
                      dest='bugnumber', help='specify a bug number to look for')
    parser.add_option('--mua', dest='mua',
                      help='send the report using the specified mail user agent')
    parser.add_option('--mta', dest='mta', help='send the report using the '
                                                'specified mail transport agent')
    parser.add_option('--list-cc', action='append', dest='listcc',
                      help='send a copy to the specified address')
    parser.add_option('--list-cc-me', action='store_true', dest='listccme',
                      help='send a copy to your detected email address')
    parser.add_option('-p', '--print', action='store_true', dest='printonly',
                      help='output the report to standard output only')
    parser.add_option('--report-quiet', action='store_const', dest='sendto',
                      const='quiet', help='file report without any mail to '
                                          'the maintainer or tracking lists')
    parser.add_option('-q', '--quiet', action='store_true', dest='quietly',
                      help='reduce the verbosity of the output', default=False)
    parser.add_option('-s', '--subject', help='the subject for your report')
    parser.add_option('-x', '--no-cc', dest='nocc', action='store_true',
                      help='do not send a copy of the report to yourself')
    parser.add_option('-z', '--no-compress', dest='nocompress',
                      action='store_true', help='do not strip blank lines '
                                                'and comments from config files')
    parser.add_option('-o', '--output', dest='outfile', help='output the report'
                                                             ' to the specified file (both mail headers and body)')
    parser.add_option('-O', '--offline', help='disable all external queries',
                      action='store_true')
    parser.add_option('-i', '--include', action='append',
                      help='include the specified file in the report')
    parser.add_option('-A', '--attach', action='append', dest='attachments',
                      help='attach the specified file to the report')
    parser.add_option('-b', '--no-query-bts', action='store_true',
                      dest='dontquery', help='do not query the BTS for reports')
    parser.add_option('--query-bts', action='store_false', dest='dontquery',
                      help='query the BTS for reports')
    parser.add_option('-T', '--tag', action='callback', dest='tags',
                      callback=verify_append_option, type='string',
                      callback_args=('Permitted tags',
                                     debbugs.get_tags),
                      help='add the specified tag to the report')
    parser.add_option('--http_proxy', '--proxy', help='use this proxy for '
                                                      'HTTP accesses')
    parser.add_option('--email', help='specify originating email address')
    parser.add_option('--realname', help='specify real name for your report')
    parser.add_option('--smtphost', help='specify SMTP server for mailing')
    parser.add_option('--tls', help='use TLS to talk to SMTP servers',
                      dest="smtptls", action='store_true')
    parser.add_option('--source', '--src', dest='source', default=False,
                      help='report the bug against the source package ',
                      action='store_true')
    parser.add_option('--smtpuser', help='username to use for SMTP')
    parser.add_option('--smtppasswd', help='password to use for SMTP')
    parser.add_option('--replyto', '--reply-to', help='specify Reply-To '
                                                      'address for your report')
    parser.add_option('--query-source', action='store_true', dest='query_src',
                      help='query on source packages, not binary packages')
    parser.add_option('--no-query-source', action='store_false',
                      dest='query_src', help='query on binary packages only')
    parser.add_option('--security-team', action='store_true', dest='secteam', default=None,
                      help='send the report only to the security team, if tag=security')
    parser.add_option('--no-security-team', action='store_false', default=None,
                      dest='secteam', help='do not send the report only to the security team, if tag=security')
    parser.add_option('--debconf', action='store_true',
                      help='include debconf settings in your report')
    parser.add_option('--no-debconf', action='store_false', dest='debconf',
                      help='exclude debconf settings from your report')
    parser.add_option('-j', '--justification', help='include justification '
                                                    'for the severity of your report')
    parser.add_option('-V', '--package-version', dest='pkgversion',
                      help='specify the version number for the package')
    parser.add_option('-u', '--interface', '--ui', action='callback',
                      callback=verify_option, type='string', dest='interface',
                      callback_args=('Valid user interfaces',
                                     list(AVAILABLE_UIS.keys())),
                      help='choose which user interface to use')
    parser.add_option('-Q', '--query-only', action='store_true',
                      dest='queryonly', help='only query the BTS')
    parser.add_option('-t', '--type', action='callback', dest='type',
                      callback=verify_option, type='string',
                      callback_args=('Valid types of report',
                                     ('gnats', 'debbugs')),
                      help='choose the type of report to file')
    parser.add_option('-B', '--bts', action='callback', dest='bts',
                      callback=verify_option, type='string',
                      callback_args=('Valid bug tracking systems',
                                     list(debbugs.SYSTEMS.keys())),
                      help='choose BTS to file the report with')
    parser.add_option('-S', '--severity', action='callback',
                      callback=verify_option, type='string', dest='severity',
                      callback_args=('Valid severities', debbugs.SEVLIST),
                      help='identify the severity of the report')
    parser.add_option('--template', action='store_true',
                      help='output a template report only')
    parser.add_option('--configure', action='store_true',
                      help='reconfigure reportbug for this user')
    parser.add_option('--check-available', action='store_true',
                      help='check for new releases on various sites')
    parser.add_option('--no-check-available', action='store_false',
                      dest='check_available', help='do not check for new '
                                                   'releases')
    parser.add_option('--mode', action='callback', help='choose the operating '
                                                        'mode for reportbug', callback=verify_option,
                      type='string', dest='mode',
                      callback_args=('Permitted operating modes',
                                     list(utils.MODES.keys())))
    parser.add_option('-v', '--verify', action='store_true', help='verify '
                                                                  'integrity of installed package using debsums')
    parser.add_option('--no-verify', action='store_false', dest='verify',
                      help='do not verify package installation')
    parser.add_option('-k', '--kudos', action='store_true', default=False,
                      help='send appreciative email to the maintainer, rather '
                           'than filing a bug report')
    parser.add_option('--body', dest="body", type="string",
                      help="specify the body for the report as a string")
    parser.add_option('--body-file', '--bodyfile', dest="bodyfile",
                      type="string",
                      help="use the specified file as the body of the report")
    parser.add_option('-r', '--resume-saved', dest="resume_saved",
                      type="string",
                      help="resume report from previously saved temporary file")
    parser.add_option('-I', '--no-check-installed', action='store_false',
                      default=True, dest='querydpkg',
                      help='don\'t check whether the package is installed')
    parser.add_option('--check-installed', action='store_true',
                      dest='querydpkg', help='check whether the specified '
                                             'package is installed when filing a report (default)')
    parser.add_option('--paranoid', action='store_true', dest='paranoid',
                      help='show contents of message before sending')
    parser.add_option('--no-paranoid', action='store_false', dest='paranoid',
                      help='don\'t show contents of message before sending '
                           '(default)')
    parser.add_option('--no-bug-script', dest="bugscript", default=True,
                      action='store_false',
                      help='don\'t execute the bug script (if present)')
    parser.add_option('--draftpath', dest="draftpath",
                      help='Save the draft in this directory')
    parser.add_option('--timeout', type="int", dest='timeout', default=60,
                      help='Specify the network timeout, in seconds [default: %default]')
    parser.add_option('--no-cc-menu', dest="ccmenu", default=True,
                      action='store_false',
                      help='don\'t show additional CC menu')
    parser.add_option('--no-tags-menu', dest="tagsmenu", default=True,
                      action='store_false',
                      help='don\'t show tags menu')
    parser.add_option('--mbox-reader-cmd', dest='mbox_reader_cmd',
                      help="Specify the program to open the reports mbox.")
    parser.add_option('--max-attachment-size', type="int", dest='max_attachment_size',
                      help="Specify the maximum size in byte for an attachment [default: 10485760].")
    parser.add_option('--latest-first', action='store_true', dest='latest_first', default=False,
                      help='Order bugs to show the latest first')
    parser.add_option('--envelope-from', dest='envelopefrom',
                      help='Specify the Envelope From (Return-path) address used to send the bug report')
    parser.add_option('--archive', dest='archived', action="store_true",
                      default=False, help='also show archived bugs')

    (options, args) = parser.parse_args()

    # if not set in config file or on cli, then set 10M as default
    if not options.max_attachment_size:
        options.max_attachment_size = 10485760

    # check if attachment files exist, else exiting
    # all are checked, and it doesn't exit at the first missing

    if options.email:
        if not utils.check_email_addr(options.email):
            ewrite('Your email address is not valid; exiting.\n')
            sys.exit(1)

    if options.attachments:
        # support glob
        globbed_attachments = []
        any_missing = False
        for attachment in options.attachments:
            globres = sorted(glob(attachment), key=str.casefold)
            if globres:
                globbed_attachments.extend(globres)
            else:
                ewrite(f"The attachment file '{attachment}' could not be found.\n")
                any_missing = True
        options.attachments = globbed_attachments
        for attachment in options.attachments:
            if check_attachment_size(attachment, options.max_attachment_size):
                ewrite('The attachment file %s size is bigger than the maximum of %d bytes: reduce '
                       'its size else the report cannot be sent.\n' % (attachment, options.max_attachment_size))
                any_missing = True
        if any_missing:
            ewrite("The above file(s) can't be attached; exiting.\n")
            sys.exit(1)

    if options.keyid and not options.sign:
        ewrite('Option -K/--keyid requires --gpg or --pgp sign option set, which currently is not; exiting.\n')
        sys.exit(1)

    if options.draftpath:
        options.draftpath = os.path.expanduser(options.draftpath)
        if not os.path.exists(options.draftpath):
            ewrite("The directory %s does not exist; exiting.\n" % options.draftpath)
            sys.exit(1)

    if options.mua and not options.template:
        if not mailer.mua_is_supported(options.mua):
            ewrite("Specified mail user agent is not supported; exiting.\n")
            sys.exit(1)

        if not mailer.mua_exists(options.mua):
            ewrite("Selected mail user agent cannot be found; exiting.\n")
            sys.exit(1)

        if not mailer.mua_can_run(options.mua):
            ewrite("Selected mail user agent cannot be run without graphical display; exiting.\n")
            sys.exit(1)

        # we want options.mua to be a mailer.Mua instance
        options.mua = mailer.MUA.get(options.mua, options.mua)

    if options.http_proxy:
        os.environ['http_proxy'] = options.http_proxy
        os.environ['https_proxy'] = options.http_proxy

    # try to import the specified UI, but only if template
    # is not set (it's useful only in 'text' UI).
    if options.interface and not options.template:
        interface = options.interface

        iface = '%(interface)s_ui' % vars()

        try:
            lib_package = __import__('reportbug.ui', fromlist=[iface])
            newui = getattr(lib_package, iface)
        except UINotImportable as msg:
            ui.long_message('*** Unable to import %s interface: %s '
                            'Falling back to text interface.\n',
                            interface, msg)
            ewrite('\n')

        if newui.initialize():
            ui = newui
            submit.ui = ui
        else:
            ui.long_message('*** Unable to initialize %s interface. '
                            'Falling back to text interface.\n',
                            interface)

        # Add INTERFACE as an environment variable to access it from the
        # script gathering the special information for reportbug, when
        # a new bug should be filed against it.
        os.environ['INTERFACE'] = interface

    if not ui.can_input():
        defaults.update({'dontquery': True, 'notatty': True,
                         'printonly': True})

    # force to report the bug against the source package if --from-buildd
    if options.buildd_format:
        options.source = True

    iface = UI(options, args)
    if not hasattr(ui, 'run_interface'):
        return iface.user_interface()
    return ui.run_interface(iface.user_interface)


class UI(object):
    def __init__(self, options, args):
        self.options = options
        self.args = args

    def user_interface(self):
        body = ''
        filename = None
        notatty = not ui.ISATTY

        charset = locale.nl_langinfo(locale.CODESET)
        # It would be nice if there were some canonical character set conversion
        if charset.lower() == 'ansi_x3.4-1968':
            charset = 'us-ascii'

        # Allow the UI to know what charset we're using
        ui.charset = charset

        if self.options.configure:
            offer_configuration(self.options)
            sys.exit(0)
        elif self.options.license:
            print(COPYRIGHT)
            print()
            print(LICENSE)
            sys.exit(0)

        # These option values may get adjusted below, so give them a variable name.
        sendto = self.options.sendto
        check_available = self.options.check_available
        dontquery = self.options.dontquery
        headers = self.options.headers or []
        pseudos = self.options.pseudos or []
        mua = self.options.mua
        pkgversion = self.options.pkgversion
        global quietly
        quietly = self.options.quietly
        severity = self.options.severity
        smtphost = self.options.smtphost
        subject = self.options.subject
        bts = self.options.bts or 'debian'
        sysinfo = debbugs.SYSTEMS[bts]
        rtype = self.options.type or sysinfo.get('type')
        attachments = self.options.attachments
        pgp_addr = self.options.keyid
        bugnumber = self.options.bugnumber
        bugscript = self.options.bugscript

        pkgavail = maintainer = origin = src_name = state = debsumsoutput = ''
        depends = []
        recommends = []
        suggests = []
        conffiles = []
        reportinfo = None
        issource = installed = usedavail = False
        status = None

        # if user specified a bug number on the command-line, don't query BTS
        if bugnumber:
            dontquery = True

        if self.options.body:
            body = textwrap.fill(self.options.body)
        elif self.options.bodyfile:
            try:
                if check_attachment_size(self.options.bodyfile, self.options.max_attachment_size):
                    print('Body file %s size bigger than the maximum of %d bytes: '
                          'reduce its size else the report cannot be sent' % (
                              self.options.bodyfile, self.options.max_attachment_size))
                    raise Exception
                with open(self.options.bodyfile, errors='backslashreplace') as bf:
                    body = bf.read()
            except Exception:
                efail('Unable to read body from file %s.\n', self.options.bodyfile)
        elif self.options.resume_saved:
            try:
                with open(self.options.resume_saved, 'rb') as fb:
                    msg = email.message_from_binary_file(fb, policy=email.policy.default)
                body = msg.get_body().get_content()
                subject = subject or msg.get('subject')
                if sendto == "submit":
                    sendto = msg.get('to')
                dontquery = True
                check_available = False
                bugscript = False
                severity = 'normal'
            except Exception:
                efail('Unable to read message from file %s.\n',
                      self.options.resume_saved)

        if body and not body.endswith('\n'):
            body += '\n'

        if self.options.queryonly:
            check_available = False

        if self.options.offline:
            check_available = False
            dontquery = True

        if self.options.tags:
            taglist = self.options.tags
            if 'none' in taglist:
                taglist = []
        else:
            taglist = []

        if self.options.testmode:
            self.options.debugmode = True
            self.options.tags = ['none']
            check_available = False
            dontquery = True
            severity = 'normal'
            subject = 'testing'
            taglist = []

        interactive = True
        if self.options.template:
            check_available = interactive = False
            dontquery = quietly = notatty = True
            mua = smtphost = None
            severity = severity or 'wishlist'
            subject = subject or 'none'
            taglist = taglist or []

        if self.options.outfile or self.options.printonly:
            mua = smtphost = None

        if smtphost and smtphost.lower() in ('master.debian.org', 'bugs.debian.org'):
            ui.long_message(
                "*** Warning: %s is no longer an appropriate smtphost setting for reportbug: "
                "it has been superseded by reportbug.debian.org and this one is forced as "
                "smtphost; please update your .reportbugrc file.\n",
                smtphost.lower())
            smtphost = 'reportbug.debian.org'

        if any(hd.startswith('X-Debbugs-CC: ') for hd in headers):
            ui.long_message(
                "*** Warning: You are trying to set an X-Debbugs-CC header. "
                "This is possibly an old default setting from your ~/.reportbugrc. "
                "In that case you may want to re-run 'reportbug --configure', or edit "
                "your configuration file to use the 'list-cc-me' command (without recipient "
                "address) instead. If you used the -H option on the command line, please "
                "see the '--list-cc' option. "
                "Reportbug cannot handle custom headers reliably with its MUA support, it is "
                "therefore recommended to use pseudoheaders instead where possible.\n")

        if utils.first_run():
            if not self.args and not self.options.searchfor:
                offer_configuration(self.options)
                # due to the multithreaded gtk UI we cannot just
                # call main() again, but need to re-execute reportbug
                os.execv(__file__, sys.argv)
                sys.exit(0)
            else:
                ewrite('Warning: no reportbug configuration found.  Proceeding in %s mode.\n' % self.options.mode)

        mode = utils.MODELIST.index(self.options.mode)

        # Disable signatures when in printonly or mua mode
        # (since they'll be bogus anyway)
        sign = self.options.sign
        if (self.options.mua or self.options.printonly) and sign:
            sign = ''
            if self.options.mua:
                ewrite('The signature option is ignored when using an MUA.\n')
            elif self.options.printonly:
                ewrite('The signature option is ignored when producing a template.\n')

        uid = os.getuid()
        if uid < MIN_USER_ID:
            if notatty and not uid:
                ewrite("reportbug will not run as root non-interactively.\n")
                sys.exit(1)

            if not uid or self.options.check_uid:
                if not uid:
                    message = "Running 'reportbug' as root is probably insecure!"
                else:
                    message = "Running 'reportbug' as an administrative user " \
                              "is probably not a good idea!"
                message += '  Continue?'

                if not ui.yes_no(message, 'Continue with reportbug.', 'Exit.',
                                 False):
                    ewrite("reportbug stopped.\n")
                    sys.exit(1)

        if (utils.first_run() and not self.args and not self.options.searchfor):
            offer_configuration(self.options)
            ewrite('To report a bug, please rerun reportbug.\n')
            sys.exit(0)

        if self.options.mta and not os.path.exists(self.options.mta) and not (
                self.options.mua or self.options.template or self.options.printonly
                or self.options.smtphost or self.options.outfile):
            ewrite(f"The MTA {self.options.mta} is not available; exiting.\n")
            ewrite("Please run 'reportbug --configure' or specify a submission method on the command line.\n")
            sys.exit(1)

        foundfile = None
        package = None
        if self.options.resume_saved:
            # dummy values for package and pkgversion; we use ones known
            # to exist to avoid any further questions. The real ones
            # will be taken from the resumed report anyway.
            package = "reportbug"
            pkgversion = VERSION_NUMBER
        elif not len(self.args) and not self.options.searchfor and not notatty and not self.options.buildd_format:
            package = get_package_name(bts, mode)
        elif self.options.buildd_format:
            # retrieve package name and version from the input string
            package, self.options.pkgversion = self.options.buildd_format.split('_')
            # TODO: fix it when refactoring
            # if not done as of below, it will ask for version when the package
            # is not available on the local system (try a dummy one, like foo_12-3)
            pkgversion = self.options.pkgversion
        elif len(self.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 self.options.searchfor:
            (foundfile, package) = find_package_for(self.options.searchfor, notatty,
                                                    self.options.pathonly)
        elif len(self.args):
            package = self.args[0]
            if package and package.startswith('/'):
                (foundfile, package) = find_package_for(package, notatty)
            elif package and self.options.source:
                # convert it to the source package if we are reporting for src
                package = utils.get_source_name(package)
            elif package.lower() in ('general', 'project', 'debian-general') and mode < MODE_EXPERT:
                ui.long_message(
                    "If you have a general problem, please do consider using "
                    'the available Debian support channels to narrow the problem '
                    'down. This will help us together to resolve the problem '
                    'quicker. See https://www.debian.org/support')
                if not ui.yes_no(
                        "Are you sure this bug doesn't apply to a specific package?",
                        'Yes, this bug is truly general.',
                        'No, this is not really a general bug.', False):
                    package = get_package_name(bts, mode)

        if package and package.startswith('src:'):
            package = package[4:]
            issource = True

        others = debbugs.SYSTEMS[bts].get('otherpkgs')
        if package == 'other' and others:
            package = get_other_package_name(others)

        if package in ('kernel', 'linux-image'):
            ui.long_message(
                "Automatically selecting the package for the running kernel")
            package = utils.get_running_kernel_pkg()

        if package in ('installation-report', 'upgrade-report') and mode < MODE_EXPERT:
            package += 's'

        if not package:
            efail("No package specified or we were unable to find it in the apt"
                  " cache; stopping.\n")

        tfprefix = tempfile_prefix(package)
        if self.options.interface == 'text':
            ewrite('*** Welcome to reportbug.  Use ? for help at prompts. ***\n')
        # we show this for the 2 "textual" UIs
        if self.options.interface in ('text', 'urwid'):
            ewrite('Note: bug reports are publicly archived (including the email address of the submitter).\n')

        try:
            _ = 'hello'
        except LookupError:
            ui.display_failure(
                'Unable to use specified character set "%s"; you probably need '
                'either cjkcodecs (for users of Asian locales) or iconvcodec '
                'installed.\nFalling back to ASCII encoding.\n', charset)
            charset = 'us-ascii'
        else:
            ewrite("Detected character set: %s\n"
                   "Please change your locale if this is incorrect.\n\n", charset)

        fromaddr = utils.get_user_id(self.options.email, self.options.realname, charset)
        if not utils.check_email_addr(email.utils.parseaddr(fromaddr)[1]):
            efail("Unable to identify a valid from address, please run 'reportbug --configure'\n")
        ewrite("Using '%s' as your from address.\n",
               str(email.header.make_header(email.header.decode_header(fromaddr))))
        if '@localhost' in fromaddr:
            ewrite("Please quit and run 'reportbug --configure' if this is not correct.\n")
        if self.options.debugmode:
            sendto = fromaddr

        edname = utils.which_editor(self.options.editor)
        baseedname = os.path.basename(edname)
        if baseedname == 'sensible-editor':
            edname = utils.realpath('/usr/bin/editor')

        if not notatty and 'vi' in baseedname and mode < MODE_STANDARD and 'EDITOR' not in os.environ:
            if not ui.yes_no('You appear to be using the "vi" editor, which is '
                             'not suited for new users.  You probably want to '
                             'change this setting by using "update-alternatives '
                             '--config editor" as root.  (You can bypass this '
                             'message in the future by using reportbug in '
                             '"standard" mode or higher.) '
                             'Do you want to continue?',
                             'Continue filing this report.',
                             'Stop reportbug to change editors.', False):
                ewrite('Exiting per user request.\n')
                sys.exit(1)

        incfiles = ""
        if self.options.include:
            for f in self.options.include:
                if os.path.exists(f):
                    with open(f, errors='backslashreplace') as fp:
                        incfiles += '\n*** {}\n{}'.format(f, fp.read())
                else:
                    ewrite("Can't find %s to include!\n", f)
                    sys.exit(1)
            incfiles += '\n'

        if self.options.source:
            issource = True

        exinfo = None
        # If user specified a bug number on the command line
        try:
            if bugnumber:
                reportre = re.compile(r'^#?(\d+)$')
                match = reportre.match(package)
                if match:
                    report = int(match.group(1))
                    exinfo = ui.show_report(report, 'debian', self.options.mirrors,
                                            self.options.http_proxy,
                                            self.options.timeout,
                                            queryonly=self.options.queryonly,
                                            title=VERSION,
                                            archived=self.options.archived,
                                            mbox_reader_cmd=self.options.mbox_reader_cmd)
                    # When asking to re-display the bugs list, None is returned
                    # given we're in the part of code that's executed when the
                    # user pass a bug number on the cli, so we'll exit
                    if exinfo is None:
                        raise NoReport
                    else:
                        package = exinfo.package or exinfo.source
                        subject = subject or exinfo.subject
                        if package == 'src:linux':
                            package = utils.get_running_kernel_pkg()
                        elif package.startswith('src:'):
                            package = package[4:]
                            issource = True
                else:
                    efail("The report bug number provided seems to not exist.\n")
        except NoBugs:
            efail('No such bug report.\n')
        except NoReport:
            efail('Exiting.\n')

        isvirtual = (package in list(sysinfo.get('otherpkgs', {}).keys())
                     and package not in sysinfo.get('nonvirtual', []))

        if issource and not pkgversion:
            # package is already ok here, just need the version
            pkgversion = utils.get_source_version(package)

        if not pkgversion and self.options.querydpkg and \
                sysinfo.get('query-dpkg', True) and \
                package not in list(debbugs.SYSTEMS[bts].get('otherpkgs').keys()):
            ewrite("Getting status for %s...\n", package)
            status = utils.get_package_status(package)

            pkgavail, installed = status[1], status[6]
            # Packages that only exist to do weird dependency things
            deppkgs = sysinfo.get('deppkgs')
            if pkgavail and deppkgs:
                if installed and package in deppkgs:
                    depends = status[2]
                    if depends:
                        newdepends = []
                        for x in depends:
                            newdepends.extend(x)
                        depends = newdepends
                        if len(depends) == 1:
                            if mode < MODE_ADVANCED:
                                ewrite('Dependency package "%s" corresponds to '
                                       'actual package "%s".\n', package, depends[0])
                                package = depends[0]
                        else:
                            opts = [(x, (utils.get_package_status(x)[11] or 'not installed'))
                                    for x in depends]
                            if mode >= MODE_ADVANCED:
                                opts += [(package,
                                          status[11] + ' (dependency package)')]

                            package = ui.menu('%s is a dependency package.  '
                                              'Which of the following '
                                              'packages is the bug in?' % package,
                                              opts,
                                              'Select one of these packages: ')
                        ewrite("Getting status for %s...\n", package)
                        status = utils.get_package_status(package)
                        pkgavail, installed = status[1], status[6]

            if not pkgavail and not isvirtual:
                # Look for a matching source package
                packages = utils.get_source_package(package)
                if len(packages) > 0:
                    if not notatty and len(packages) > 1:
                        package = ui.menu(
                            'Which of the following packages is the bug in?',
                            packages, empty_ok=True,
                            prompt='Select one of these packages: ')
                    else:
                        package = packages[0][0]

                    if not package:
                        efail("No package specified; stopping.\n")

                    if package.startswith('src:'):
                        package = package[4:]
                        issource = True
                        pkgversion = utils.get_source_version(package)
                    else:
                        ewrite("Getting status for %s...\n", package)
                        status = utils.get_package_status(package)
                        pkgavail, installed = status[1], status[6]
                else:
                    ewrite('No matching source or binary packages.\n')

            if (not installed and not isvirtual and not issource) and not notatty:
                packages = utils.packages_providing(package)
                tmp = pack = None
                if not packages:
                    if ui.yes_no('A package named "%s" does not appear to be installed; do '
                                 'you want to search for a similar-looking filename in '
                                 'an installed package?' % package,
                                 'Look for a file with a similar filename.',
                                 'Continue filing with this package name.', True):
                        pkgavail = False
                    else:
                        pack = package
                        packages = [(package, '')]
                        ewrite("Getting available info for %s...\n", package)
                        status = utils.get_package_status(package, avail=True)
                        check_available = False
                        usedavail = True

                if not packages and not pkgavail and not pack:
                    (tmp, pack) = find_package_for(package, notatty)
                    if pack:
                        status = None
                        if not ui.yes_no("A package named '%s' does not appear to be installed "
                                         "on your system; however, '%s' contains a file named "
                                         "'%s'.  Do you want to file your report on the "
                                         "package reportbug found?" % (package, pack, tmp),
                                         'Yes, use the package specified.',
                                         'No, give up the search.'):
                            efail("Package not installed; stopping.\n")

                if not status and pack:
                    foundfile, package = tmp, pack
                    ewrite("Getting status for %s...\n", package)
                    status = utils.get_package_status(package)
                elif not packages:
                    if not ui.yes_no(
                            'This package does not appear to be installed; continue '
                            'with this report?', 'Ignore this problem and continue.',
                            'Exit without filing a report.', False):
                        efail("Package not installed; stopping.\n")
                elif (len(packages) == 1) and (packages[0][0] != package):
                    if not ui.yes_no(
                            'This package does not appear to be installed: {}.\n '
                            'Do you want to file the report on package {} instead?'
                            .format(package, packages[0][0]),
                            'Yes, use the other package.',
                            'Exit without filing a report.',
                            True):
                        efail("Package not installed; stopping.\n")
                    else:
                        package = packages[0][0]
                        ewrite("Getting status for %s...\n", package)
                        status = utils.get_package_status(package)
                elif len(packages) > 1:
                    packages.sort()
                    package = ui.menu('Which of the following installed packages '
                                      'is the bug in?', packages,
                                      'Select one of these packages: ',
                                      empty_ok=True)
                    if not package:
                        efail("No package specified; stopping.\n")
                    else:
                        ewrite("Getting status for %s...\n", package)
                        status = utils.get_package_status(package)
            elif not pkgavail and not notatty and not isvirtual and not issource:
                if not ui.yes_no(
                        'This package does not appear to exist; continue?',
                        'Ignore this problem and continue.',
                        'Exit without filing a report.', False):
                    efail("Package does not exist; stopping.\n")
                    sys.exit(1)

            # we can use status only if it's not a source pkg
            if not issource:
                (pkgversion, pkgavail, depends, recommends, conffiles, maintainer,
                 installed, origin, vendor, reportinfo, priority, desc, src_name,
                 fulldesc, state, suggests, section) = status

        buginfo = '/usr/share/bug/' + package
        bugexec = submitas = submitto = presubj = None
        reportwith = []
        supplemental = []
        if self.options.resume_saved:
            pass
        elif os.path.isfile(buginfo) and os.access(buginfo, os.X_OK):
            bugexec = buginfo
        elif os.path.isdir(buginfo):
            if os.path.isfile(buginfo + '/script') and os.access(buginfo + '/script', os.X_OK):
                bugexec = buginfo + '/script'

            if os.path.isfile(buginfo + '/presubj'):
                presubj = buginfo + '/presubj'

            if os.path.isfile(buginfo + '/control'):
                submitas, submitto, reportwith, supplemental = \
                    utils.parse_bug_control_file(buginfo + '/control')
        elif os.path.isfile('/usr/share/bug/default/' + package) \
                and os.access('/usr/share/bug/default/' + package, os.X_OK):
            bugexec = '/usr/share/bug/default/' + package
        elif os.path.isdir('/usr/share/bug/default/' + package):
            buginfo = '/usr/share/bug/default/' + package
            if os.path.isfile(buginfo + '/script') and os.access(buginfo + '/script',
                                                                 os.X_OK):
                bugexec = buginfo + '/script'

            if os.path.isfile(buginfo + '/presubj'):
                presubj = buginfo + '/presubj'

            if os.path.isfile(buginfo + '/control'):
                submitas, submitto, reportwith, supplemental = \
                    utils.parse_bug_control_file(buginfo + '/control')

        if submitas and (submitas not in reportwith):
            reportwith += [submitas]

        if reportwith:
            # Remove current package from report-with list
            reportwith = [x for x in reportwith if x != package]

        if (pkgavail and self.options.verify and os.path.exists('/usr/bin/debsums')
                and not self.options.kudos and state == 'installed'):
            ewrite('Verifying package integrity...\n')
            fullpackagename = package
            try:
                fullpackagename = utils.get_command_output(
                    "dpkg-query -W -f='${binary:Package}\n' %s 2>/dev/null" % shlex.quote(package)).split()[0]
            except IndexError:
                pass
            rc, output = subprocess.getstatusoutput('/usr/bin/debsums --ignore-permissions -s '
                                                    + shlex.quote(fullpackagename))
            debsumsoutput = output

            if rc and not notatty:
                if not ui.yes_no(f'There may be a problem with your installation of {package};\n'
                                 'the following problems were detected by debsums:\n'
                                 f'{output}\nDo you still want to file a report?',
                                 'Ignore this problem and continue.  This may be '
                                 'appropriate if you have fixed the package manually already.  '
                                 'This problem may also result from the use of localepurge.',
                                 'Exit without filing a report.', False, nowrap=True):
                    efail("Package integrity check failed; stopping.\n")

        if not pkgversion or usedavail or (not pkgavail and not issource):
            if not bugnumber and not (isvirtual or notatty) and not self.options.resume_saved:
                pkgversion = ui.get_string('Please enter the version of the '
                                           'package this report applies to '
                                           '(blank OK)', empty_ok=True, force_prompt=True)
        elif (check_available and not (self.options.kudos or notatty or self.options.offline)
              and state == 'installed' and bts == 'debian'):
            arch = utils.get_arch()
            check_more = (mode > MODE_STANDARD)
            if check_more:
                ewrite('Checking for newer versions at madison'
                       ' and https://ftp-master.debian.org/new.html\n')
            else:
                ewrite('Checking for newer versions at madison...\n')
            (avail, toonew) = checkversions.check_available(
                package, pkgversion, timeout=self.options.timeout,
                check_incoming=check_more, check_newqueue=check_more,
                http_proxy=self.options.http_proxy, arch=arch)
            if toonew:
                if not ui.yes_no('\nYour version of %s (%s) is newer than that in Debian!\n'
                                 'Do you still want to file a report?' % (package, pkgversion),
                                 'Ignore this problem and continue.  This may be '
                                 'appropriate if you know this bug is present in older '
                                 'releases of the package, or you\'re running a mixed '
                                 'stable/testing installation.',
                                 'Exit without filing a report.', False):
                    efail("Newer released version; stopping.\n")

            if avail:
                availtext = ''
                availlist = list(avail.keys())
                availlist.sort()
                for rel in availlist:
                    availtext += '  %s: %s\n' % (rel, avail[rel])

                if not ui.yes_no(('\nYour version (%s) of %s appears to be out of date.\nThe '
                                  'following newer release(s) are available in the Debian '
                                  'archive:\n' % (pkgversion, package)) + availtext
                                 + 'Please try to verify if the bug you are about to report is '
                                 + 'already addressed by these releases.  Do you still want to file a report?',
                                 'Ignore this problem and continue.  This may be '
                                 'appropriate if you know this bug is still present in more '
                                 'recent releases of the package.',
                                 'Exit without filing a report.', False, nowrap=True):
                    efail("Newer released version; stopping.\n")

        bts = DEFAULT_BTS
        if self.options.bts:
            bts = self.options.bts
            ewrite("Will send report to %s (per request).\n",
                   debbugs.SYSTEMS[bts].get('name', bts))
        elif origin:
            if origin.lower() == bts:
                ewrite("Package originates from %s.\n", vendor or origin)
                reportinfo = None
            elif origin.lower() in list(debbugs.SYSTEMS.keys()):
                ewrite("Package originates from %s; overriding your system "
                       "selection.\n", vendor or origin)
                bts = origin.lower()
                sysinfo = debbugs.SYSTEMS[bts]
            elif reportinfo:
                ewrite("Unknown origin %s; will send to %s.\n", origin,
                       reportinfo[1])
                rtype, submitto = reportinfo
            elif submitto:
                ewrite("Unknown origin %s; will send to %s.\n", origin, submitto)
            else:
                ewrite("Unknown origin %s; will send to %s.\n", origin, bts)
        elif reportinfo:
            rtype, submitto = reportinfo
            ewrite("Will use %s protocol talking to %s.\n", rtype, submitto)
            dontquery = True
        else:
            lsbr = subprocess.getoutput('lsb_release -si 2>/dev/null')
            if lsbr:
                distro = lsbr.strip().lower()
                if distro in debbugs.SYSTEMS:
                    bts = distro
                    ewrite("Will send report to %s (per lsb_release).\n",
                           debbugs.SYSTEMS[bts].get('name', bts))

        if rtype == 'mailto':
            rtype = 'debbugs'
            dontquery = True

        special = False
        if not body and not subject and not notatty:
            res = special_prompts(package, bts, ui, fromaddr,
                                  self.options.timeout,
                                  not self.options.offline
                                  and (check_available or not dontquery),
                                  self.options.http_proxy)
            if res:
                (subject, severity, h, ph, body, query) = res
                headers += h
                pseudos += ph
                if not query:
                    dontquery = True
                special = True

        if not (dontquery or notatty or self.options.kudos):
            pkg, src = package, issource
            if self.options.query_src and not issource and not isvirtual:
                pkg = [pkg]
                if src_name:
                    pkg += ['src:' + src_name]
                elif not package.startswith('src:'):
                    pkg += ['src:' + package]
                if submitas and submitas not in pkg:
                    pkg += [submitas]
            try:
                exinfo = ui.handle_bts_query(pkg, bts, self.options.timeout,
                                             self.options.mirrors,
                                             self.options.http_proxy,
                                             source=src,
                                             queryonly=self.options.queryonly,
                                             archived=self.options.archived,
                                             version=pkgversion,
                                             mbox_reader_cmd=self.options.mbox_reader_cmd,
                                             latest_first=self.options.latest_first)
            except UINotImplemented:
                exinfo = None
            except NoNetwork:
                sys.exit(1)
            except NoPackage:
                if not self.options.queryonly and maintainer and ui.yes_no(
                        'There is no record of this package in the bug tracking '
                        'system.\nSend report directly to maintainer?',
                        'Send the report to the maintainer (%s).' % maintainer,
                        'Send the report to the BTS anyway.'):
                    rtype = 'debbugs'
                    sendto = maintainer
            except NoBugs:
                ewrite('No bug reports found.\n')
            except NoReport:
                if self.options.queryonly:
                    ewrite('Exiting at user request.\n')
                else:
                    ewrite('Nothing new to report; exiting.\n')
                return
            except QuertBTSError as q:
                if not ui.yes_no(
                        'Error retrieving information on existing bug reports from the BTS. '
                        'The following error was detected:\n'
                        + str(q)
                        + '\nDo you still want to file a report?',
                        'Keep going', 'Abort', False, nowrap=True):
                    return

            if self.options.queryonly and not exinfo:
                return

        ccaddr = os.environ.get('MAILCC')
        if self.options.nocc:
            bccaddr = os.environ.get('MAILBCC')
        else:
            bccaddr = os.environ.get('MAILBCC', fromaddr)

        if maintainer:
            mstr = "Maintainer for %s is '%s'.\n" % (package, maintainer)
            ewrite(mstr)
            if 'qa.debian.org' in maintainer:
                ui.long_message(
                    "This package seems to be currently \"orphaned\"; it also seems you're a bit "
                    "interested in this package, since you're reporting a bug against it, so you "
                    "might consider being involved in the package maintenance in Debian and/or "
                    "adopting it.  Please be aware that your report may not be resolved for a while, "
                    "because the package seems to lack an active maintainer, but please GO ON and "
                    "REPORT the bug, if there is one.\n"
                    "\n"
                    "For more details, please see: https://www.debian.org/devel/wnpp/ "
                )

        if self.options.kudos and not self.options.debugmode:
            sendto = '%s@packages.debian.org' % package

        depinfo = ""
        # Grab dependency list, removing version conditions.
        if (depends or recommends or suggests) and not self.options.kudos:
            ewrite("Looking up dependencies of %s...\n", package)
            depinfo = (utils.get_dependency_info(package, depends)
                       + utils.get_dependency_info(package, recommends, "recommends")
                       + utils.get_dependency_info(package, suggests, "suggests"))

        if reportwith and not self.options.kudos:
            # retrieve information for the packages listed in 'report-with' bug
            # control file field
            for extrapackage in reportwith:
                ewrite("Getting status for related package %s...\n", extrapackage)
                extrastatus = utils.get_package_status(extrapackage)
                # depends
                if extrastatus[2]:
                    extradepends = [x for x in extrastatus[2] if package not in x]
                    ewrite("Looking up 'depends' of related package %s...\n", extrapackage)
                    depinfo += utils.get_dependency_info(extrapackage, extradepends)
                # recommends
                if extrastatus[3]:
                    extrarecommends = [x for x in extrastatus[3] if package not in x]
                    ewrite("Looking up 'recommends' of related package %s...\n", extrapackage)
                    depinfo += utils.get_dependency_info(extrapackage, extrarecommends, "recommends")
                # suggests
                if extrastatus[15]:
                    extrasuggests = [x for x in extrastatus[15] if package not in x]
                    ewrite("Looking up 'suggests' of related package %s...\n", extrapackage)
                    depinfo += utils.get_dependency_info(extrapackage, extrasuggests, "suggests")

        if supplemental and not self.options.kudos:
            ewrite("Looking up status of additional packages...\n")
            depinfo += utils.get_dependency_info(
                package, [[x] for x in supplemental], rel='is related to')

        confinfo = []
        if conffiles and not self.options.kudos:
            ewrite("Getting changed configuration files...\n")
            confinfo, changed = utils.get_changed_config_files(
                conffiles, self.options.nocompress)

            if self.options.noconf and changed:
                for f in changed:
                    confinfo[f] = 'changed [not included]'
            elif changed and not notatty:
                while 1:
                    x = ui.select_options(
                        "*** WARNING: The following configuration files have been "
                        "modified:\n" + "\n".join(changed)
                        + "\nSend modified configuration files", 'Ynd',
                        {'y': 'Send your modified configuration files.',
                         'n': "Don't send modified configuration files.",
                         'd': 'Display modified configuration files (exit with "q").'})
                    if x == 'n':
                        for f in changed:
                            confinfo[f] = 'changed [not included]'
                        break
                    elif x == 'd':
                        PAGER = os.environ.get('PAGER', '/usr/bin/sensible-pager')
                        ui.system(PAGER + ' ' + ' '.join(changed))
                    else:
                        break

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

        if (self.options.debconf and os.path.exists('/usr/bin/debconf-show')
                and not self.options.kudos and installed):
            showpkgs = package
            if reportwith:
                showpkgs += ' ' + ' '.join(reportwith)
            r = subprocess.run(
                'DEBCONF_SYSTEMRC=1 DEBCONF_NOWARNINGS=yes '
                '/usr/bin/debconf-show %s' % showpkgs,
                shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
            (status, output) = r.returncode, r.stdout.decode(errors='backslashreplace').rstrip()
            if status:
                conftext += '\n-- debconf-show failed\n'
            elif output:
                if (notatty or ui.yes_no("*** The following debconf settings were detected:\n"
                                         + output + "\nInclude these settings in your report?",
                                         'Send your debconf settings.',
                                         "Don't send your debconf settings.", nowrap=True)):
                    conftext += '\n-- debconf information:\n%s\n' % output
                else:
                    conftext += '\n-- debconf information excluded\n'
            else:
                conftext += '\n-- no debconf information\n'

        ewrite('\n')
        prompted = False
        if interactive and not (self.options.kudos or exinfo) and presubj:
            with open(presubj, errors='backslashreplace') as f:
                ui.display_report(f.read() + '\n', presubj=True)

        if self.options.kudos:
            subject = subject or ('Thanks for packaging %s!' % package)
        elif exinfo:
            if special:
                body = ''
            prompted = True
            subject_ok = bool(self.options.subject)
            while not subject_ok:
                subject = ui.get_string(
                    'Please provide a subject for your response.', default="Re: %s" % exinfo.subject, force_prompt=True)
                if subject:
                    subject_ok = True
                else:
                    ewrite("Providing a subject is mandatory.\n")

            # Check to make sure the bug still exists to avoid auto-reopens
            if subject and pkgversion:
                if not ui.yes_no('Are you able to confirm that this bug still exists in version %s '
                                 'of this package?' % pkgversion,
                                 'Yes, it does.',
                                 'No, it doesn\'t (or I don\'t know).',
                                 default=False):
                    pkgversion = None
        elif not subject and not notatty:
            prompted = True
            subject_ok = False
            while not subject_ok:
                subject = ui.get_string(
                    'Briefly describe the problem (max. 100 characters '
                    'allowed). This will be the bug email subject, so keep the '
                    'summary as concise as possible, for example: "fails to '
                    'send email" or "does not start with -q option specified" '
                    '(enter Ctrl+c to exit reportbug without reporting a bug).',
                    force_prompt=True)

                if subject:
                    subject_ok = True
                else:
                    ewrite("Providing a subject is mandatory.\n")

        if len(subject) > 100 and prompted and mode < MODE_EXPERT:
            subject = ui.get_string(
                'Your description is a bit long; please enter a shorter subject. '
                '(An empty response will retain the existing subject.)',
                empty_ok=True, force_prompt=True) or subject
        if package != 'wnpp' and mode < MODE_EXPERT:
            if foundfile:
                subject = foundfile + ": " + subject
                ewrite("Rewriting subject to '%s'\n", subject)
            elif not re.match(r"\S+:\s", subject) and not subject.startswith(package):
                subject = package + ": " + subject
                ewrite("Rewriting subject to '%s'\n", subject)

        listcc = self.options.listcc
        if not listcc:
            listcc = []

        if not listcc and mode > MODE_STANDARD and rtype == 'debbugs' and not self.options.testmode and not self.options.template and self.options.ccmenu:
            listcc += ui.get_multiline(
                'Enter any additional addresses this report should be sent to; press ENTER after each address.')

        if self.options.listccme:
            detected_addr = self.options.email or utils.get_email()[1]
            if not detected_addr:
                efail("list-cc-me option specified but email address not detected")
            listcc += [detected_addr]

        # If the bug is reported against a package with a version that possibly
        # indicates a security update add the security or LTS team to CC
        # after user confirmation
        if pkgversion and package and not self.options.offline and mode > MODE_NOVICE and utils.is_security_update(package, pkgversion):
            if ui.yes_no('Do you want to report a regression because of a security update?',
                         'Yes, please inform the LTS and security teams.',
                         'No or I am not sure.', True):
                distnumber = re.search(r'[+~]deb(\d+)u\d+', pkgversion).group(1)
                support = 'none'
                email_address = 'none'
                try:
                    r = requests.get('https://security-tracker.debian.org/tracker/distributions.json', timeout=self.options.timeout)
                    data = r.json()
                    for key, value in data.items():
                        if distnumber == value['major-version']:
                            support = value['support']
                            email_address = value['contact']
                            break

                    if support != 'none' and utils.check_email_addr(email_address):
                        listcc += [email_address]
                    else:
                        raise

                except requests.exceptions.RequestException:
                    ewrite('Unable to connect to security-tracker.debian.org.\n'
                           'Please try again later or contact the LTS or security team via email directly.\n')
                except Exception:  # catch-all
                    ewrite('No support team contact address could be identified.\n')

        if severity and rtype:
            severity = debbugs.convert_severity(severity, rtype)

        klass = self.options.klass
        if not notatty and not (exinfo or self.options.kudos):
            if not severity:
                if rtype == 'gnats':
                    severities = debbugs.SEVERITIES_gnats
                    default = 'non-critical'
                else:
                    severities = debbugs.SEVERITIES
                    if mode < MODE_STANDARD:
                        ewrite("Removing release critical severities, since running in \'%s\' mode.\n" % utils.MODELIST[mode])
                        for sev in ['critical', 'grave', 'serious', 'does-not-build']:
                            del severities[sev]
                    if isvirtual:
                        for sev in ['critical', 'does-not-build']:
                            if sev in severities:
                                del severities[sev]
                    default = 'normal'
                while not severity or severity not in debbugs.SEVLIST:
                    severity = ui.menu("How would you rate the severity of this "
                                       "problem or report?", severities,
                                       'Please select a severity level: ',
                                       default=default, order=debbugs.SEVLIST[::-1])
                    # urwid has a cancel and a quit button that return < 0
                    if isinstance(severity, int) and severity < 0:
                        sys.exit()

            if rtype == 'gnats':
                # Class of report
                klass = ui.menu("What sort of problem are you reporting?",
                                debbugs.CLASSES, 'Please select a class: ',
                                default='sw-bug', order=debbugs.CLASSLIST)

        severity = severity or 'normal'

        justification = self.options.justification
        if rtype == 'debbugs' and package != 'wnpp' and mode < MODE_EXPERT:
            if severity in ('critical', 'grave'):
                justification = ui.menu(
                    'You are reporting a ' + severity + ' bug; which of the '
                                                        'following criteria does it meet?',
                    debbugs.JUSTIFICATIONS[severity],
                    'Please select the impact of the bug: ', default='unknown')
            elif severity == 'serious':
                justification = ui.get_string(
                    'You are reporting a serious bug; which section of the '
                    'Debian Policy Manual contains the "must" or "required" '
                    'directive that it violates (E.g., "1.2.3")? '
                    'Just type "unknown" if you are not sure (that would '
                    'downgrade severity to normal).', force_prompt=True)
                if re.match(r'[0-9]+\.[0-9.]+', justification):
                    justification = 'Policy ' + justification
                elif not justification:
                    justification = 'unknown'

            if justification == 'unknown':
                justification = ''
                severity = 'normal'
                ewrite('Severity downgraded to "normal".\n')

        if severity == 'does-not-build':
            if pkgversion and not src_name:
                src_name = package
            if src_name and check_available and not notatty:
                ewrite('Checking buildd.debian.org for past builds of %s...\n',
                       src_name)
                built = checkbuildd.check_built(src_name,
                                                http_proxy=self.options.http_proxy,
                                                timeout=self.options.timeout)

                severity = 'serious'
                justification = 'fails to build from source'
                # special-case only if it was built in the past
                if built:
                    justification += ' (but built successfully in the past)'
            else:
                severity = 'serious'
                justification = 'fails to build from source'
                if not notatty:
                    # special-case only if it was built in the past
                    if ui.yes_no(
                            'Has this package successfully been built for this '
                            'architecture in the past (you can look this up at '
                            'buildd.debian.org)?',
                            'Yes, this is a recently-introduced problem.',
                            'No, it has always been this way.'):
                        justification += ' (but built successfully in the past)'

        HOMEDIR = os.environ.get('HOME', '/')

        if (
            rtype == 'debbugs'
            and not self.options.tags
            and not (notatty or self.options.kudos or exinfo)
            and package not in ('wnpp', 'ftp.debian.org', 'release.debian.org')
            and not self.options.resume_saved
            and mode > MODE_NOVICE
            and self.options.tagsmenu
        ):
            tags = debbugs.get_tags(severity, mode)

            taglist = ui.select_multiple(
                'Do any of the following apply to this report?', tags,
                'Please select tags: ')
            if taglist is None:
                # We've pressed cancel or quit in urwid
                sys.exit()

        patch = ('patch' in taglist)

        if justification and 'security' not in taglist and 'security' in \
                justification:
            ewrite('Adding security tag to this report.\n')
            taglist += ['security']

        if justification and 'ftbfs' not in taglist and 'fails to build from source' in justification:
            ewrite('Adding ftbfs tag to this report.\n')
            taglist += ['ftbfs']

        if taglist:
            tags = ' '.join(taglist)
        else:
            tags = ''

        if 'security' in taglist:
            if self.options.secteam or (self.options.secteam is None and ui.yes_no(
                    'Are you reporting an undisclosed vulnerability? If so, in order '
                    'to responsibly disclose the issue, it should not be sent to the public BTS '
                    'right now, but instead to the private Security Team mailing list.',
                    'Yes, it is an undisclosed vulnerability, send this report to the '
                    'private Security Team mailing list and not to the BTS.',
                    'No, it is already a publicly disclosed vulnerability, send this report to the BTS.', False)):
                sendto = 'team@security.debian.org'

        # Execute bug script
        if bugscript and bugexec and not self.options.kudos:
            # add a warning, since it can take a while, 587952
            ewrite("Gathering additional data, this may take a while...\n")
            handler = '/usr/share/reportbug/handle_bugscript'

            # we get the return code of the script, headers and pseudo- set
            # by the script, and last the text output of the script
            (rc, bugscript_hdrs, bugscript_pseudo, text, bugscript_attachments) = \
                utils.exec_and_parse_bugscript(handler, bugexec, ui.system)

            if rc and not notatty:
                if not ui.yes_no('The package bug script %s exited with an error status (return '
                                 'code = %s). Do you still want to file a report?' % (bugexec, rc),
                                 'Ignore this problem and continue.',
                                 'Exit without filing a report.', False, nowrap=True):
                    efail("Package bug script failed; stopping.\n")

            # add bugscript headers only if present
            if bugscript_hdrs:
                headers.extend(bugscript_hdrs.split('\n'))
            if bugscript_pseudo:
                pseudos.append(bugscript_pseudo.strip())
            if bugscript_attachments:
                attachments += bugscript_attachments
            addinfo = None
            if not self.options.noconf:
                addinfo = "\n-- Package-specific info:\n" + text

            if addinfo and incfiles:
                incfiles = addinfo + "\n" + incfiles
            elif addinfo:
                incfiles = addinfo

        if bts == 'debian' and 'security' in taglist and sendto != 'team@security.debian.org':
            ewrite('Will send a CC of this report to the Debian Security Team.\n')
            listcc += ['Debian Security Team <team@security.debian.org>']

        listcc = [cc.strip() for cc in listcc if cc.strip()]
        if listcc:
            pseudos.append('X-Debbugs-Cc: ' + ', '.join(listcc))

        # Prepare bug report
        if self.options.kudos:
            message = '\n\n'
            if not mua:
                SIGFILE = os.path.join(HOMEDIR, '.signature')
                with suppress(OSError):
                    with open(SIGFILE, errors='backslashreplace') as sf:
                        message = "\n\n-- \n" + sf.read()
        else:
            p = submitas or package
            # multiarch: remove arch qualifier only if we're not reporting
            # against the src package
            if not p.startswith('src:'):
                p = p.split(':')[0]
            if self.options.resume_saved:
                message = body
            else:
                message = utils.generate_blank_report(
                    p, pkgversion, severity, justification,
                    depinfo, conftext, foundfile, incfiles, bts, exinfo, rtype,
                    klass, subject, tags, body, mode, pseudos, debsumsoutput,
                    issource=issource, options=self.options)

        # Substitute server email address
        if submitto and '@' not in sendto:
            if '@' in submitto:
                sendto = submitto
            else:
                if exinfo:
                    if sendto != 'submit':
                        sendto = '%d-%s' % (exinfo.bug_num, sendto)
                    else:
                        sendto = str(exinfo.bug_num)

                sendto = sendto + '@' + submitto
        elif '@' not in sendto:
            if exinfo:
                if sendto != 'submit':
                    sendto = '%d-%s' % (exinfo.bug_num, sendto)
                else:
                    sendto = str(exinfo.bug_num)

            try:
                sendto = sysinfo['email'] % sendto
            except TypeError:
                sendto = sysinfo['email']

            sendto = email.utils.formataddr(
                (sysinfo['name'] + ' Bug Tracking System', sendto))

        mailing = not (mua or self.options.printonly or self.options.template)
        message = "Subject: %s\n%s" % (subject, message)
        justsave = False

        if mailing:
            fh, filename = TempFile(prefix=tfprefix, dir=self.options.draftpath)
            fh.write(message)
            fh.close()
            oldmua = mua or self.options.mua
            if not self.options.body and not self.options.bodyfile:
                message, haspatch, justsave = handle_editing(
                    filename, message, self.options, sendto, attachments,
                    package, severity, mode, bool(exinfo), issource,
                    charset=charset, tags=tags,
                    resumed=bool(self.options.resume_saved))
                if haspatch:
                    patch = True

            if not oldmua and self.options.mua:
                mua = self.options.mua
            if mua:
                mailing = False
            elif not sendto:
                print(message, end=' ')
                cleanup_temp_file(filename)
                return

            cleanup_temp_file(filename)

            if not mua and patch and not attachments and not notatty:
                while True:
                    patchfile = ui.get_filename(
                        'What is the filename of the patch (if none, or you have '
                        'already included it, just press ENTER)?',
                        force_prompt=True)
                    if patchfile:
                        attachfile = os.path.expanduser(patchfile)
                        # loop over the glob of 'attachfile', we support glob now
                        for attachf in sorted(glob(attachfile), key=str.casefold):
                            if os.path.exists(attachfile):
                                attachments.append(attachfile)
                            else:
                                ewrite('%s not found!', attachfile)
                    else:
                        break

        # Pass both headers and pseudo-headers (passed on command-line, f.e.)
        body, headers, pseudoheaders = utils.cleanup_msg(message, headers, pseudos, rtype)

        if sign:
            ewrite('Passing message to %s for signature...\n', sign)
            oldbody = body
            body = submit.sign_message(body, fromaddr, package, pgp_addr, sign, self.options.draftpath)
            if not body:
                ewrite('Signature failed; sending message unsigned.\n')
                body = oldbody

        if pseudoheaders:
            body = '\n'.join(pseudoheaders) + '\n\n' + body

        # Strip the body of useless whitespace at the end, then put a final
        # newline in the message.  See #234963.
        body = body.rstrip('\n') + '\n'

        if not pseudoheaders and not justsave:
            ui.display_failure('Invalid bug report message: No pseudoheaders found.\n'
                               'Your message will not be submitted, but stored in a temporary file instead.\n')
            justsave = True

        if justsave:
            if not self.options.outfile:
                fh, outputfile = TempFile(prefix=tfprefix,
                                          dir=self.options.draftpath)
                fh.close()
            mua = mailing = False
            # fake sending the report, it actually saves it in a tempfile
            # but with all the email headers and stuff
            submit.send_report(
                body, attachments, mua, fromaddr, sendto, ccaddr, bccaddr,
                headers, package, charset, mailing, sysinfo, rtype, exinfo,
                self.options.replyto,
                outfile=self.options.outfile or outputfile, mta=None,
                smtphost=None)
        else:
            submit.send_report(
                body, attachments, mua, fromaddr, sendto, ccaddr, bccaddr,
                headers, package, charset, mailing, sysinfo, rtype, exinfo,
                self.options.replyto, self.options.printonly,
                self.options.template, self.options.outfile, self.options.mta,
                self.options.kudos, self.options.smtptls, smtphost,
                self.options.smtpuser, self.options.smtppasswd,
                self.options.paranoid, self.options.draftpath,
                self.options.envelopefrom)

        ui.final_message('Thank you for using reportbug\n')
        return


if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        ewrite("\nreportbug: exiting due to user interrupt.\n")
    except debbugs.Error as x:
        ewrite('error accessing BTS: %s\n' % x)

SAMX