source: emailtotracscript/trunk/email2trac.py.in @ 91

Last change on this file since 91 was 91, checked in by bas, 16 years ago

EmailtoTracScript?:

email2trac.py.in:

  • Fixed and unicode bug in email_header_txt() Only triggered if email_header is set to 1
  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 20.4 KB
Line 
1#!@PYTHON@
2# Copyright (C) 2002
3#
4# This file is part of the email2trac utils
5#
6# This program is free software; you can redistribute it and/or modify it
7# under the terms of the GNU General Public License as published by the
8# Free Software Foundation; either version 2, or (at your option) any
9# later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
19#
20# For vi/emacs or other use tabstop=4 (vi: set ts=4)
21#
22"""
23email2trac.py -- Email tickets to Trac.
24
25A simple MTA filter to create Trac tickets from inbound emails.
26
27Copyright 2005, Daniel Lundin <daniel@edgewall.com>
28Copyright 2005, Edgewall Software
29
30Changed By: Bas van der Vlies <basv@sara.nl>
31Date      : 13 September 2005
32Descr.    : Added config file and command line options, spam level
33            detection, reply address and mailto option. Unicode support
34
35Changed By: Walter de Jong <walter@sara.nl>
36Descr.    : multipart-message code and trac attachments
37
38
39The scripts reads emails from stdin and inserts directly into a Trac database.
40MIME headers are mapped as follows:
41
42        * From:      => Reporter
43                     => CC (Optional via reply_address option)
44        * Subject:   => Summary
45        * Body       => Description
46        * Component  => Can be set to SPAM via spam_level option
47
48How to use
49----------
50 * Create an config file:
51        [DEFAULT]                      # REQUIRED
52        project      : /data/trac/test # REQUIRED
53        debug        : 1               # OPTIONAL, if set print some DEBUG info
54        spam_level   : 4               # OPTIONAL, if set check for SPAM mail
55        reply_address: 1               # OPTIONAL, if set then fill in ticket CC field
56        umask        : 022             # OPTIONAL, if set then use this umask for creation of the attachments
57        mailto_link  : 1               # OPTIONAL, if set then [mailto:<>] in description
58        mailto_cc    : basv@sara.nl    # OPTIONAL, use this address as CC in mailto line
59        ticket_update: 1               # OPTIONAL, if set then check if this is an update for a ticket
60        trac_version : 0.8             # OPTIONAL, default is 0.9
61
62        [jouvin]                         # OPTIONAL project declaration, if set both fields necessary
63        project      : /data/trac/jouvin # use -p|--project jouvin. 
64       
65 * default config file is : /etc/email2trac.conf
66
67 * Commandline opions:
68                -h | --help
69                -c <value> | --component=<value>
70                -f <config file> | --file=<config file>
71                -p <project name> | --project=<project name>
72
73SVN Info:
74        $Id: email2trac.py.in 91 2006-06-30 12:09:51Z bas $
75"""
76import os
77import sys
78import string
79import getopt
80import stat
81import time
82import email
83import re
84import urllib
85import unicodedata
86import ConfigParser
87from email import Header
88from stat import *
89import mimetypes
90
91# Fix some errors
92#import codecs
93
94trac_default_version = 0.9
95
96class TicketEmailParser(object):
97        env = None
98        comment = '> '
99   
100        def __init__(self, env, parameters, version):
101                self.env = env
102
103                # Database connection
104                #
105                self.db = None
106
107                # Some useful mail constants
108                #
109                self.author = None
110                self.email_addr = None
111                self.email_field = None
112
113                self.VERSION = version
114                if self.VERSION == 0.8:
115                        self.get_config = self.env.get_config
116                else:
117                        self.get_config = self.env.config.get
118
119                if parameters.has_key('umask'):
120                        os.umask(int(parameters['umask'], 8))
121
122                if parameters.has_key('debug'):
123                        self.DEBUG = int(parameters['debug'])
124                else:
125                        self.DEBUG = 0
126
127                if parameters.has_key('mailto_link'):
128                        self.MAILTO = int(parameters['mailto_link'])
129                        if parameters.has_key('mailto_cc'):
130                                self.MAILTO_CC = parameters['mailto_cc']
131                        else:
132                                self.MAILTO_CC = ''
133                else:
134                        self.MAILTO = 0
135
136                if parameters.has_key('spam_level'):
137                        self.SPAM_LEVEL = int(parameters['spam_level'])
138                else:
139                        self.SPAM_LEVEL = 0
140
141                if parameters.has_key('email_comment'):
142                        self.comment = str(parameters['email_comment'])
143
144                if parameters.has_key('email_header'):
145                        self.EMAIL_HEADER = int(parameters['email_header'])
146                else:
147                        self.EMAIL_HEADER = 0
148
149                if parameters.has_key('alternate_notify_template'):
150                        self.notify_template = str(parameters['alternate_notify_template'])
151                else:
152                        self.notify_template = None
153
154                if parameters.has_key('reply_all'):
155                        self.REPLY_ALL = int(parameters['reply_all'])
156                else:
157                        self.REPLY_ALL = 0
158
159                if parameters.has_key('ticket_update'):
160                        self.TICKET_UPDATE = int(parameters['ticket_update'])
161                else:
162                        self.TICKET_UPDATE = 0
163
164
165        # X-Spam-Score: *** (3.255) BAYES_50,DNS_FROM_AHBL_RHSBL,HTML_
166        # Note if Spam_level then '*' are included
167        def spam(self, message):
168                if message.has_key('X-Spam-Score'):
169                        spam_l = string.split(message['X-Spam-Score'])
170                        number = spam_l[0].count('*')
171
172                        if number >= self.SPAM_LEVEL:
173                                return 'Spam'
174
175                elif message.has_key('X-Virus-found'):                  # treat virus mails as spam
176                        return 'Spam'
177
178                return self.get_config('ticket', 'default_component')
179
180        def to_unicode(self, str):
181                """
182                Email has 7 bit ASCII code, convert it to unicode with the charset
183        that is encoded in 7-bit ASCII code and encode it as utf-8 so Trac
184                understands it.
185                """
186                results =  Header.decode_header(str)
187                str = None
188                for text,format in results:
189                        if format:
190                                try:
191                                        temp = unicode(text, format)
192                                except (UnicodeError,LookupError):
193                                        # This always works
194                                        #
195                                        temp = unicode(text, 'iso-8859-15')
196                                       
197                                temp =  temp.encode('utf-8')
198                        else:
199                                temp = string.strip(text)
200
201                        if str:
202                                str = '%s %s' %(str, temp)
203                        else:
204                                str = temp
205
206                return str
207
208        def debug_attachments(self, message):
209                n = 0
210                for part in message.walk():
211                        if part.get_content_maintype() == 'multipart':      # multipart/* is just a container
212                                print 'TD: multipart container'
213                                continue
214
215                        n = n + 1
216                        print 'TD: part%d: Content-Type: %s' % (n, part.get_content_type())
217                        print 'TD: part%d: filename: %s' % (n, part.get_filename())
218
219                        if part.is_multipart():
220                                print 'TD: this part is multipart'
221                                payload = part.get_payload(decode=1)
222                                print 'TD: payload:', payload
223                        else:
224                                print 'TD: this part is not multipart'
225
226                        part_file = '/var/tmp/part%d' % n
227                        print 'TD: writing part%d (%s)' % (n,part_file)
228                        fx = open(part_file, 'wb')
229                        text = part.get_payload(decode=1)
230                        if not text:
231                                text = '(None)'
232                        fx.write(text)
233                        fx.close()
234                        try:
235                                os.chmod(part_file,S_IRWXU|S_IRWXG|S_IRWXO)
236                        except OSError:
237                                pass
238
239        def email_header_txt(self, m):
240                """
241                Display To and CC addresses in description field
242                """
243                str = ''
244                if m['To'] and len(m['To']) > 0 and m['To'] != 'hic@sara.nl':
245                        str = "'''To:''' %s [[BR]]" %(m['To'])
246                if m['Cc'] and len(m['Cc']) > 0:
247                        str = "%s'''Cc:''' %s [[BR]]" % (str, m['Cc'])
248
249                return  self.to_unicode(str)
250
251        def set_owner(self, ticket):
252                """
253                Select default owner for ticket component
254                """
255                cursor = self.db.cursor()
256                sql = "SELECT owner FROM component WHERE name='%s'" % ticket['component']
257                cursor.execute(sql)
258                try:
259                        ticket['owner'] = cursor.fetchone()[0]
260                except TypeError, detail:
261                        ticket['owner'] = "UNKNOWN"
262
263        def get_author_emailaddrs(self, message):
264                """
265                Get the default author name and email address from the message
266                """
267                self.author, self.email_addr  = email.Utils.parseaddr(message['from'])
268
269                # Look for email address in registered trac users
270                #
271                if self.VERSION == 0.8:
272                        users = []
273                else:
274                        users = [ u for (u, n, e) in self.env.get_known_users(self.db)
275                                if e == self.email_addr ]
276
277                if len(users) == 1:
278                        self.email_field = users[0]
279                else:
280                        self.email_field =  self.to_unicode(message['from'])
281
282        def set_reply_fields(self, ticket, message):
283                """
284                Set all the right fields for a new ticket
285                """
286                ticket['reporter'] = self.email_field
287
288                # Put all CC-addresses in ticket CC field
289                #
290                if self.REPLY_ALL:
291                        #tos = message.get_all('to', [])
292                        ccs = message.get_all('cc', [])
293
294                        addrs = email.Utils.getaddresses(ccs)
295
296                        # Remove reporter email address if notification is
297                        # on
298                        #
299                        if self.notification:
300                                try:
301                                        addrs.remove((self.author, self.email_addr))
302                                except ValueError, detail:
303                                        pass
304
305                        for name,mail in addrs:
306                                        try:
307                                                ticket['cc'] = '%s,%s' %(ticket['cc'], mail)
308                                        except:
309                                                ticket['cc'] = mail
310
311        def save_email_for_debug(self, message):
312                msg_file = '/var/tmp/msg.txt'
313                print 'TD: saving email to %s' % msg_file
314                fx = open(msg_file, 'wb')
315                fx.write('%s' % message)
316                fx.close()
317                try:
318                        os.chmod(msg_file,S_IRWXU|S_IRWXG|S_IRWXO)
319                except OSError:
320                        pass
321
322        def ticket_update(self, m):
323                """
324                If the current email is a reply to an existing ticket, this function
325                will append the contents of this email to that ticket, instead of
326                creating a new one.
327                """
328                if not m['Subject']:
329                        return False
330                else:
331                        subject  = self.to_unicode(m['Subject'])
332
333                TICKET_RE = re.compile(r"""
334                                        (?P<ticketnr>[#][0-9]+:)
335                                        """, re.VERBOSE)
336
337                result =  TICKET_RE.search(subject)
338                if not result:
339                        return False
340
341                body_text = self.get_body_text(m)
342                body_text = '{{{\n%s\n}}}' %body_text
343
344                # Strip '#' and ':' from ticket_id
345                #
346                ticket_id = result.group('ticketnr')
347                ticket_id = int(ticket_id[1:-1])
348
349                # Get current time
350                #
351                when = int(time.time())
352
353                if self.VERSION  == 0.8:
354                        tkt = Ticket(self.db, ticket_id)
355                        tkt.save_changes(self.db, self.author, body_text, when)
356                else:
357                        tkt = Ticket(self.env, ticket_id, self.db)
358                        tkt.save_changes(self.author, body_text, when)
359                        tkt['id'] = ticket_id
360
361                self.attachments(m, tkt)
362
363                if self.notification:
364                        self.notify(tkt, False, when)
365
366                return True
367
368        def new_ticket(self, msg):
369                """
370                Create a new ticket
371                """
372                tkt = Ticket(self.env)
373                tkt['status'] = 'new'
374
375                # Some defaults
376                #
377                tkt['milestone'] = self.get_config('ticket', 'default_milestone')
378                tkt['priority'] = self.get_config('ticket', 'default_priority')
379                tkt['severity'] = self.get_config('ticket', 'default_severity')
380                tkt['version'] = self.get_config('ticket', 'default_version')
381
382                if not msg['Subject']:
383                        tkt['summary'] = '(geen subject)'
384                else:
385                        tkt['summary'] = self.to_unicode(msg['Subject'])
386
387
388                if settings.has_key('component'):
389                        tkt['component'] = settings['component']
390                else:
391                        tkt['component'] = self.spam(msg)
392
393                # Must make this an option or so, discard SPAM messages or save then
394                # and delete later
395                #
396                #if self.SPAM_LEVEL and self.spam(msg):
397                #       print 'This message is a SPAM. Automatic ticket insertion refused (SPAM level > %d' % self.SPAM_LEVEL
398                #       sys.exit(1)
399
400                # Set default owner for component
401                #
402                self.set_owner(tkt)
403                self.set_reply_fields(tkt, msg)
404
405                # produce e-mail like header
406                #
407                head = ''
408                if self.EMAIL_HEADER > 0:
409                        head = self.email_header_txt(msg)
410
411
412                body_text = self.get_body_text(msg)
413                body_text = ('\r\n'.join(body_text.splitlines()))
414
415                # A patch so that the web-interface will not update the description
416                # field of a ticket
417                #
418                tkt['description'] = u'email2trac:%s\r\n{{{\r\n%s\r\n}}}' \
419                        %(head, body_text)
420
421                when = int(time.time())
422                if self.VERSION == 0.8:
423                        ticket_id = tkt.insert(self.db)
424                else:
425                        ticket_id = tkt.insert()
426
427                changed = False
428                comment = ''
429
430                # Rewrite the description if we have mailto enabled
431                #
432                if self.MAILTO:
433                        mailto = self.html_mailto_link(self.to_unicode(msg['subject']), ticket_id, body_text)
434                        tkt['description'] = u'email2trac:%s%s\r\n{{{\r\n%s\r\n}}}' %(head, mailto, body_text)
435                        comment = '\nadded mailto line\n'
436                        changed = True
437
438                n =  self.attachments(msg, tkt)
439                if n:
440                        comment = '%s\nThis message has %d attachment(s)\n' %(comment, n)
441                        changed = True
442
443                if changed:
444                        if self.VERSION  == 0.8:
445                                tkt.save_changes(self.db, self.author, comment)
446                        else:
447                                tkt.save_changes(self.author, comment)
448
449                #print tkt.get_changelog(self.db, when)
450
451                if self.notification:
452                        self.notify(tkt, True)
453                        #self.notify(tkt, False)
454
455        def parse(self, fp):
456                m = email.message_from_file(fp)
457                if not m:
458                        return
459
460                if self.DEBUG > 1:        # save the entire e-mail message text
461                        self.save_email_for_debug(m)
462                        self.debug_attachments(m)
463
464                self.db = self.env.get_db_cnx()
465                self.get_author_emailaddrs(m)
466
467                if self.get_config('notification', 'smtp_enabled') in ['true']:
468                        self.notification = 1
469                else:
470                        self.notification = 0
471
472                # Must we update existing tickets
473                #
474                if self.TICKET_UPDATE > 0:
475                        if self.ticket_update(m):
476                                return True
477
478                self.new_ticket(m)
479
480        def get_body_text(self, msg):
481                """
482                put the message text in the ticket description or in the changes field.
483                message text can be plain text or html or something else
484                """
485                has_description = 0
486                ubody_text = '\r\n{{{\nNo plain text message\n}}}\r\n'
487                for part in msg.walk():
488
489                        # 'multipart/*' is a container for multipart messages
490                        #
491                        if part.get_content_maintype() == 'multipart':
492                                continue
493
494                        if part.get_content_type() == 'text/plain':
495                                # Try to decode, if fails then do not decode
496                                #
497                                body_text = part.get_payload(decode=1)
498                                if not body_text:                       
499                                        body_text = part.get_payload(decode=0)
500
501                                # Get contents charset (iso-8859-15 if not defined in mail headers)
502                                # UTF-8 encode body_text
503                                #
504                                charset = part.get_content_charset('iso-8859-15')
505                                try:
506                                        temp = unicode(body_text, charset)
507                                except (UnicodeError,LookupError):
508                                        temp = unicode(body_text, 'iso-8859-15')
509
510                                ubody_text = temp.encode('utf-8')
511
512                        elif part.get_content_type() == 'text/html':
513                                ubody_text = '\r\n(see attachment for HTML mail message)\r\n'
514
515                        else:
516                                ubody_text = '\r\n(see attachment for message)\r\n'
517
518                        has_description = 1
519                        break           # we have the description, so break
520
521                if not has_description:
522                        ubody_text = '\r\n(see attachment for message)\r\n'
523
524                return ubody_text
525
526        def notify(self, tkt , new=True, modtime=0):
527                """
528                A wrapper for the TRAC notify function. So we can use templates
529                """
530                try:
531                        # create false {abs_}href properties, to trick Notify()
532                        #
533                        self.env.abs_href = Href(self.get_config('project', 'url'))
534                        self.env.href = Href(self.get_config('project', 'url'))
535
536                        tn = TicketNotifyEmail(self.env)
537                        if self.notify_template:
538                                tn.template_name = self.notify_template;
539
540                        tn.notify(tkt, new, modtime)
541
542                except Exception, e:
543                        print 'TD: Failure sending notification on creation of ticket #%s: %s' %(tkt['id'], e)
544
545        def mail_line(self, str):
546                return '%s %s' % (self.comment, str)
547
548
549        def html_mailto_link(self, subject, id, body):
550                if not self.author:
551                        author = self.email_addr
552                else:   
553                        author = self.to_unicode(self.author)
554
555                # Must find a fix
556                #
557                #arr = string.split(body, '\n')
558                #arr = map(self.mail_line, arr)
559                #body = string.join(arr, '\n')
560                #body = '%s wrote:\n%s' %(author, body)
561
562                # Temporary fix
563                str = 'mailto:%s?Subject=%s&Cc=%s' %(
564                       urllib.quote(self.email_addr),
565                           urllib.quote('Re: #%s: %s' %(id, subject)),
566                           urllib.quote(self.MAILTO_CC)
567                           )
568
569                str = '\r\n{{{\r\n#!html\r\n<a href="%s">Reply to: %s</a>\r\n}}}\r\n' %(str, author)
570
571                return str
572
573        def attachments(self, message, ticket):
574                '''
575                save any attachments as files in the ticket's directory
576                '''
577                count = 0
578                first = 0
579                number = 0
580                for part in message.walk():
581                        if part.get_content_maintype() == 'multipart':          # multipart/* is just a container
582                                continue
583
584                        if not first:                                                                           # first content is the message
585                                first = 1
586                                if part.get_content_type() == 'text/plain':             # if first is text, is was already put in the description
587                                        continue
588
589                        filename = part.get_filename()
590                        count = count + 1
591                        if not filename:
592                                number = number + 1
593                                filename = 'part%04d' % number
594
595                                ext = mimetypes.guess_extension(part.get_content_type())
596                                if not ext:
597                                        ext = '.bin'
598
599                                filename = '%s%s' % (filename, ext)
600                        else:
601                                filename = self.to_unicode(filename)
602
603                        # From the trac code
604                        #
605                        filename = filename.replace('\\', '/').replace(':', '/')
606                        filename = os.path.basename(filename)
607
608                        # We try to normalize the filename to utf-8 NFC if we can.
609                        # Files uploaded from OS X might be in NFD.
610                        #
611                        if sys.version_info[0] > 2 or (sys.version_info[0] == 2 and sys.version_info[1] >= 3):
612                                filename = unicodedata.normalize('NFC', unicode(filename, 'utf-8')).encode('utf-8') 
613
614                        url_filename = urllib.quote(filename)
615                        if self.VERSION == 0.8:
616                                dir = os.path.join(self.env.get_attachments_dir(), 'ticket',
617                                                        urllib.quote(str(ticket['id'])))
618                                if not os.path.exists(dir):
619                                        mkdir_p(dir, 0755)
620                        else:
621                                dir = '/tmp'
622
623                        path, fd =  util.create_unique_file(os.path.join(dir, url_filename))
624                        text = part.get_payload(decode=1)
625                        if not text:
626                                text = '(None)'
627                        fd.write(text)
628                        fd.close()
629
630                        # get the filesize
631                        #
632                        stats = os.lstat(path)
633                        filesize = stats[stat.ST_SIZE]
634
635                        # Insert the attachment it differs for the different TRAC versions
636                        #
637                        if self.VERSION == 0.8:
638                                cursor = self.db.cursor()
639                                try:
640                                        cursor.execute('INSERT INTO attachment VALUES("%s","%s","%s",%d,%d,"%s","%s","%s")'
641                                                %('ticket', urllib.quote(str(ticket['id'])), filename + '?format=raw', filesize,
642                                                int(time.time()),'', self.author, 'e-mail') )
643
644                                # Attachment is already known
645                                #
646                                except sqlite.IntegrityError:   
647                                        #self.db.close()
648                                        return count
649
650                                self.db.commit()
651
652                        else:
653                                fd = open(path)
654                                att = attachment.Attachment(self.env, 'ticket', ticket['id'])
655                                att.insert(url_filename, fd, filesize)
656                                fd.close()
657
658                # Return how many attachments
659                #
660                return count
661
662
663def mkdir_p(dir, mode):
664        '''do a mkdir -p'''
665
666        arr = string.split(dir, '/')
667        path = ''
668        for part in arr:
669                path = '%s/%s' % (path, part)
670                try:
671                        stats = os.stat(path)
672                except OSError:
673                        os.mkdir(path, mode)
674
675
676def ReadConfig(file, name):
677        """
678        Parse the config file
679        """
680
681        if not os.path.isfile(file):
682                print 'File %s does not exist' %file
683                sys.exit(1)
684
685        config = ConfigParser.ConfigParser()
686        try:
687                config.read(file)
688        except ConfigParser.MissingSectionHeaderError,detail:
689                print detail
690                sys.exit(1)
691
692
693        # Use given project name else use defaults
694        #
695        if name:
696                if not config.has_section(name):
697                        print "Not a valid project name: %s" %name
698                        print "Valid names: %s" %config.sections()
699                        sys.exit(1)
700
701                project =  dict()
702                for option in  config.options(name):
703                        project[option] = config.get(name, option)
704
705        else:
706                project = config.defaults()
707
708        return project
709
710
711if __name__ == '__main__':
712        # Default config file
713        #
714        configfile = '@email2trac_conf@'
715        project = ''
716        component = ''
717        ENABLE_SYSLOG = 0
718               
719        try:
720                opts, args = getopt.getopt(sys.argv[1:], 'chf:p:', ['component=','help', 'file=', 'project='])
721        except getopt.error,detail:
722                print __doc__
723                print detail
724                sys.exit(1)
725       
726        project_name = None
727        for opt,value in opts:
728                if opt in [ '-h', '--help']:
729                        print __doc__
730                        sys.exit(0)
731                elif opt in ['-c', '--component']:
732                        component = value
733                elif opt in ['-f', '--file']:
734                        configfile = value
735                elif opt in ['-p', '--project']:
736                        project_name = value
737       
738        settings = ReadConfig(configfile, project_name)
739        if not settings.has_key('project'):
740                print __doc__
741                print 'No Trac project is defined in the email2trac config file.'
742                sys.exit(1)
743       
744        if component:
745                settings['component'] = component
746       
747        if settings.has_key('trac_version'):
748                version = float(settings['trac_version'])
749        else:
750                version = trac_default_version
751
752        if settings.has_key('enable_syslog'):
753                ENABLE_SYSLOG =  float(settings['enable_syslog'])
754                       
755        #debug HvB
756        #print settings
757       
758        try:
759                if version == 0.8:
760                        from trac.Environment import Environment
761                        from trac.Ticket import Ticket
762                        from trac.Notify import TicketNotifyEmail
763                        from trac.Href import Href
764                        from trac import util
765                        import sqlite
766                elif version == 0.9:
767                        from trac import attachment
768                        from trac.env import Environment
769                        from trac.ticket import Ticket
770                        from trac.web.href import Href
771                        from trac import util
772                        from trac.Notify import TicketNotifyEmail
773                elif version == 0.10:
774                        from trac import attachment
775                        from trac.env import Environment
776                        from trac.ticket import Ticket
777                        from trac.web.href import Href
778                        from trac import util
779                        # see http://projects.edgewall.com/trac/changeset/2799
780                        from trac.ticket.notification import TicketNotifyEmail
781       
782                env = Environment(settings['project'], create=0)
783                tktparser = TicketEmailParser(env, settings, version)
784                tktparser.parse(sys.stdin)
785
786        # Catch all errors ans log to SYSLOG if we have enabled this
787        # else stdout
788        #
789        except Exception, error:
790                import syslog, traceback
791
792                if ENABLE_SYSLOG:
793                        syslog.openlog('email2trac', syslog.LOG_NOWAIT)
794                        etype, evalue, etb = sys.exc_info()
795                        for e in traceback.format_exception(etype, evalue, etb):
796                                syslog.syslog(e)
797                        syslog.closelog()
798                else:
799                        traceback.print_exc()
800
801# EOB
Note: See TracBrowser for help on using the repository browser.