#!/usr/bin/python

# Copyright (C) 1999 Milan Zamazal
#
# COPYRIGHT NOTICE
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING.  If not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.


"""CGI interface.

This module is very ugly and is intended to be rewritten sometimes.
"""

__author__  = "Milan Zamazal <pdm@freesoft.cz>"
__version__ = "$Id: cgiscript.py,v 1.20 2000/01/09 19:38:27 pdm Exp $"
__copyright__ = "GNU General Public License, version 2"


import cgi
import os
import string
import StringIO
import sys
import urllib

import DocumentTemplate
import HTMLgen
HTMLgen.Pre.html_escape = 'ON'

from gnats2w import access, config, gnats, mail, problem
from gnats2w.i18n import L


# Constants


VERSION = '0.13'


# Reporting errors


def report_error (message):
    """Create and output HTML page with error 'message'.
    """
    output_document ('error', {'message': HTMLgen.escape (message)})


def report_traceback ():
    """Create and output HTML page with current traceback.
    """
    import traceback
    type, value, tb = sys.exc_info ()
    s = StringIO.StringIO ()
    traceback.print_tb (tb, None, s)
    tb = None
    printed_traceback = s.getvalue ()
    s.close ()
    output_document ('traceback',
                     {'basic_info': '%s: %s' % (str (type), str (value)),
                      'traceback': HTMLgen.escape (printed_traceback)})


def report_access_denied (atype):
    """Create and output HTML page with an access denied message.
    """
    report_error (L('Access denied for the action %s') % atype)
    

# Auxiliary functions


def language ():
    """Return list of allowed languages.
    """
    try:
        language = os.environ['HTTP_ACCEPT_LANGUAGE']
    except KeyError:
        return []
    return string.split (language, ',')


def expert_mode ():
    """Return whether expert mode was requested.
    """
    return os.environ.has_key ('GNATS_EXPERT_MODE') and \
           os.environ['GNATS_EXPERT_MODE'] == 'on'


def output_document (basename, params):
    """Output an HTML document corresponding to 'basename' with 'params'.

    Arguments:

     basename -- Base name of a DTML template.  'config.TEMPLATE_DIR' is
       prepend and '_LANGUAGE.dtml' is appened to this name.  If no language
       was specified or the corresponding language file does not exist, append
       only '.dtml'.
     params -- Dictionary to pass to 'DocumentTemplate' constructor.
    """
    # Get document file name
    base = config.BASE_URL
    if L.language ():
        name = basename + '_' + L.language ()
        base = base[:-5] + '_' + L.language () + '.html'
    else:
        name = basename
    file = os.path.join (config.TEMPLATE_DIR, name + '.dtml')
    if not os.path.isfile (file):
        file = os.path.join (config.TEMPLATE_DIR, basename + '.dtml')
    # Create document
    BASIC_PARAMS['base'] = base
    BASIC_PARAMS.update (params)        # destructive update
    if expert_mode ():
        BASIC_PARAMS['expert'] = 1
    d = DocumentTemplate.HTMLFile (file, BASIC_PARAMS)
    # Output it
    print 'Content-Type: text/html\n'
    print d ()


def form2dict (form):
    """Convert 'cgi.FieldStorage' list to dictionary.

    If multiple values to some key are present, only one of them is retained.
    """
    result = {}
    for i in form.list:
        result[i.name] = i.value
    return result


def html_dump (problem):
    """Dump 'problem' to HTML.

    Return HTML string.
    """
    # Generate dictionary
    items = problem.items ()
    for i, j in (('submitter', 'submitter-id'),
                 ('arrived', 'arrival-date'),
                 ('closed', 'closed-date'),
                 ('modified', 'last-modified'),
                 ('repeat', 'how-to-repeat'),
                 ('trail', 'audit-trail')):
        try:
            items[i] = items[j]
        except KeyError:
            pass
    for k in ('synopsis', 'release', 'originator'):
        if items.has_key (k):
            items[k] = HTMLgen.escape (items[k])
    for k in ('synopsis', 'category', 'release', 'confidential', 'responsible',
              'originator', 'organization', 'state', 'class', 'severity',
              'priority', 'arrived', 'modified', 'closed', 'description',
              'repeat', 'fix', 'environment', 'note', 'trail', 'unformatted'):
        if not items.has_key (k) or not items[k]:
            items[k] = '---'
    for k in ('description', 'repeat', 'fix', 'environment', 'note', 'trail',
              'unformatted'):
        items[k] = HTMLgen.Pre (items[k])
    items['confidentialp'] = config.DISPLAY_CONFIDENTIAL
    items['organizationp'] = config.DISPLAY_ORGANIZATION
    subject = urllib.quote (items['synopsis'])
    _, remail = problem.responsible ()
    if remail:
        items['responsible'] = HTMLgen.Href ('mailto:%s?subject=%s' % \
                                             (remail, subject),
                                             items['responsible'])
    try:
        oemail = items['email']
    except KeyError:
        oemail = ''
    if oemail:
        items['originator'] = HTMLgen.Href ('mailto:%s?subject=%s' % \
                                            (oemail, subject),
                                            items['originator'])
    # Create file name
    lfile = file = os.path.join (config.TEMPLATE_DIR, 'problem')
    if L.language ():
        lfile = file + '_' + L.language () + '.dtml'
    file = file + '.dtml'
    if os.path.isfile (lfile):
        file = lfile
    # Return result
    d = DocumentTemplate.HTMLFile (file, items)
    return d ()
    

def make_edit_dict (problem, id):
    """Return dictionary for 'output_document' of a 'problem' # 'id'.
    """
    # Get items
    items = problem.items ()
    # Originator, email
    try:
        originator = items['originator']
    except IndexError:
        originator = ''
    try:
        email = items['email']
    except IndexError:
        email = ''
    # Release
    try:
        release = items['release']
    except KeyError:
        release = ''
    # Confidential
    if items.has_key ('confidential') and items['confidential'] == 'yes':
        confidential_checked = 'checked'
    else:
        confidential_checked = ''
    # Responsibles and changers
    all_responsible = G.responsible ()
    changer = config.ACCESS.identity ()
    if changer:
        changers = HTMLgen.Select ([changer], name='changer',
                                   selected=[changer])
    else:
        changer_list = filter (lambda c:
                               config.ACCESS.granted (config.ACCESS.EDIT, None,
                                                      c),
                               all_responsible)
        changer_list = ['*', 'submitter'] + changer_list
        changers = HTMLgen.Select (changer_list, name='changer',
                                   selected=['*'])
    if items['responsible']:
        selected = [items['responsible']]
    else:
        selected = []
    responsibles = HTMLgen.Select (all_responsible, name='responsible',
                                   selected=selected)
    responsible, mail = problem.responsible ()
    responsible = HTMLgen.escape (responsible)
    mail = HTMLgen.escape (mail)
    # Other enumerated items
    list = []
    names = ('category', 'state', 'class', 'severity', 'priority')
    current = map (lambda x, i=items: i[x], names)
    cat = items['category']
    cats = map (lambda c: c[0], categories_full ())
    if cat not in cats:
        cats.append (cat)
    cats.sort ()
    all = (cats,
           ['open', 'analyzed', 'feedback', 'closed', 'suspended'],
           ['sw-bug', 'doc-bug', 'change-request', 'support', 'duplicate',
            'mistaken'],
           ['critical', 'serious', 'non-critical'],
           ['high', 'medium', 'low'])
    for i in range (len (all)):
        list.append (HTMLgen.Select (all[i], name=names[i],
                                     selected=[current[i]]))
    dump = html_dump (problem)
    # Return that all
    return {'id': id,
            'email': email,
            'originator': originator,
            'synopsis': HTMLgen.escape (problem.synopsis ()),
            'synops': urllib.quote (problem.synopsis ()),
            'release': release,
            'confidential_checked': confidential_checked,
            'confidentialp': config.DISPLAY_CONFIDENTIAL,
            'changers': str (changers),
            'responsible': responsible,
            'mail': mail,
            'responsibles': str (responsibles),
            'category': str (list[0]),
            'state': str (list[1]),
            'class': str (list[2]),
            'severity': str (list[3]),
            'priority': str (list[4]),
            'config': config,
            'dump': dump,
            'catcgi': '%s' % gen_submit_url ('categories')
            }


def url_path ():
    """Return base URL corresponding to the invoked script.
    """
#    base = os.path.split (os.environ['REQUEST_URI'])[0]
    base = os.path.split (os.environ['SCRIPT_NAME'])[0]
    if base[-1] == '/':
        base = base[:-1]
#      if os.environ.has_key ('HTTPS') and os.environ['HTTPS'] == 'on':
#          protocol = 'https'
#      else:
#          protocol = 'http'
#      host = os.environ['HTTP_HOST']
    return '%s%s' % (config.HOST_URL, base)


def gen_submit_url (basename):
    """Return submit url for script BASENAME.
    
    Include everything, PATH_INFO too.
    """
    url = url_path ()
    if url[-1] != '/':
        url = url + '/'
    url = url + basename + '.' + extension
    if os.environ.has_key ('PATH_INFO'):
        url = url + os.environ['PATH_INFO']
    return url


def mail_it (to, cc, replyto, subject, message, id):
    """Send mail with 'subject' and body 'message' to 'to' and 'cc'.

    'id' is the problem ID.
    'to', 'cc', and 'replyto' are address lists.

    The sender is the GNATS administrator.

    Return true iff the mail was sent successfully.
    """
    url = '%s?id=%s' % (gen_submit_url ('view'), id)
    message = message + '\n' + L('Problem URL is:\n%s\n') % url
    result = mail.mail ('Bug Tracking System on WWW <%s>' % \
                        config.ADMIN_MAIL,
                        to, cc, replyto, subject, message)
    return result == 0


def remove_crs (str):
    """Remove all right side and bottom line garbage in multiline string 'str'.

    Return the cleared string.
    """
    str = string.rstrip (str)
    lines = string.split (str, '\n')
    lines = map (lambda l: string.rstrip (l), lines)
    return string.join (lines, '\n')


def categories_full ():
    """Return list of category tuples of the current 'gnats.Gnats' instance.

    Consider category limiting.
    Do not sort the categories.
    """
    categories = G.categories_full ()
    if os.environ.has_key ('GNATS_CATEGORIZE') and \
       os.environ['GNATS_CATEGORIZE']:
        r = os.environ['GNATS_CATEGORIZE']
        l = len (r) 
        categories = filter (lambda c, r=r, l=l: c[0][:l] == r, categories)
    return categories


def category_prefixes ():
    """Return sorted list of category prefixes of the current 'Gnats' instance.
    """
    categories = map (lambda c: c[0], categories_full ())
    for i in range (len (categories)):
        c[i] = string.split (c[i], '-')[0]
    categories.sort ()
    result = []
    last = ''
    for c in categories:
        if c == last:
            continue
        result.append (c)
        last = c
    return categories


# Main functions


def index (form):
    """Display the introductory page.
    """
    if config.LOCAL_INDEX_FUNCTION:
        local = config.LOCAL_INDEX_FUNCTION ()
    else:
        local = ''
    output_document ('index', {'idrequestp': config.DISPLAY_IDREQUEST,
                               'local': local})

def submit (form):
    """Submitting new problem report.
    """
    # Access permitted?
    access = config.ACCESS.granted (config.ACCESS.SUBMIT)
    if not access:
        report_access_denied (config.ACCESS.SUBMIT)
        return
    # New report
    if not form.keys ():
        categories = categories_full ()
        if config.AUTO_CONFIDENTIAL and access != config.ACCESS.FULL:
            categories = filter (lambda c: not c[1], categories)
        categories = map (lambda c: c[0], categories)
        try:
            categories.remove ('pending')
        except:
            pass
        if not categories:
            report_error (L('No accessible categories'))
            return
        categories.sort ()
        html_categories = HTMLgen.Select (categories, name='category',
                                          selected=[config.DEFAULT_CATEGORY])
        output_document ('submit',
                         {'categories': str (html_categories),
                          'confidentialp': config.DISPLAY_CONFIDENTIAL,
                          'submitter': config.ACCESS.name (),
                          'submittere': config.ACCESS.email (),
                          'catcgi': '%s' % gen_submit_url ('categories')})
        return
    # Filled report
    form = form2dict (form)
    if not form.has_key ('email'):
        report_error (L('No e-mail given, where to send the answers to?'))
        return
    form['confidential'] = 'yes'
    categories = categories_full ()
    if config.AUTO_CONFIDENTIAL and form.has_key ('category'):
        cat = form['category']
        for c in categories:
            if c[0] == cat:
                if not c[1]:
                    form['confidential'] = 'no'
                break
    else:
        if not form.has_key ('confidential'):
            form['confidential'] = 'no'    
    for k in ('description', 'how-to-repeat', 'fix', 'environment'):
        if form.has_key (k):
            form[k] = remove_crs (form[k])
    p = problem.Problem (G, form)
    if p.ok () and G.submit (p):
        output_document ('submit-ok', {'problem': p, 'dump': html_dump (p)})
    else:
        errors = HTMLgen.Pre (p.errors ())
        output_document ('submit-error',
                         {'problem': p,
                          'errors': str (errors)})
    

def edit (form):
    """Editing problem report.
    """
    # Retrieve the problem
    form = form2dict (form)
    try:
        id = form['id']
    except KeyError:
        report_error (L('No problem id specified.'))
        return
    p = G.problem (id)
    # Access permitted?
    access_type = config.ACCESS.granted (config.ACCESS.EDIT, p)
    if not access_type:
        report_access_denied (config.ACCESS.EDIT)
        return
    # Problem OK?
    if not p:
        report_error (L('Problem %d could not be retrieved.') % id)
        return
    # Edit form only
    if len (form.keys ()) <= 2:
        dict = make_edit_dict (p, id)
        output_document ('edit', make_edit_dict (p, id))
        return
    # Real edit -- retrieve values
    errors = rresponsible = rstate = changer = ''
    items = p.items ()
    try:
        oldcat = items['category']
    except KeyError:
        oldcat = ''
    new_items = {'release': ''}         # release can be erased in the form
    if not config.AUTO_CONFIDENTIAL and config.DISPLAY_CONFIDENTIAL:
        if form.has_key ('confidential'):
            new_items['confidential'] = 'yes'
        else:
            new_items['confidential'] = 'no'
    for k, v in form.items ():
        if k in ('id', 'submit', 'confidential'):
            pass
        elif k == 'changer':
            changer = v
        elif k == 'rresponsible':
            rresponsible = remove_crs (v)
        elif k == 'rstate':
            rstate = remove_crs (v)
        elif k in ('class', 'severity', 'priority', 'category', 'release',
                   'responsible', 'state', 'synopsis'):
            v = string.strip (v)
            new_items[k] = v
            if k == 'category' and \
               (not form.has_key ('responsible')
                or form['responsible'] == items['responsible']):
                cats = G.categories_full ()
                for c in cats:
                    if c[0] == v:
                        r = c[3]
                        if r != items['responsible']:
                            new_items['responsible'] = r
                        break
        elif k in ('description', 'how-to-repeat', 'fix',
                   'environment'):
            new_items[k] = remove_crs (v)
        else:
            errors = errors + L('Invalid item: %s\n') % k
    if not changer:
        report_error (L('No changer specified.'))
        return
    if access_type != config.ACCESS.FULL:
        new_items['confidential'] = 'no'
    # Update the problem
    p.change (new_items, changer, rresponsible, rstate)
    new_items = p.changes ()
    if not new_items.keys ():
        report_error (L('No changes requested'))
        return
    # Check new values
    if not p.ok ():
        errors = errors + p.errors ()
    if errors:
        errors = str (HTMLgen.Pre (errors))
        output_document ('edit-error', {'problem': p, 'errors': errors})
        return
    # Update the problem
    errors = p.update ()
    if errors:
        errors = HTMLgen.Pre (errors)
        output_document ('edit-error', {'problem': p, 'errors': str (errors)})
        return
    # Send notifications
    name, email = p.responsible ()
    originator, originator_email = items['originator'], items['email']
    if changer == 'submitter':
        chname, chemail = originator, originator_email
    else:
        chname, chemail = G.id2email (changer)
    try:
        del new_items['audit-trail']
    except KeyError:
        pass
    changes = map (lambda k: k+'\n', new_items.keys ())
    changes = string.join (changes, '')
    big_changes = remail = ''
    if new_items.has_key ('responsible') and \
       new_items['responsible'] != items['responsible']:
        big_changes = big_changes + \
                      L('Responsible changed to %s.\nReason:\n%s\n\n') % \
                      (new_items['responsible'], rresponsible)
        try:
            _, remail = G.id2email (items['responsible'])
        except KeyError:
            remail = ''
    if new_items.has_key ('state') and rstate:
        big_changes = big_changes + \
                      L('State changed to %s.\nReason:\n%s\n\n') % \
                      (new_items['state'], rstate)
    changelog = L('The problem was edited by %s.\n\n') % changer
    message = changelog + \
              L('%sThere were changed the following items:\n%s') % \
              (big_changes, changes)
    try:
        category = p.items ()['category']
        categories = categories_full ()
        for c in categories:
            if c[0] == category:
                notify = c[4]
                break
        else:
            notify = []
    except KeyError:
        category = '?'
        categories = None
        notify = []
    subject = 'Re: %s/%s: %s' % (category, id, p.synopsis ())
    for i in email, chemail, remail, originator_email:
        try:
            notify.remove (i)
        except:
            pass
    if chemail:
        replyto = [chemail]
    else:
        replyto = []
    if email:
        mail_it ([email], notify, replyto, subject, message, id)
    if chemail and chemail != email:
        mail_it ([chemail], [], [], subject, message, id)
    if remail:
        notify = []
        if oldcat and categories:
            for c in categories:
                if c[0] == oldcat:
                    notify = c[4]
                    break
        mail_it ([remail], notify, replyto, subject,
                 L('This problem was given to %s.\n') % \
                 new_items['responsible'],
                 id)
    if originator_email and originator_email not in (email, chemail) \
       and big_changes:
        mail_it ([originator_email], [], replyto, subject,
                 changelog + big_changes, id)
    # Report success
    output_document ('edit-ok', {'problem': p, 'dump': html_dump (p)})


def funcid (x, y):
    if x > y:
        return 1
    elif x < y:
        return -1
    else:
        return 0

def funcspec (rank, x, y):
     try:
         xx = rank.index (x)
     except ValueError:
         xx = 100
     try:
         yy = rank.index (y)
     except ValueError:
         yy = 100
     if xx < yy:
         return -1
     elif xx > yy:
         return 1
     else:
         return 0

def query (form):
    """Query problem database.
    """
    # Access permitted?
    access_type = config.ACCESS.granted (config.ACCESS.VIEW)
    if not access_type:
        report_access_denied (config.ACCESS.VIEW)
        return
    # Clear query page
    if not form.keys ():
        categories = categories_full ()
        if access_type != config.ACCESS.FULL:
            categories = filter (lambda c: not c[1], categories)
        if not categories:
            report_error (L('No accessible categories'))
            return
        categories = map (lambda c: c[0], categories)
        categories.sort ()
        categories = HTMLgen.Select (categories, name='category', size=6,
                                     multiple=1)
        responsible = HTMLgen.Select (G.responsible (), name='responsible',
                                      size=6, multiple=1)
        output_document ('query',
                         {'categories': str (categories),
                          'responsible': str (responsible),
                          'confidentialp': config.DISPLAY_CONFIDENTIAL,
                          'catcgi': '%s' % gen_submit_url ('categories')})
        return
    # Real query
    items = map (lambda m: (m.name, m.value), form.list)
    if access_type != config.ACCESS.FULL:
        items = filter (lambda i: i[0] != 'confidential', items)
        items.append (('confidential', 'no'))
    if os.environ.has_key ('GNATS_CATEGORIZE'):
        cat = os.environ['GNATS_CATEGORIZE']
        items = filter (lambda i, c=cat, l=len(cat): \
                        i[0] != 'category' or i[1][:l] == c,
                        items)
        if not filter (lambda i: i[0] == 'category', items):
            items.append (('category', cat))
    else:
        cat_limit = ''
        cat_limited = 1
    query, result = G.query (items)
    if type (result) == type (''):
        report_error (L('Query error:\n%s') % result)
    elif result == []:
        report_error (L('Nothing found.'))
    else:
        base = url_path ()
        output = HTMLgen.TableLite ()
        if expert_mode ():
            edit = "[E]"
        else:
            edit = L("[E]dit")
        sortby = None
        for k, v in items:
            if k == 'sort':
                sortby = v
        if sortby == 'responsible':
            def func (x, y):
                return funcid (x[1], y[1]) or \
                       funcid (x[2], y[2]) or \
                       funcid (string.atoi (x[0]), string.atoi (y[0]))
        elif sortby == 'category':
            def func (x, y):
                return funcid (x[2], y[2]) or \
                       funcid (string.atoi (x[0]), string.atoi (y[0]))
        elif sortby == 'state':
            def func (x, y):
                return funcspec (['open', 'analyzed', 'feedback', 'closed',
                                  'suspended'],
                                 x[3], y[3]) or \
                       funcspec (['critical', 'serious', 'non-critical'],
                                 x[4], y[4]) or \
                       funcspec (['high', 'medium', 'low'], x[5], y[5]) or \
                       funcid (string.atoi (x[0]), string.atoi (y[0]))
        elif sortby == 'severity':
            def func (x, y):
                return funcspec (['critical', 'serious', 'non-critical'],
                                 x[4], y[4]) or \
                       funcspec (['high', 'medium', 'low'], x[5], y[5]) or \
                       funcid (string.atoi (x[0]), string.atoi (y[0]))
        elif sortby == 'priority':
            def func (x, y):
                return funcspec (['high', 'medium', 'low'], x[5], y[5]) or \
                       funcspec (['critical', 'serious', 'non-critical'],
                                 x[4], y[4]) or \
                       funcid (string.atoi (x[0]), string.atoi (y[0]))
        else:
            sortby = None
        if sortby:
            result.sort (func)
        for l in result:
            if not config.DISPLAY_ORGANIZATION:
                del l[6]
            row = HTMLgen.TR ()
            if os.environ.has_key ('PATH_INFO'):
                pinfo = os.environ['PATH_INFO']
            else:
                pinfo = ''
            id = l[0]
            link = HTMLgen.Href ("%s?id=%s" % (gen_submit_url ('view'), id),
                                 id)
            row.append (HTMLgen.TD (link, align="right"))
            link = HTMLgen.Href ("%s?id=%s" % (gen_submit_url ('edit'), id),
                                 edit)
            row.append (HTMLgen.TD (link))
            for i in l[1:]:
                row.append (HTMLgen.TD (i))
            output.append (row)
        output_document ('query-result', {'query': query,
                                          'number': len (result),
                                          'problem_list': str (output)})


def view (form):
    """View problem report.
    """
    # Get the problem
    try:
        id = form['id'].value
    except:
        report_error (L('No problem id specified.'))
        return
    p = G.problem (id)
    # Access permitted?
    access_type = config.ACCESS.granted (config.ACCESS.VIEW, p)
    if not access_type:
        report_access_denied (config.ACCESS.VIEW)
        return
    # Problem OK?
    if not p:
        report_error (L('Problem %s could not be retrieved.') % id)
        return
    if access_type != config.ACCESS.FULL and p.confidential ():
        report_error (L('Sorry, this problem is confidential.'))
        return
    # Output the problem
    responsible, email = p.responsible ()
    subject = L("Problem%%20#%s") % id
    output_document ('view', {'number': id,
                              'problem': p,
                              'responsible': responsible,
                              'email': email,
                              'subject': subject,
                              'dump': html_dump (p)})


def request (form):
    """Handle request for submitter ID.
    """
    # Access permitted?
    if not config.ACCESS.granted (config.ACCESS.SUBMIT):
        report_access_denied (config.ACCESS.SUBMIT)
        return
    # Form only
    if not form.keys ():
        output_document ('request', {})
        return
    # Get request data
    values = {}
    for k in form.keys ():
        if k in ('originator', 'email', 'id'):
            values[k] = form[k].value
        else:
            report_error (L('Invalid item: %s\n') % k)
            return
    if not values.has_key ('originator') or not values.has_key ('email'):
        report_error (L('You must fill in both your name and e-mail to' + \
                        'register.'))
        return
    # Submit the request
    try:
        id = values['id']
        del values['id']
    except KeyError:
        id = '*'
    values['confidential'] = 'yes'
    values['synopsis'] = L('Request for submitter id: %s') % id
    p = problem.Problem (G, values)
    if p.ok () and G.submit (p):
        page = 'request-ok'
    else:
        page = 'request-error'
    output_document (page, {'problem': p})    


def categories (form):
    """Create list of categories with description.
    """
    # Access permitted?
    access = config.ACCESS.granted (config.ACCESS.SUBMIT)
    if not access:
        report_access_denied (config.ACCESS.SUBMIT)
        return
    # Create category list
    table = HTMLgen.TableLite ()
    row = HTMLgen.TR ()
    table.append (row)
    if config.AUTO_CONFIDENTIAL:
        row.append (HTMLgen.TD (HTMLgen.Strong (L('Confidential'))))
    row.prepend (HTMLgen.TD (HTMLgen.Strong (L('Category'))))
    for i in (L('Description'), L('Responsible')):
        row.append (HTMLgen.TD (HTMLgen.Strong (i)))
    for c in categories_full ():
        if c[1] and access != config.ACCESS.FULL:
            continue
        row = HTMLgen.TR ()
        table.append (row)
        row.append (HTMLgen.TD (c[0]))
        if config.AUTO_CONFIDENTIAL:
            if c[1]:
                flag = L('yes')
            else:
                flag = L('no')
            row.append (HTMLgen.TD (flag))
        row.append (HTMLgen.TD (L(c[2])))
        name, email = G.id2email (c[3])
        if not name:
            name = '?'
        if email:
            who = HTMLgen.Href ('mailto:%s' % email, name)
        else:
            who = name
        row.append (HTMLgen.TD (who))
    # Output it
    output_document ('categories', {'categories': str (table)})
    

# Top level function


def go (form=None):
    """Perform CGI request.
    """
    try:
        # Consider PATH_INFO
        if os.environ.has_key ('PATH_INFO'):
            for i in string.split (os.environ['PATH_INFO'], '/'):
                try:
                    if i[:6] == 'GNATS_':
                        var, val = string.split (i, '=')
                        os.environ[var] = val
                except:
                    pass
        # Set some global variables
        global BASIC_PARAMS
        BASIC_PARAMS = {'adminmail': config.ADMIN_MAIL}
        global G
        G = gnats.Gnats ()
        global BASIC_PARAMS
        BASIC_PARAMS.update ({'gen_submit_url': gen_submit_url})
        versions = '%s, gnats2w %s' % (G.version (), VERSION)
        BASIC_PARAMS.update ({'versions': versions})
        # Set language
        L.set_language (language ())
        # Get data
        if not form:
            form = cgi.FieldStorage ()
        # Run appropriate function
        _, script_name = os.path.split (os.environ['SCRIPT_FILENAME'])
        script_name = string.split (script_name, '.')
        global extension
        extension = script_name[1]
        script_name = script_name[0]
        if script_name == 'index':
            index (form)
        elif script_name == 'submit':
            submit (form)
        elif script_name == 'edit':
            edit (form)
        elif script_name == 'query':
            query (form)
        elif script_name == 'view':
            view (form)
        elif script_name == 'request':
            request (form)
        elif script_name == 'categories':
            categories (form)
        else:
            report_error (L('Unknown request: %s') % script_name)
    except:
        report_traceback ()


if __name__ == '__main__':
    go ()
