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

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

EmailtoTracScript?:

email2trac.py.in:

  • Remove the temporarly created attachment files.
  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 21.1 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 103 2006-07-17 11:51:07Z 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                        # Remove the created temporary filename
689                        #
690                        os.unlink(path)
691
692                # Return how many attachments
693                #
694                return count
695
696
697def mkdir_p(dir, mode):
698        '''do a mkdir -p'''
699
700        arr = string.split(dir, '/')
701        path = ''
702        for part in arr:
703                path = '%s/%s' % (path, part)
704                try:
705                        stats = os.stat(path)
706                except OSError:
707                        os.mkdir(path, mode)
708
709
710def ReadConfig(file, name):
711        """
712        Parse the config file
713        """
714
715        if not os.path.isfile(file):
716                print 'File %s does not exist' %file
717                sys.exit(1)
718
719        config = ConfigParser.ConfigParser()
720        try:
721                config.read(file)
722        except ConfigParser.MissingSectionHeaderError,detail:
723                print detail
724                sys.exit(1)
725
726
727        # Use given project name else use defaults
728        #
729        if name:
730                if not config.has_section(name):
731                        print "Not a valid project name: %s" %name
732                        print "Valid names: %s" %config.sections()
733                        sys.exit(1)
734
735                project =  dict()
736                for option in  config.options(name):
737                        project[option] = config.get(name, option)
738
739        else:
740                project = config.defaults()
741
742        return project
743
744
745if __name__ == '__main__':
746        # Default config file
747        #
748        configfile = '@email2trac_conf@'
749        project = ''
750        component = ''
751        ENABLE_SYSLOG = 0
752               
753        try:
754                opts, args = getopt.getopt(sys.argv[1:], 'chf:p:', ['component=','help', 'file=', 'project='])
755        except getopt.error,detail:
756                print __doc__
757                print detail
758                sys.exit(1)
759       
760        project_name = None
761        for opt,value in opts:
762                if opt in [ '-h', '--help']:
763                        print __doc__
764                        sys.exit(0)
765                elif opt in ['-c', '--component']:
766                        component = value
767                elif opt in ['-f', '--file']:
768                        configfile = value
769                elif opt in ['-p', '--project']:
770                        project_name = value
771       
772        settings = ReadConfig(configfile, project_name)
773        if not settings.has_key('project'):
774                print __doc__
775                print 'No Trac project is defined in the email2trac config file.'
776                sys.exit(1)
777       
778        if component:
779                settings['component'] = component
780       
781        if settings.has_key('trac_version'):
782                version = float(settings['trac_version'])
783        else:
784                version = trac_default_version
785
786        if settings.has_key('enable_syslog'):
787                ENABLE_SYSLOG =  float(settings['enable_syslog'])
788                       
789        #debug HvB
790        #print settings
791       
792        try:
793                if version == 0.8:
794                        from trac.Environment import Environment
795                        from trac.Ticket import Ticket
796                        from trac.Notify import TicketNotifyEmail
797                        from trac.Href import Href
798                        from trac import util
799                        import sqlite
800                elif version == 0.9:
801                        from trac import attachment
802                        from trac.env import Environment
803                        from trac.ticket import Ticket
804                        from trac.web.href import Href
805                        from trac import util
806                        from trac.Notify import TicketNotifyEmail
807                elif version == 0.10:
808                        from trac import attachment
809                        from trac.env import Environment
810                        from trac.ticket import Ticket
811                        from trac.web.href import Href
812                        from trac import util
813                        # see http://projects.edgewall.com/trac/changeset/2799
814                        from trac.ticket.notification import TicketNotifyEmail
815       
816                env = Environment(settings['project'], create=0)
817                tktparser = TicketEmailParser(env, settings, version)
818                tktparser.parse(sys.stdin)
819
820        # Catch all errors ans log to SYSLOG if we have enabled this
821        # else stdout
822        #
823        except Exception, error:
824                if ENABLE_SYSLOG:
825                        syslog.openlog('email2trac', syslog.LOG_NOWAIT)
826                        etype, evalue, etb = sys.exc_info()
827                        for e in traceback.format_exception(etype, evalue, etb):
828                                syslog.syslog(e)
829                        syslog.closelog()
830                else:
831                        traceback.print_exc()
832
833                if m:
834                        tktparser.save_email_for_debug(m, True)
835
836# EOB
Note: See TracBrowser for help on using the repository browser.