    from Common import *
    import string, time, traceback, types, whrandom, sys, MimeWriter, smtplib, StringIO
    from time import asctime, localtime
    from MiscUtils.Funcs import dateForEmail
    from WebUtils.HTMLForException import HTMLForException
    from WebUtils.Funcs import htmlForDict, htmlEncode
    from HTTPResponse import HTTPResponse
    from types import DictType, StringType

    class singleton: pass

    class ExceptionHandler(Object):
        """
        ExceptionHandler is a utility class for Application that is created
        to handle a particular exception. The object is a one-shot deal.
        After handling an exception, it should be removed.

        At some point, the exception handler sends "writeExceptionReport"
        to the transaction (if present), which in turn sends it to the other
        transactional objects (application, request, response, etc.)
        The handler is the single argument for this message.

        Classes may find it useful to do things like this:

        exceptionReportAttrs = 'foo bar baz'.split()
        def writeExceptionReport(self, handler):
            handler.writeTitle(self.__class__.__name__)
            handler.writeAttrs(self, self.exceptionReportAttrs)

        The handler write methods that may be useful are:
            def write(self, s):
            def writeln(self, s):
            def writeTitle(self, s):
            def writeDict(self, d):
            def writeTable(self, listOfDicts, keys=None):
            def writeAttrs(self, obj, attrNames):

        Derived classes must not assume that the error occured in a
        transaction.  self._tra may be None for exceptions outside
        of transactions.

        See the WebKit.html documentation for other information.

        HOW TO CREATE A CUSTOM EXCEPTION HANDLER

        In the __init__.py of your context:

            from WebKit.ExceptionHandler import ExceptionHandler as _ExceptionHandler

            class ExceptionHandler(_ExceptionHandler):

                hideValuesForFields = _ExceptionHandler.hideValuesForFields + ['foo', 'bar']

                def work(self):
                    _ExceptionHandler.work(self)
                    # do whatever
                    # override other methods if you like

            def contextInitialize(app, ctxPath):
                app._exceptionHandlerClass = ExceptionHandler
        """

        hideValuesForFields = ['creditcard', 'credit card', 'cc', 'password', 'passwd']
            # ^ keep all lower case to support case insensitivity
        if 0: # for testing
            hideValuesForFields.extend('application uri http_accept userid'.split())

        hiddenString = '*** hidden ***'

        ## Init ##

        def __init__(self, application, transaction, excInfo):
            Object.__init__(self)

            # Keep references to the objects
            self._app = application
            self._tra = transaction
            self._exc = excInfo
            if self._tra:
                self._req = self._tra.request()
                self._res = self._tra.response()
            else:
                self._req = self._res = None

            # Make some repairs, if needed. We use the transaction & response to get the error page back out
            # @@ 2000-05-09 ce: Maybe a fresh transaction and response should always be made for that purpose
            ## @@ 2003-01-10 sd: This requires a transaction which we do not have.
            ## Making remaining code safe for no transaction.
            ##
                    ##if self._res is None:
            ##  self._res = HTTPResponse()
            ##  self._tra.setResponse(self._res)

            # Cache MaxValueLengthInExceptionReport for speed
            self._maxValueLength = self.setting('MaxValueLengthInExceptionReport')

            # exception occurance time. (overridden by response.endTime())
            self._time = time.time()

            # Get to work
            self.work()

        ## Utilities ##

        def setting(self, name):
            return self._app.setting(name)

        def servletPathname(self):
            try:
                return self._tra.request().serverSidePath()
            except:

                return None

        def basicServletName(self):
            name = self.servletPathname()
            if name is None:
                return 'unknown'
            else:
                return os.path.basename(name)

        ## Exception handling ##

        def work(self):
            ''' Invoked by __init__ to do the main work. '''

            if self._res:
                self._res.recordEndTime()
                self._time = self._res.endTime()

            self.logExceptionToConsole()

            # write the error page out to the response if available.
            if self._res and (not self._res.isCommitted() or self._res.header('Content-type', None)=='text/html'):
                if not self._res.isCommitted():
                    self._res.reset()
                if self.setting('ShowDebugInfoOnErrors')==1:
                    publicErrorPage = self.privateErrorPage()
                else:
                    publicErrorPage = self.publicErrorPage()
                self._res.write(publicErrorPage)

            privateErrorPage = None
            if self.setting('SaveErrorMessages'):
                privateErrorPage = self.privateErrorPage()
                filename = self.saveErrorPage(privateErrorPage)
            else:
                filename = ''

            self.logExceptionToDisk(errorMsgFilename=filename)

            if self.setting('EmailErrors'):
                if privateErrorPage is None:
                    privateErrorPage = self.privateErrorPage()
                self.emailException(privateErrorPage)

        def logExceptionToConsole(self, stderr=None):
            ''' Logs the time, servlet name and traceback to the console (typically stderr). This usually results in the information appearing in console/terminal from which AppServer was launched. '''
            if stderr is None:
                stderr = sys.stderr
            stderr.write('[%s] [error] WebKit: Error while executing script %s\n' % (
                asctime(localtime(self._time)), self.servletPathname()))
            traceback.print_exc(file=stderr)

        def publicErrorPage(self):
            return '''<html>
        <head>
            <title>Error</title>
        </head>
        <body fgcolor=black bgcolor=white>
            %s
            <p> %s
        </body>
    </html>
    ''' % (htTitle('Error'), self.setting('UserErrorMessage'))

        def privateErrorPage(self):
            ''' Returns an HTML page intended for the developer with useful information such as the traceback. '''
            html = ['''
    <html>
        <head>
            <title>Error</title>
        </head>
        <body fgcolor=black bgcolor=white>
    %s
    <p> %s''' % (htTitle('Error'), self.setting('UserErrorMessage'))]

            html.append(self.htmlDebugInfo())

            html.append('</body></html>')
            return string.join(html, '')

        def htmlDebugInfo(self):
            ''' Return HTML-formatted debugging information about the current exception. '''
            self.html = []
            self.writeHTML()
            html = ''.join(self.html)
            self.html = None
            return html

        def writeHTML(self):
            self.writeTraceback()
            self.writeMiscInfo()
            self.writeTransaction()
            self.writeEnvironment()
            self.writeIds()
            self.writeFancyTraceback()

        ## Write utility methods ##

        def write(self, s):
            self.html.append(str(s))

        def writeln(self, s):
            self.html.append(str(s))
            self.html.append('\n')

        def writeTitle(self, s):
            self.writeln(htTitle(s))

        def writeDict(self, d):
            self.writeln(htmlForDict(d, filterValueCallBack=self.filterDictValue, maxValueLength=self._maxValueLength))

        def writeTable(self, listOfDicts, keys=None):
            """
            Writes a table whose contents are given by listOfDicts. The
            keys of each dictionary are expected to be the same. If the
            keys arg is None, the headings are taken in alphabetical order
            from the first dictionary. If listOfDicts is "false", nothing
            happens.

            The keys and values are already considered to be HTML.

            Caveat: There's no way to influence the formatting or to use
            column titles that are different than the keys.

            Note: Used by writeAttrs().
            """
            if not listOfDicts:
                return

            if keys is None:
                keys = listOfDicts[0].keys()
                keys.sort()

            wr = self.writeln
            wr('<table>\n<tr>')
            for key in keys:
                wr('<td bgcolor=#F0F0F0><b>%s</b></td>' % key)
            wr('</tr>\n')

            for row in listOfDicts:
                wr('<tr>')
                for key in keys:
                    wr('<td bgcolor=#F0F0F0>%s</td>' % self.filterTableValue(row[key], key, row, listOfDicts))
                wr('</tr>\n')

            wr('</table>')

        def writeAttrs(self, obj, attrNames):
            """
            Writes the attributes of the object as given by attrNames.
            Tries obj._name first, followed by obj.name(). Is resilient
            regarding exceptions so as not to spoil the exception report.
            """
            rows = []
            for name in attrNames:
                value = getattr(obj, '_'+name, singleton) # go for data attribute
                try:
                    if value is singleton:
                        value = getattr(obj, name, singleton) # go for method
                        if value is singleton:
                            value = '(could not find attribute or method)'
                        else:
                            try:
                                if callable(value):
                                    value = value()
                            except Exception, e:
                                value = '(exception during method call: %s: %s)' % (e.__class__.__name__, e)
                            value = self.repr(value)
                    else:
                        value = self.repr(value)
                except Exception, e:
                    value = '(exception during value processing: %s: %s)' % (e.__class__.__name__, e)
                rows.append({'attr': name, 'value': value})
            self.writeTable(rows, ('attr', 'value'))

        ## Write specific parts ##

        def writeTraceback(self):
            self.writeTitle('Traceback')
            self.write('<p> <i>%s</i>' % self.servletPathname())
            self.write(HTMLForException(self._exc))

        def writeMiscInfo(self):
            self.writeTitle('MiscInfo')
            info = {
                'time':          asctime(localtime(self._time)),
                'filename':      self.servletPathname(),
                'os.getcwd()':   os.getcwd(),
                'sys.path':      sys.path
            }
            self.writeDict(info)

        def writeTransaction(self):
            if self._tra:
                self._tra.writeExceptionReport(self)
            else:
                self.writeTitle("No current Transaction")

        def writeEnvironment(self):
            self.writeTitle('Environment')
            self.writeDict(os.environ)

        def writeIds(self):
            self.writeTitle('Ids')
            self.writeTable(osIdTable(), ['name', 'value'])

        def writeFancyTraceback(self):
            if self.setting('IncludeFancyTraceback'):
                self.writeTitle('Fancy Traceback')
                try:
                    from WebUtils.ExpansiveHTMLForException import ExpansiveHTMLForException
                    self.write(ExpansiveHTMLForException(context=self.setting('FancyTracebackContext')))
                except:
                    self.write('Unable to generate a fancy traceback! (uncaught exception)')
                    try:
                        self.write(HTMLForException(sys.exc_info()))
                    except:
                        self.write('<br>Unable to even generate a normal traceback of the exception in fancy traceback!')

        def saveErrorPage(self, html):
            ''' Saves the given HTML error page for later viewing by the developer, and returns the filename used. '''
            filename = self._app.serverSidePath(os.path.join(self.setting('ErrorMessagesDir'), self.errorPageFilename()))
            f = open(filename, 'w')
            f.write(html)
            f.close()
            return filename

        def errorPageFilename(self):
            ''' Construct a filename for an HTML error page, not including the 'ErrorMessagesDir' setting. '''
            return 'Error-%s-%s-%d.html' % (
                self.basicServletName(),
                string.join(map(lambda x: '%02d' % x, localtime(self._time)[:6]), '-'),
                whrandom.whrandom().randint(10000, 99999))
                # @@ 2000-04-21 ce: Using the timestamp & a random number is a poor technique for filename uniqueness, but this works for now

        def logExceptionToDisk(self, errorMsgFilename=''):
            ''' Writes a tuple containing (date-time, filename, pathname, exception-name, exception-data,error report filename) to the errors file (typically 'Errors.csv') in CSV format. Invoked by handleException(). '''
            logline = (
                asctime(localtime(self._time)),
                self.basicServletName(),
                self.servletPathname(),
                str(self._exc[0]),
                str(self._exc[1]),
                errorMsgFilename)
            filename = self._app.serverSidePath(self.setting('ErrorLogFilename'))
            if os.path.exists(filename):
                f = open(filename, 'a')
            else:
                f = open(filename, 'w')
                f.write('time,filename,pathname,exception name,exception data,error report filename\n')

            def fixElement(element):
                element = str(element)
                if string.find(element, ',') or string.find(element, '"'):
                    element = string.replace(str(element), '"', '""')
                    element = '"' + element + '"'
                return element
            logline = map(fixElement, logline)

            f.write(string.join(logline, ','))
            f.write('\n')
            f.close()

        def emailException(self, htmlErrMsg):
            message = StringIO.StringIO()
            writer = MimeWriter.MimeWriter(message)

            ## Construct the message headers
            headers = self.setting('ErrorEmailHeaders').copy()
            headers['Date'] = dateForEmail()
            headers['Subject'] = headers.get('Subject','[WebKit Error]') + ' ' \
                         + str(sys.exc_info()[0]) + ': ' \
                         + str(sys.exc_info()[1])
            for h,v in headers.items():
                if isinstance(v, types.ListType):
                    v = ','.join(v)
                writer.addheader(h, v)

            ## Construct the message body

            if self.setting('EmailErrorReportAsAttachment'):
                writer.startmultipartbody('mixed')
                # start off with a text/plain part
                part = writer.nextpart()
                body = part.startbody('text/plain')
                body.write(
                    'WebKit caught an exception while processing ' +
                    'a request for "%s" ' % self.servletPathname() +
                    'at %s (timestamp: %s).  ' %
                    (asctime(localtime(self._time)), self._time) +
                    'The plain text traceback from Python is printed below and ' +
                    'the full HTML error report from WebKit is attached.\n\n'
                    )
                traceback.print_exc(file=body)

                # now add htmlErrMsg
                part = writer.nextpart()
                part.addheader('Content-Transfer-Encoding', '7bit')
                part.addheader('Content-Description', 'HTML version of WebKit error message')
                body = part.startbody('text/html; name=WebKitErrorMsg.html')
                body.write(htmlErrMsg)

                # finish off
                writer.lastpart()
            else:
                body = writer.startbody('text/html')
                body.write(htmlErrMsg)

            # Send the message
            server = smtplib.SMTP(self.setting('ErrorEmailServer'))
            server.set_debuglevel(0)
            server.sendmail(headers['From'], headers['To'], message.getvalue())
            server.quit()

        ## Filtering Values ##

        def filterDictValue(self, value, key, dict):
            return self.filterValue(value, key)

        def filterTableValue(self, value, key, row, table):
            """
            Invoked by writeTable() to afford the opportunity to filter
            the values written in tables. These values are already HTML
            when they arrive here. Use the extra key, row and table
            args as necessary.
            """
            if row.has_key('attr') and key!='attr':
                return self.filterValue(value, row['attr'])
            else:
                return self.filterValue(value, key)

        def filterValue(self, value, key):
            """
            This is the core filter method that is used in all filtering.
            By default, it simply returns self.hiddenString if the key is
            in self.hideValuesForField (case insensitive). Subclasses
            could override for more elaborate filtering techniques.
            """
            if key.lower() in self.hideValuesForFields:
                return self.hiddenString
            else:
                return value

        ## Self utility ##

        def repr(self, x):
            """
            Returns the repr() of x already html encoded. As a special case, dictionaries are nicely formatted in table.

            This is a utility method for writeAttrs.
            """
            if type(x) is DictType:
                return htmlForDict(x, filterValueCallBack=self.filterDictValue, maxValueLength=self._maxValueLength)
            else:
                rep = repr(x)
                if self._maxValueLength and len(rep) > self._maxValueLength:
                    rep = rep[:self._maxValueLength] + '...'
                return htmlEncode(rep)

    # Some misc functions
    def htTitle(name):
        return '''
    <p> <br> <table border=0 cellpadding=4 cellspacing=0 bgcolor=#A00000 width=100%%> <tr> <td align=center>
        <font face="Tahoma, Arial, Helvetica" color=white> <b> %s </b> </font>
    </td> </tr> </table>''' % name

    def osIdTable():
        ''' Returns a list of dictionaries contained id information such as uid, gid, etc.,
            all obtained from the os module. Dictionary keys are 'name' and 'value'. '''
        funcs = ['getegid', 'geteuid', 'getgid', 'getpgrp', 'getpid', 'getppid', 'getuid']
        table = []
        for funcName in funcs:
            if hasattr(os, funcName):
                value = getattr(os, funcName)()
                table.append({'name': funcName, 'value': value})
        return table