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

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

EmailtoTracScript?:

email2trac.py.in:

  • Fixed the unicode/encode problems
  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 21.0 KB
Line 
1#!@PYTHON@
2# Copyright (C) 2002
3#
4# This file is part of the email2trac utils
5#
6# This program is free software; you can redistribute it and/or modify it
7# under the terms of the GNU General Public License as published by the
8# Free Software Foundation; either version 2, or (at your option) any
9# later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
19#
20# For vi/emacs or other use tabstop=4 (vi: set ts=4)
21#
22"""
23email2trac.py -- Email tickets to Trac.
24
25A simple MTA filter to create Trac tickets from inbound emails.
26
27Copyright 2005, Daniel Lundin <daniel@edgewall.com>
28Copyright 2005, Edgewall Software
29
30Changed By: Bas van der Vlies <basv@sara.nl>
31Date      : 13 September 2005
32Descr.    : Added config file and command line options, spam level
33            detection, reply address and mailto option. Unicode support
34
35Changed By: Walter de Jong <walter@sara.nl>
36Descr.    : multipart-message code and trac attachments
37
38
39The scripts reads emails from stdin and inserts directly into a Trac database.
40MIME headers are mapped as follows:
41
42        * From:      => Reporter
43                     => CC (Optional via reply_address option)
44        * Subject:   => Summary
45        * Body       => Description
46        * Component  => Can be set to SPAM via spam_level option
47
48How to use
49----------
50 * Create an config file:
51        [DEFAULT]                      # REQUIRED
52        project      : /data/trac/test # REQUIRED
53        debug        : 1               # OPTIONAL, if set print some DEBUG info
54        spam_level   : 4               # OPTIONAL, if set check for SPAM mail
55        reply_address: 1               # OPTIONAL, if set then fill in ticket CC field
56        umask        : 022             # OPTIONAL, if set then use this umask for creation of the attachments
57        mailto_link  : 1               # OPTIONAL, if set then [mailto:<>] in description
58        mailto_cc    : basv@sara.nl    # OPTIONAL, use this address as CC in mailto line
59        ticket_update: 1               # OPTIONAL, if set then check if this is an update for a ticket
60        trac_version : 0.8             # OPTIONAL, default is 0.9
61
62        [jouvin]                         # OPTIONAL project declaration, if set both fields necessary
63        project      : /data/trac/jouvin # use -p|--project jouvin. 
64       
65 * default config file is : /etc/email2trac.conf
66
67 * Commandline opions:
68                -h | --help
69                -c <value> | --component=<value>
70                -f <config file> | --file=<config file>
71                -p <project name> | --project=<project name>
72
73SVN Info:
74        $Id: email2trac.py.in 100 2006-07-14 09:02:43Z 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                                try:
515                                        ubody_text = unicode(body_text, charset)
516
517                                except UnicodeError, detail:
518                                        ubody_text = unicode(body_text, 'iso-8859-15')
519
520                                except LookupError, detail:
521                                        ubody_text = body_text
522                                        encoding = False
523
524                        elif part.get_content_type() == 'text/html':
525                                ubody_text = '\r\n(see attachment for HTML mail message)\r\n'
526
527                        else:
528                                ubody_text = '\r\n(see attachment for message)\r\n'
529
530                        has_description = 1
531                        break           # we have the description, so break
532
533                if not has_description:
534                        ubody_text = '\r\n(see attachment for message)\r\n'
535
536                # A patch so that the web-interface will not update the description
537                # field of a ticket
538                #
539                ubody_text = ('\r\n'.join(ubody_text.splitlines()))
540
541                #  If we can unicode it try to encode it for trac
542                #  else we a lot of garbage
543                #
544                if encoding:
545                        ubody_text = ubody_text.encode('utf-8')
546
547                return ubody_text
548
549        def notify(self, tkt , new=True, modtime=0):
550                """
551                A wrapper for the TRAC notify function. So we can use templates
552                """
553                try:
554                        # create false {abs_}href properties, to trick Notify()
555                        #
556                        self.env.abs_href = Href(self.get_config('project', 'url'))
557                        self.env.href = Href(self.get_config('project', 'url'))
558
559                        tn = TicketNotifyEmail(self.env)
560                        if self.notify_template:
561                                tn.template_name = self.notify_template;
562
563                        tn.notify(tkt, new, modtime)
564
565                except Exception, e:
566                        print 'TD: Failure sending notification on creation of ticket #%s: %s' %(tkt['id'], e)
567
568        def mail_line(self, str):
569                return '%s %s' % (self.comment, str)
570
571
572        def html_mailto_link(self, subject, id, body):
573                if not self.author:
574                        author = self.email_addr
575                else:   
576                        author = self.to_unicode(self.author)
577
578                # Must find a fix
579                #
580                #arr = string.split(body, '\n')
581                #arr = map(self.mail_line, arr)
582                #body = string.join(arr, '\n')
583                #body = '%s wrote:\n%s' %(author, body)
584
585                # Temporary fix
586                str = 'mailto:%s?Subject=%s&Cc=%s' %(
587                       urllib.quote(self.email_addr),
588                           urllib.quote('Re: #%s: %s' %(id, subject)),
589                           urllib.quote(self.MAILTO_CC)
590                           )
591
592                str = '\r\n{{{\r\n#!html\r\n<a href="%s">Reply to: %s</a>\r\n}}}\r\n' %(str, author)
593
594                return str
595
596        def attachments(self, message, ticket):
597                '''
598                save any attachments as files in the ticket's directory
599                '''
600                count = 0
601                first = 0
602                number = 0
603                for part in message.walk():
604                        if part.get_content_maintype() == 'multipart':          # multipart/* is just a container
605                                continue
606
607                        if not first:                                                                           # first content is the message
608                                first = 1
609                                if part.get_content_type() == 'text/plain':             # if first is text, is was already put in the description
610                                        continue
611
612                        filename = part.get_filename()
613                        count = count + 1
614                        if not filename:
615                                number = number + 1
616                                filename = 'part%04d' % number
617
618                                ext = mimetypes.guess_extension(part.get_content_type())
619                                if not ext:
620                                        ext = '.bin'
621
622                                filename = '%s%s' % (filename, ext)
623                        else:
624                                filename = self.to_unicode(filename)
625
626                        # From the trac code
627                        #
628                        filename = filename.replace('\\', '/').replace(':', '/')
629                        filename = os.path.basename(filename)
630
631                        # We try to normalize the filename to utf-8 NFC if we can.
632                        # Files uploaded from OS X might be in NFD.
633                        # Check python version and then try it
634                        #
635                        if sys.version_info[0] > 2 or (sys.version_info[0] == 2 and sys.version_info[1] >= 3):
636                                try:
637                                        filename = unicodedata.normalize('NFC', unicode(filename, 'utf-8')).encode('utf-8') 
638                                except TypeError:
639                                        pass
640
641                        url_filename = urllib.quote(filename)
642                        if self.VERSION == 0.8:
643                                dir = os.path.join(self.env.get_attachments_dir(), 'ticket',
644                                                        urllib.quote(str(ticket['id'])))
645                                if not os.path.exists(dir):
646                                        mkdir_p(dir, 0755)
647                        else:
648                                dir = '/tmp'
649
650                        path, fd =  util.create_unique_file(os.path.join(dir, url_filename))
651                        text = part.get_payload(decode=1)
652                        if not text:
653                                text = '(None)'
654                        fd.write(text)
655                        fd.close()
656
657                        # get the filesize
658                        #
659                        stats = os.lstat(path)
660                        filesize = stats[stat.ST_SIZE]
661
662                        # Insert the attachment it differs for the different TRAC versions
663                        #
664                        if self.VERSION == 0.8:
665                                cursor = self.db.cursor()
666                                try:
667                                        cursor.execute('INSERT INTO attachment VALUES("%s","%s","%s",%d,%d,"%s","%s","%s")'
668                                                %('ticket', urllib.quote(str(ticket['id'])), filename + '?format=raw', filesize,
669                                                int(time.time()),'', self.author, 'e-mail') )
670
671                                # Attachment is already known
672                                #
673                                except sqlite.IntegrityError:   
674                                        #self.db.close()
675                                        return count
676
677                                self.db.commit()
678
679                        else:
680                                fd = open(path)
681                                att = attachment.Attachment(self.env, 'ticket', ticket['id'])
682                                att.insert(url_filename, fd, filesize)
683                                fd.close()
684
685                # Return how many attachments
686                #
687                return count
688
689
690def mkdir_p(dir, mode):
691        '''do a mkdir -p'''
692
693        arr = string.split(dir, '/')
694        path = ''
695        for part in arr:
696                path = '%s/%s' % (path, part)
697                try:
698                        stats = os.stat(path)
699                except OSError:
700                        os.mkdir(path, mode)
701
702
703def ReadConfig(file, name):
704        """
705        Parse the config file
706        """
707
708        if not os.path.isfile(file):
709                print 'File %s does not exist' %file
710                sys.exit(1)
711
712        config = ConfigParser.ConfigParser()
713        try:
714                config.read(file)
715        except ConfigParser.MissingSectionHeaderError,detail:
716                print detail
717                sys.exit(1)
718
719
720        # Use given project name else use defaults
721        #
722        if name:
723                if not config.has_section(name):
724                        print "Not a valid project name: %s" %name
725                        print "Valid names: %s" %config.sections()
726                        sys.exit(1)
727
728                project =  dict()
729                for option in  config.options(name):
730                        project[option] = config.get(name, option)
731
732        else:
733                project = config.defaults()
734
735        return project
736
737
738if __name__ == '__main__':
739        # Default config file
740        #
741        configfile = '@email2trac_conf@'
742        project = ''
743        component = ''
744        ENABLE_SYSLOG = 0
745               
746        try:
747                opts, args = getopt.getopt(sys.argv[1:], 'chf:p:', ['component=','help', 'file=', 'project='])
748        except getopt.error,detail:
749                print __doc__
750                print detail
751                sys.exit(1)
752       
753        project_name = None
754        for opt,value in opts:
755                if opt in [ '-h', '--help']:
756                        print __doc__
757                        sys.exit(0)
758                elif opt in ['-c', '--component']:
759                        component = value
760                elif opt in ['-f', '--file']:
761                        configfile = value
762                elif opt in ['-p', '--project']:
763                        project_name = value
764       
765        settings = ReadConfig(configfile, project_name)
766        if not settings.has_key('project'):
767                print __doc__
768                print 'No Trac project is defined in the email2trac config file.'
769                sys.exit(1)
770       
771        if component:
772                settings['component'] = component
773       
774        if settings.has_key('trac_version'):
775                version = float(settings['trac_version'])
776        else:
777                version = trac_default_version
778
779        if settings.has_key('enable_syslog'):
780                ENABLE_SYSLOG =  float(settings['enable_syslog'])
781                       
782        #debug HvB
783        #print settings
784       
785        try:
786                if version == 0.8:
787                        from trac.Environment import Environment
788                        from trac.Ticket import Ticket
789                        from trac.Notify import TicketNotifyEmail
790                        from trac.Href import Href
791                        from trac import util
792                        import sqlite
793                elif version == 0.9:
794                        from trac import attachment
795                        from trac.env import Environment
796                        from trac.ticket import Ticket
797                        from trac.web.href import Href
798                        from trac import util
799                        from trac.Notify import TicketNotifyEmail
800                elif version == 0.10:
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                        # see http://projects.edgewall.com/trac/changeset/2799
807                        from trac.ticket.notification import TicketNotifyEmail
808       
809                env = Environment(settings['project'], create=0)
810                tktparser = TicketEmailParser(env, settings, version)
811                tktparser.parse(sys.stdin)
812
813        # Catch all errors ans log to SYSLOG if we have enabled this
814        # else stdout
815        #
816        except Exception, error:
817                if ENABLE_SYSLOG:
818                        syslog.openlog('email2trac', syslog.LOG_NOWAIT)
819                        etype, evalue, etb = sys.exc_info()
820                        for e in traceback.format_exception(etype, evalue, etb):
821                                syslog.syslog(e)
822                        syslog.closelog()
823                else:
824                        traceback.print_exc()
825
826                if m:
827                        tktparser.save_email_for_debug(m, True)
828
829# EOB
Note: See TracBrowser for help on using the repository browser.