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

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

EmailtoTracScript?:

email2trac.py.in:

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