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

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

EmailtoTracScript?:

email2trac.py.in:

  • Changed some layout statements

configure, Makefile.in, configure.in:

  • Can set DEBUG value on command line; eg: DEBUG=1 ./configure default is: 0
  • 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 109 2006-09-29 07:27:12Z 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                try:
561                        # create false {abs_}href properties, to trick Notify()
562                        #
563                        self.env.abs_href = Href(self.get_config('project', 'url'))
564                        self.env.href = Href(self.get_config('project', 'url'))
565
566                        tn = TicketNotifyEmail(self.env)
567                        if self.notify_template:
568                                tn.template_name = self.notify_template;
569
570                        tn.notify(tkt, new, modtime)
571
572                except Exception, e:
573                        print 'TD: Failure sending notification on creation of ticket #%s: %s' %(tkt['id'], e)
574
575        def mail_line(self, str):
576                return '%s %s' % (self.comment, str)
577
578
579        def html_mailto_link(self, subject, id, body):
580                if not self.author:
581                        author = self.email_addr
582                else:   
583                        author = self.to_unicode(self.author)
584
585                # Must find a fix
586                #
587                #arr = string.split(body, '\n')
588                #arr = map(self.mail_line, arr)
589                #body = string.join(arr, '\n')
590                #body = '%s wrote:\n%s' %(author, body)
591
592                # Temporary fix
593                str = 'mailto:%s?Subject=%s&Cc=%s' %(
594                       urllib.quote(self.email_addr),
595                           urllib.quote('Re: #%s: %s' %(id, subject)),
596                           urllib.quote(self.MAILTO_CC)
597                           )
598
599                str = '\r\n{{{\r\n#!html\r\n<a href="%s">Reply to: %s</a>\r\n}}}\r\n' %(str, author)
600
601                return str
602
603        def attachments(self, message, ticket):
604                '''
605                save any attachments as files in the ticket's directory
606                '''
607                count = 0
608                first = 0
609                number = 0
610                for part in message.walk():
611                        if part.get_content_maintype() == 'multipart':          # multipart/* is just a container
612                                continue
613
614                        if not first:                                                                           # first content is the message
615                                first = 1
616                                if part.get_content_type() == 'text/plain':             # if first is text, is was already put in the description
617                                        continue
618
619                        filename = part.get_filename()
620                        count = count + 1
621                        if not filename:
622                                number = number + 1
623                                filename = 'part%04d' % number
624
625                                ext = mimetypes.guess_extension(part.get_content_type())
626                                if not ext:
627                                        ext = '.bin'
628
629                                filename = '%s%s' % (filename, ext)
630                        else:
631                                filename = self.to_unicode(filename)
632
633                        # From the trac code
634                        #
635                        filename = filename.replace('\\', '/').replace(':', '/')
636                        filename = os.path.basename(filename)
637
638                        # We try to normalize the filename to utf-8 NFC if we can.
639                        # Files uploaded from OS X might be in NFD.
640                        # Check python version and then try it
641                        #
642                        if sys.version_info[0] > 2 or (sys.version_info[0] == 2 and sys.version_info[1] >= 3):
643                                try:
644                                        filename = unicodedata.normalize('NFC', unicode(filename, 'utf-8')).encode('utf-8') 
645                                except TypeError:
646                                        pass
647
648                        url_filename = urllib.quote(filename)
649                        if self.VERSION == 0.8:
650                                dir = os.path.join(self.env.get_attachments_dir(), 'ticket',
651                                                        urllib.quote(str(ticket['id'])))
652                                if not os.path.exists(dir):
653                                        mkdir_p(dir, 0755)
654                        else:
655                                dir = '/tmp'
656
657                        path, fd =  util.create_unique_file(os.path.join(dir, url_filename))
658                        text = part.get_payload(decode=1)
659                        if not text:
660                                text = '(None)'
661                        fd.write(text)
662                        fd.close()
663
664                        # get the filesize
665                        #
666                        stats = os.lstat(path)
667                        filesize = stats[stat.ST_SIZE]
668
669                        # Insert the attachment it differs for the different TRAC versions
670                        #
671                        if self.VERSION == 0.8:
672                                cursor = self.db.cursor()
673                                try:
674                                        cursor.execute('INSERT INTO attachment VALUES("%s","%s","%s",%d,%d,"%s","%s","%s")'
675                                                %('ticket', urllib.quote(str(ticket['id'])), filename + '?format=raw', filesize,
676                                                int(time.time()),'', self.author, 'e-mail') )
677
678                                # Attachment is already known
679                                #
680                                except sqlite.IntegrityError:   
681                                        #self.db.close()
682                                        return count
683
684                                self.db.commit()
685
686                        else:
687                                fd = open(path)
688                                att = attachment.Attachment(self.env, 'ticket', ticket['id'])
689                                att.insert(url_filename, fd, filesize)
690                                fd.close()
691
692                        # Remove the created temporary filename
693                        #
694                        os.unlink(path)
695
696                # Return how many attachments
697                #
698                return count
699
700
701def mkdir_p(dir, mode):
702        '''do a mkdir -p'''
703
704        arr = string.split(dir, '/')
705        path = ''
706        for part in arr:
707                path = '%s/%s' % (path, part)
708                try:
709                        stats = os.stat(path)
710                except OSError:
711                        os.mkdir(path, mode)
712
713
714def ReadConfig(file, name):
715        """
716        Parse the config file
717        """
718
719        if not os.path.isfile(file):
720                print 'File %s does not exist' %file
721                sys.exit(1)
722
723        config = ConfigParser.ConfigParser()
724        try:
725                config.read(file)
726        except ConfigParser.MissingSectionHeaderError,detail:
727                print detail
728                sys.exit(1)
729
730
731        # Use given project name else use defaults
732        #
733        if name:
734                if not config.has_section(name):
735                        print "Not a valid project name: %s" %name
736                        print "Valid names: %s" %config.sections()
737                        sys.exit(1)
738
739                project =  dict()
740                for option in  config.options(name):
741                        project[option] = config.get(name, option)
742
743        else:
744                project = config.defaults()
745
746        return project
747
748
749if __name__ == '__main__':
750        # Default config file
751        #
752        configfile = '@email2trac_conf@'
753        project = ''
754        component = ''
755        ENABLE_SYSLOG = 0
756               
757        try:
758                opts, args = getopt.getopt(sys.argv[1:], 'chf:p:', ['component=','help', 'file=', 'project='])
759        except getopt.error,detail:
760                print __doc__
761                print detail
762                sys.exit(1)
763       
764        project_name = None
765        for opt,value in opts:
766                if opt in [ '-h', '--help']:
767                        print __doc__
768                        sys.exit(0)
769                elif opt in ['-c', '--component']:
770                        component = value
771                elif opt in ['-f', '--file']:
772                        configfile = value
773                elif opt in ['-p', '--project']:
774                        project_name = value
775       
776        settings = ReadConfig(configfile, project_name)
777        if not settings.has_key('project'):
778                print __doc__
779                print 'No Trac project is defined in the email2trac config file.'
780                sys.exit(1)
781       
782        if component:
783                settings['component'] = component
784       
785        if settings.has_key('trac_version'):
786                version = float(settings['trac_version'])
787        else:
788                version = trac_default_version
789
790        if settings.has_key('enable_syslog'):
791                ENABLE_SYSLOG =  float(settings['enable_syslog'])
792                       
793        #debug HvB
794        #print settings
795       
796        try:
797                if version == 0.8:
798                        from trac.Environment import Environment
799                        from trac.Ticket import Ticket
800                        from trac.Notify import TicketNotifyEmail
801                        from trac.Href import Href
802                        from trac import util
803                        import sqlite
804                elif version == 0.9:
805                        from trac import attachment
806                        from trac.env import Environment
807                        from trac.ticket import Ticket
808                        from trac.web.href import Href
809                        from trac import util
810                        from trac.Notify import TicketNotifyEmail
811                elif version == 0.10:
812                        from trac import attachment
813                        from trac.env import Environment
814                        from trac.ticket import Ticket
815                        from trac.web.href import Href
816                        from trac import util
817                        # see http://projects.edgewall.com/trac/changeset/2799
818                        from trac.ticket.notification import TicketNotifyEmail
819       
820                env = Environment(settings['project'], create=0)
821                tktparser = TicketEmailParser(env, settings, version)
822                tktparser.parse(sys.stdin)
823
824        # Catch all errors ans log to SYSLOG if we have enabled this
825        # else stdout
826        #
827        except Exception, error:
828                if ENABLE_SYSLOG:
829                        syslog.openlog('email2trac', syslog.LOG_NOWAIT)
830                        etype, evalue, etb = sys.exc_info()
831                        for e in traceback.format_exception(etype, evalue, etb):
832                                syslog.syslog(e)
833                        syslog.closelog()
834                else:
835                        traceback.print_exc()
836
837                if m:
838                        tktparser.save_email_for_debug(m, True)
839
840# EOB
Note: See TracBrowser for help on using the repository browser.