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

Last change on this file since 102 was 102, checked in by bas, 18 years ago

EmailtoTracScript?:

email2trac.py.in:

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