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

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

EmailtoTracScript?:

email2trac.py.in:

  • Added author and description field for attachments
  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 21.2 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 117 2006-10-05 18:41:00Z 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                        if not addrs:
300                                return
301
302                        # Remove reporter email address if notification is
303                        # on
304                        #
305                        if self.notification:
306                                try:
307                                        addrs.remove((self.author, self.email_addr))
308                                except ValueError, detail:
309                                        pass
310
311                        for name,mail in addrs:
312                                try:
313                                        mail_list = '%s, %s' %(mail_list, mail)
314                                except:
315                                        mail_list = mail
316
317                        if mail_list:
318                                ticket['cc'] = self.to_unicode(mail_list)
319
320        def save_email_for_debug(self, message, tempfile=False):
321                if tempfile:
322                        import tempfile
323                        msg_file = tempfile.mktemp('.email2trac')
324                else:
325                        msg_file = '/var/tmp/msg.txt'
326                print 'TD: saving email to %s' % msg_file
327                fx = open(msg_file, 'wb')
328                fx.write('%s' % message)
329                fx.close()
330                try:
331                        os.chmod(msg_file,S_IRWXU|S_IRWXG|S_IRWXO)
332                except OSError:
333                        pass
334
335        def ticket_update(self, m):
336                """
337                If the current email is a reply to an existing ticket, this function
338                will append the contents of this email to that ticket, instead of
339                creating a new one.
340                """
341                if not m['Subject']:
342                        return False
343                else:
344                        subject  = self.to_unicode(m['Subject'])
345
346                TICKET_RE = re.compile(r"""
347                                        (?P<ticketnr>[#][0-9]+:)
348                                        """, re.VERBOSE)
349
350                result =  TICKET_RE.search(subject)
351                if not result:
352                        return False
353
354                body_text = self.get_body_text(m)
355
356                # Strip '#' and ':' from ticket_id
357                #
358                ticket_id = result.group('ticketnr')
359                ticket_id = int(ticket_id[1:-1])
360
361                # Get current time
362                #
363                when = int(time.time())
364
365                if self.VERSION  == 0.8:
366                        tkt = Ticket(self.db, ticket_id)
367                        tkt.save_changes(self.db, self.author, body_text, when)
368                else:
369                        tkt = Ticket(self.env, ticket_id, self.db)
370                        tkt.save_changes(self.author, body_text, when)
371                        tkt['id'] = ticket_id
372
373                self.attachments(m, tkt)
374
375                if self.notification:
376                        self.notify(tkt, False, when)
377
378                return True
379
380        def new_ticket(self, msg):
381                """
382                Create a new ticket
383                """
384                tkt = Ticket(self.env)
385                tkt['status'] = 'new'
386
387                # Some defaults
388                #
389                tkt['milestone'] = self.get_config('ticket', 'default_milestone')
390                tkt['priority'] = self.get_config('ticket', 'default_priority')
391                tkt['severity'] = self.get_config('ticket', 'default_severity')
392                tkt['version'] = self.get_config('ticket', 'default_version')
393
394                if not msg['Subject']:
395                        tkt['summary'] = u'(geen subject)'
396                else:
397                        tkt['summary'] = self.to_unicode(msg['Subject'])
398
399
400                if settings.has_key('component'):
401                        tkt['component'] = settings['component']
402                else:
403                        tkt['component'] = self.spam(msg)
404
405                # Must make this an option or so, discard SPAM messages or save then
406                # and delete later
407                #
408                #if self.SPAM_LEVEL and self.spam(msg):
409                #       print 'This message is a SPAM. Automatic ticket insertion refused (SPAM level > %d' % self.SPAM_LEVEL
410                #       sys.exit(1)
411
412                # Set default owner for component
413                #
414                self.set_owner(tkt)
415                self.set_reply_fields(tkt, msg)
416
417                # produce e-mail like header
418                #
419                head = ''
420                if self.EMAIL_HEADER > 0:
421                        head = self.email_header_txt(msg)
422                       
423
424                body_text = self.get_body_text(msg)
425
426                tkt['description'] = 'email2trac:%s\r\n%s' \
427                        %(head, body_text)
428
429                when = int(time.time())
430                if self.VERSION == 0.8:
431                        ticket_id = tkt.insert(self.db)
432                else:
433                        ticket_id = tkt.insert()
434                        tkt['id'] = ticket_id
435
436                changed = False
437                comment = ''
438
439                # Rewrite the description if we have mailto enabled
440                #
441                if self.MAILTO:
442                        changed = True
443                        comment = '\nadded mailto line\n'
444                        mailto = self.html_mailto_link(self.to_unicode(msg['subject']), ticket_id, body_text)
445                        #tkt['description'] = 'email2trac:%s%s\r\n{{{\r\n%s\r\n}}}' %(head, mailto, body_text)
446                        tkt['description'] = 'email2trac:%s%s\r\n%s' %(head, mailto, body_text)
447
448                n =  self.attachments(msg, tkt)
449                if n:
450                        changed = True
451                        comment = '%s\nThis message has %d attachment(s)\n' %(comment, n)
452
453                if changed:
454                        if self.VERSION  == 0.8:
455                                tkt.save_changes(self.db, self.author, comment)
456                        else:
457                                tkt.save_changes(self.author, comment)
458
459                #print tkt.get_changelog(self.db, when)
460
461                if self.notification:
462                        self.notify(tkt, True)
463                        #self.notify(tkt, False)
464
465        def parse(self, fp):
466                global m
467
468                m = email.message_from_file(fp)
469                if not m:
470                        return
471
472                if self.DEBUG > 1:        # save the entire e-mail message text
473                        self.save_email_for_debug(m)
474                        self.debug_attachments(m)
475
476                self.db = self.env.get_db_cnx()
477                self.get_author_emailaddrs(m)
478
479                if self.get_config('notification', 'smtp_enabled') in ['true']:
480                        self.notification = 1
481                else:
482                        self.notification = 0
483
484                # Must we update existing tickets
485                #
486                if self.TICKET_UPDATE > 0:
487                        if self.ticket_update(m):
488                                return True
489
490                self.new_ticket(m)
491
492        def get_body_text(self, msg):
493                """
494                put the message text in the ticket description or in the changes field.
495                message text can be plain text or html or something else
496                """
497                has_description = 0
498                encoding = True
499                ubody_text = u'No plain text message'
500                for part in msg.walk():
501
502                        # 'multipart/*' is a container for multipart messages
503                        #
504                        if part.get_content_maintype() == 'multipart':
505                                continue
506
507                        if part.get_content_type() == 'text/plain':
508                                # Try to decode, if fails then do not decode
509                                #
510                                body_text = part.get_payload(decode=1)
511                                if not body_text:                       
512                                        body_text = part.get_payload(decode=0)
513
514                                # Get contents charset (iso-8859-15 if not defined in mail headers)
515                                #
516                                charset = part.get_content_charset()
517                                if not charset:
518                                        charset = 'iso-8859-15'
519
520                                try:
521                                        ubody_text = unicode(body_text, charset)
522
523                                except UnicodeError, detail:
524                                        ubody_text = unicode(body_text, 'iso-8859-15')
525
526                                except LookupError, detail:
527                                        ubody_text = body_text
528                                        encoding = False
529
530                        elif part.get_content_type() == 'text/html':
531                                ubody_text = '(see attachment for HTML mail message)'
532
533                        else:
534                                ubody_text = '(see attachment for message)'
535
536                        has_description = 1
537                        break           # we have the description, so break
538
539                if not has_description:
540                        ubody_text = '(see attachment for message)'
541
542                # A patch so that the web-interface will not update the description
543                # field of a ticket
544                #
545                ubody_text = ('\r\n'.join(ubody_text.splitlines()))
546
547                #  If we can unicode it try to encode it for trac
548                #  else we a lot of garbage
549                #
550                if encoding:
551                        ubody_text = ubody_text.encode('utf-8')
552
553                ubody_text = '{{{\r\n%s\r\n}}}' %ubody_text
554                return ubody_text
555
556        def notify(self, tkt , new=True, modtime=0):
557                """
558                A wrapper for the TRAC notify function. So we can use templates
559                """
560                if tkt['component'] == 'Spam':
561                        return 
562
563                try:
564                        # create false {abs_}href properties, to trick Notify()
565                        #
566                        self.env.abs_href = Href(self.get_config('project', 'url'))
567                        self.env.href = Href(self.get_config('project', 'url'))
568
569                        tn = TicketNotifyEmail(self.env)
570                        if self.notify_template:
571                                tn.template_name = self.notify_template;
572
573                        tn.notify(tkt, new, modtime)
574
575                except Exception, e:
576                        print 'TD: Failure sending notification on creation of ticket #%s: %s' %(tkt['id'], e)
577
578        def mail_line(self, str):
579                return '%s %s' % (self.comment, str)
580
581
582        def html_mailto_link(self, subject, id, body):
583                if not self.author:
584                        author = self.email_addr
585                else:   
586                        author = self.to_unicode(self.author)
587
588                # Must find a fix
589                #
590                #arr = string.split(body, '\n')
591                #arr = map(self.mail_line, arr)
592                #body = string.join(arr, '\n')
593                #body = '%s wrote:\n%s' %(author, body)
594
595                # Temporary fix
596                str = 'mailto:%s?Subject=%s&Cc=%s' %(
597                       urllib.quote(self.email_addr),
598                           urllib.quote('Re: #%s: %s' %(id, subject)),
599                           urllib.quote(self.MAILTO_CC)
600                           )
601
602                str = '\r\n{{{\r\n#!html\r\n<a href="%s">Reply to: %s</a>\r\n}}}\r\n' %(str, author)
603
604                return str
605
606        def attachments(self, message, ticket):
607                '''
608                save any attachments as files in the ticket's directory
609                '''
610                count = 0
611                first = 0
612                number = 0
613                for part in message.walk():
614                        if part.get_content_maintype() == 'multipart':          # multipart/* is just a container
615                                continue
616
617                        if not first:                                                                           # first content is the message
618                                first = 1
619                                if part.get_content_type() == 'text/plain':             # if first is text, is was already put in the description
620                                        continue
621
622                        filename = part.get_filename()
623                        count = count + 1
624                        if not filename:
625                                number = number + 1
626                                filename = 'part%04d' % number
627
628                                ext = mimetypes.guess_extension(part.get_content_type())
629                                if not ext:
630                                        ext = '.bin'
631
632                                filename = '%s%s' % (filename, ext)
633                        else:
634                                filename = self.to_unicode(filename)
635
636                        # From the trac code
637                        #
638                        filename = filename.replace('\\', '/').replace(':', '/')
639                        filename = os.path.basename(filename)
640
641                        # We try to normalize the filename to utf-8 NFC if we can.
642                        # Files uploaded from OS X might be in NFD.
643                        # Check python version and then try it
644                        #
645                        if sys.version_info[0] > 2 or (sys.version_info[0] == 2 and sys.version_info[1] >= 3):
646                                try:
647                                        filename = unicodedata.normalize('NFC', unicode(filename, 'utf-8')).encode('utf-8') 
648                                except TypeError:
649                                        pass
650
651                        url_filename = urllib.quote(filename)
652                        if self.VERSION == 0.8:
653                                dir = os.path.join(self.env.get_attachments_dir(), 'ticket',
654                                                        urllib.quote(str(ticket['id'])))
655                                if not os.path.exists(dir):
656                                        mkdir_p(dir, 0755)
657                        else:
658                                dir = '/tmp'
659
660                        path, fd =  util.create_unique_file(os.path.join(dir, url_filename))
661                        text = part.get_payload(decode=1)
662                        if not text:
663                                text = '(None)'
664                        fd.write(text)
665                        fd.close()
666
667                        # get the filesize
668                        #
669                        stats = os.lstat(path)
670                        filesize = stats[stat.ST_SIZE]
671
672                        # Insert the attachment it differs for the different TRAC versions
673                        #
674                        if self.VERSION == 0.8:
675                                cursor = self.db.cursor()
676                                try:
677                                        cursor.execute('INSERT INTO attachment VALUES("%s","%s","%s",%d,%d,"%s","%s","%s")'
678                                                %('ticket', urllib.quote(str(ticket['id'])), filename + '?format=raw', filesize,
679                                                int(time.time()),'', self.author, 'e-mail') )
680
681                                # Attachment is already known
682                                #
683                                except sqlite.IntegrityError:   
684                                        #self.db.close()
685                                        return count
686
687                                self.db.commit()
688
689                        else:
690                                fd = open(path)
691                                att = attachment.Attachment(self.env, 'ticket', ticket['id'])
692                                att.author = self.author
693                                att.description = 'email2trac'
694                                att.insert(url_filename, fd, filesize)
695                                fd.close()
696
697                        # Remove the created temporary filename
698                        #
699                        os.unlink(path)
700
701                # Return how many attachments
702                #
703                return count
704
705
706def mkdir_p(dir, mode):
707        '''do a mkdir -p'''
708
709        arr = string.split(dir, '/')
710        path = ''
711        for part in arr:
712                path = '%s/%s' % (path, part)
713                try:
714                        stats = os.stat(path)
715                except OSError:
716                        os.mkdir(path, mode)
717
718
719def ReadConfig(file, name):
720        """
721        Parse the config file
722        """
723
724        if not os.path.isfile(file):
725                print 'File %s does not exist' %file
726                sys.exit(1)
727
728        config = ConfigParser.ConfigParser()
729        try:
730                config.read(file)
731        except ConfigParser.MissingSectionHeaderError,detail:
732                print detail
733                sys.exit(1)
734
735
736        # Use given project name else use defaults
737        #
738        if name:
739                if not config.has_section(name):
740                        print "Not a valid project name: %s" %name
741                        print "Valid names: %s" %config.sections()
742                        sys.exit(1)
743
744                project =  dict()
745                for option in  config.options(name):
746                        project[option] = config.get(name, option)
747
748        else:
749                project = config.defaults()
750
751        return project
752
753
754if __name__ == '__main__':
755        # Default config file
756        #
757        configfile = '@email2trac_conf@'
758        project = ''
759        component = ''
760        ENABLE_SYSLOG = 0
761               
762        try:
763                opts, args = getopt.getopt(sys.argv[1:], 'chf:p:', ['component=','help', 'file=', 'project='])
764        except getopt.error,detail:
765                print __doc__
766                print detail
767                sys.exit(1)
768       
769        project_name = None
770        for opt,value in opts:
771                if opt in [ '-h', '--help']:
772                        print __doc__
773                        sys.exit(0)
774                elif opt in ['-c', '--component']:
775                        component = value
776                elif opt in ['-f', '--file']:
777                        configfile = value
778                elif opt in ['-p', '--project']:
779                        project_name = value
780       
781        settings = ReadConfig(configfile, project_name)
782        if not settings.has_key('project'):
783                print __doc__
784                print 'No Trac project is defined in the email2trac config file.'
785                sys.exit(1)
786       
787        if component:
788                settings['component'] = component
789       
790        if settings.has_key('trac_version'):
791                version = float(settings['trac_version'])
792        else:
793                version = trac_default_version
794
795        if settings.has_key('enable_syslog'):
796                ENABLE_SYSLOG =  float(settings['enable_syslog'])
797                       
798        #debug HvB
799        #print settings
800       
801        try:
802                if version == 0.8:
803                        from trac.Environment import Environment
804                        from trac.Ticket import Ticket
805                        from trac.Notify import TicketNotifyEmail
806                        from trac.Href import Href
807                        from trac import util
808                        import sqlite
809                elif version == 0.9:
810                        from trac import attachment
811                        from trac.env import Environment
812                        from trac.ticket import Ticket
813                        from trac.web.href import Href
814                        from trac import util
815                        from trac.Notify import TicketNotifyEmail
816                elif version == 0.10:
817                        from trac import attachment
818                        from trac.env import Environment
819                        from trac.ticket import Ticket
820                        from trac.web.href import Href
821                        from trac import util
822                        # see http://projects.edgewall.com/trac/changeset/2799
823                        from trac.ticket.notification import TicketNotifyEmail
824       
825                env = Environment(settings['project'], create=0)
826                tktparser = TicketEmailParser(env, settings, version)
827                tktparser.parse(sys.stdin)
828
829        # Catch all errors ans log to SYSLOG if we have enabled this
830        # else stdout
831        #
832        except Exception, error:
833                if ENABLE_SYSLOG:
834                        syslog.openlog('email2trac', syslog.LOG_NOWAIT)
835                        etype, evalue, etb = sys.exc_info()
836                        for e in traceback.format_exception(etype, evalue, etb):
837                                syslog.syslog(e)
838                        syslog.closelog()
839                else:
840                        traceback.print_exc()
841
842                if m:
843                        tktparser.save_email_for_debug(m, True)
844
845# EOB
Note: See TracBrowser for help on using the repository browser.