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

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

EmailtoTracScript?:

email2trac.py.in:

  • Fixed an error in saving attachments for new tickets
  • Added some debug code for offending messages
  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 20.8 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 98 2006-07-13 08:28:45Z 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                                       
201                                temp =  temp.encode('utf-8')
202                        else:
203                                temp = string.strip(text)
204                                temp = unicode(text, 'iso-8859-15')
205
206                        if str:
207                                str = u'%s %s' %(str, temp)
208                        else:
209                                str = u'%s' %temp
210
211                str = str.encode('utf-8')
212                return str
213
214        def debug_attachments(self, message):
215                n = 0
216                for part in message.walk():
217                        if part.get_content_maintype() == 'multipart':      # multipart/* is just a container
218                                print 'TD: multipart container'
219                                continue
220
221                        n = n + 1
222                        print 'TD: part%d: Content-Type: %s' % (n, part.get_content_type())
223                        print 'TD: part%d: filename: %s' % (n, part.get_filename())
224
225                        if part.is_multipart():
226                                print 'TD: this part is multipart'
227                                payload = part.get_payload(decode=1)
228                                print 'TD: payload:', payload
229                        else:
230                                print 'TD: this part is not multipart'
231
232                        part_file = '/var/tmp/part%d' % n
233                        print 'TD: writing part%d (%s)' % (n,part_file)
234                        fx = open(part_file, 'wb')
235                        text = part.get_payload(decode=1)
236                        if not text:
237                                text = '(None)'
238                        fx.write(text)
239                        fx.close()
240                        try:
241                                os.chmod(part_file,S_IRWXU|S_IRWXG|S_IRWXO)
242                        except OSError:
243                                pass
244
245        def email_header_txt(self, m):
246                """
247                Display To and CC addresses in description field
248                """
249                str = ''
250                if m['To'] and len(m['To']) > 0 and m['To'] != 'hic@sara.nl':
251                        str = "'''To:''' %s [[BR]]" %(m['To'])
252                if m['Cc'] and len(m['Cc']) > 0:
253                        str = "%s'''Cc:''' %s [[BR]]" % (str, m['Cc'])
254
255                return  self.to_unicode(str)
256
257        def set_owner(self, ticket):
258                """
259                Select default owner for ticket component
260                """
261                cursor = self.db.cursor()
262                sql = "SELECT owner FROM component WHERE name='%s'" % ticket['component']
263                cursor.execute(sql)
264                try:
265                        ticket['owner'] = cursor.fetchone()[0]
266                except TypeError, detail:
267                        ticket['owner'] = "UNKNOWN"
268
269        def get_author_emailaddrs(self, message):
270                """
271                Get the default author name and email address from the message
272                """
273                self.author, self.email_addr  = email.Utils.parseaddr(message['from'])
274
275                # Look for email address in registered trac users
276                #
277                if self.VERSION == 0.8:
278                        users = []
279                else:
280                        users = [ u for (u, n, e) in self.env.get_known_users(self.db)
281                                if e == self.email_addr ]
282
283                if len(users) == 1:
284                        self.email_field = users[0]
285                else:
286                        self.email_field =  self.to_unicode(message['from'])
287
288        def set_reply_fields(self, ticket, message):
289                """
290                Set all the right fields for a new ticket
291                """
292                ticket['reporter'] = self.email_field
293
294                # Put all CC-addresses in ticket CC field
295                #
296                if self.REPLY_ALL:
297                        #tos = message.get_all('to', [])
298                        ccs = message.get_all('cc', [])
299
300                        addrs = email.Utils.getaddresses(ccs)
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                        ticket['cc'] = self.to_unicode(mail_list)
318
319        def save_email_for_debug(self, message, tempfile=False):
320                if tempfile:
321                        import tempfile
322                        msg_file = tempfile.mktemp('.email2trac')
323                else:
324                        msg_file = '/var/tmp/msg.txt'
325                print 'TD: saving email to %s' % msg_file
326                fx = open(msg_file, 'wb')
327                fx.write('%s' % message)
328                fx.close()
329                try:
330                        os.chmod(msg_file,S_IRWXU|S_IRWXG|S_IRWXO)
331                except OSError:
332                        pass
333
334        def ticket_update(self, m):
335                """
336                If the current email is a reply to an existing ticket, this function
337                will append the contents of this email to that ticket, instead of
338                creating a new one.
339                """
340                if not m['Subject']:
341                        return False
342                else:
343                        subject  = self.to_unicode(m['Subject'])
344
345                TICKET_RE = re.compile(r"""
346                                        (?P<ticketnr>[#][0-9]+:)
347                                        """, re.VERBOSE)
348
349                result =  TICKET_RE.search(subject)
350                if not result:
351                        return False
352
353                body_text = self.get_body_text(m)
354                body_text = '{{{\n%s\n}}}' %body_text
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                body_text = ('\r\n'.join(body_text.splitlines()))
426
427                # A patch so that the web-interface will not update the description
428                # field of a ticket
429                #
430                tkt['description'] = u'email2trac:%s\r\n{{{\r\n%s\r\n}}}' \
431                        %(head, body_text)
432
433                when = int(time.time())
434                if self.VERSION == 0.8:
435                        ticket_id = tkt.insert(self.db)
436                else:
437                        ticket_id = tkt.insert()
438                        tkt['id'] = ticket_id
439
440                changed = False
441                comment = ''
442
443                # Rewrite the description if we have mailto enabled
444                #
445                if self.MAILTO:
446                        mailto = self.html_mailto_link(self.to_unicode(msg['subject']), ticket_id, body_text)
447                        tkt['description'] = u'email2trac:%s%s\r\n{{{\r\n%s\r\n}}}' %(head, mailto, body_text)
448                        comment = '\nadded mailto line\n'
449                        changed = True
450
451                n =  self.attachments(msg, tkt)
452                if n:
453                        comment = '%s\nThis message has %d attachment(s)\n' %(comment, n)
454                        changed = True
455
456                if changed:
457                        if self.VERSION  == 0.8:
458                                tkt.save_changes(self.db, self.author, comment)
459                        else:
460                                tkt.save_changes(self.author, comment)
461
462                #print tkt.get_changelog(self.db, when)
463
464                if self.notification:
465                        self.notify(tkt, True)
466                        #self.notify(tkt, False)
467
468        def parse(self, fp):
469                global m
470
471                m = email.message_from_file(fp)
472                if not m:
473                        return
474
475                if self.DEBUG > 1:        # save the entire e-mail message text
476                        self.save_email_for_debug(m)
477                        self.debug_attachments(m)
478
479                self.db = self.env.get_db_cnx()
480                self.get_author_emailaddrs(m)
481
482                if self.get_config('notification', 'smtp_enabled') in ['true']:
483                        self.notification = 1
484                else:
485                        self.notification = 0
486
487                # Must we update existing tickets
488                #
489                if self.TICKET_UPDATE > 0:
490                        if self.ticket_update(m):
491                                return True
492
493                self.new_ticket(m)
494
495        def get_body_text(self, msg):
496                """
497                put the message text in the ticket description or in the changes field.
498                message text can be plain text or html or something else
499                """
500                has_description = 0
501                ubody_text = u'\r\n{{{\nNo plain text message\n}}}\r\n'
502                for part in msg.walk():
503
504                        # 'multipart/*' is a container for multipart messages
505                        #
506                        if part.get_content_maintype() == 'multipart':
507                                continue
508
509                        if part.get_content_type() == 'text/plain':
510                                # Try to decode, if fails then do not decode
511                                #
512                                body_text = part.get_payload(decode=1)
513                                if not body_text:                       
514                                        body_text = part.get_payload(decode=0)
515
516                                # Get contents charset (iso-8859-15 if not defined in mail headers)
517                                # UTF-8 encode body_text
518                                #
519                                charset = part.get_content_charset('iso-8859-15')
520                                try:
521                                        ubody_text = unicode(body_text, charset)
522                                except (UnicodeError,LookupError):
523                                        ubody_text = unicode(body_text, 'iso-8859-15')
524
525                        elif part.get_content_type() == 'text/html':
526                                ubody_text = u'\r\n(see attachment for HTML mail message)\r\n'
527
528                        else:
529                                ubody_text = u'\r\n(see attachment for message)\r\n'
530
531                        has_description = 1
532                        break           # we have the description, so break
533
534                if not has_description:
535                        ubody_text = u'\r\n(see attachment for message)\r\n'
536
537                return ubody_text.encode('utf-8')
538
539        def notify(self, tkt , new=True, modtime=0):
540                """
541                A wrapper for the TRAC notify function. So we can use templates
542                """
543                try:
544                        # create false {abs_}href properties, to trick Notify()
545                        #
546                        self.env.abs_href = Href(self.get_config('project', 'url'))
547                        self.env.href = Href(self.get_config('project', 'url'))
548
549                        tn = TicketNotifyEmail(self.env)
550                        if self.notify_template:
551                                tn.template_name = self.notify_template;
552
553                        tn.notify(tkt, new, modtime)
554
555                except Exception, e:
556                        print 'TD: Failure sending notification on creation of ticket #%s: %s' %(tkt['id'], e)
557
558        def mail_line(self, str):
559                return '%s %s' % (self.comment, str)
560
561
562        def html_mailto_link(self, subject, id, body):
563                if not self.author:
564                        author = self.email_addr
565                else:   
566                        author = self.to_unicode(self.author)
567
568                # Must find a fix
569                #
570                #arr = string.split(body, '\n')
571                #arr = map(self.mail_line, arr)
572                #body = string.join(arr, '\n')
573                #body = '%s wrote:\n%s' %(author, body)
574
575                # Temporary fix
576                str = 'mailto:%s?Subject=%s&Cc=%s' %(
577                       urllib.quote(self.email_addr),
578                           urllib.quote('Re: #%s: %s' %(id, subject)),
579                           urllib.quote(self.MAILTO_CC)
580                           )
581
582                str = '\r\n{{{\r\n#!html\r\n<a href="%s">Reply to: %s</a>\r\n}}}\r\n' %(str, author)
583
584                return str
585
586        def attachments(self, message, ticket):
587                '''
588                save any attachments as files in the ticket's directory
589                '''
590                count = 0
591                first = 0
592                number = 0
593                for part in message.walk():
594                        if part.get_content_maintype() == 'multipart':          # multipart/* is just a container
595                                continue
596
597                        if not first:                                                                           # first content is the message
598                                first = 1
599                                if part.get_content_type() == 'text/plain':             # if first is text, is was already put in the description
600                                        continue
601
602                        filename = part.get_filename()
603                        count = count + 1
604                        if not filename:
605                                number = number + 1
606                                filename = 'part%04d' % number
607
608                                ext = mimetypes.guess_extension(part.get_content_type())
609                                if not ext:
610                                        ext = '.bin'
611
612                                filename = '%s%s' % (filename, ext)
613                        else:
614                                filename = self.to_unicode(filename)
615
616                        # From the trac code
617                        #
618                        filename = filename.replace('\\', '/').replace(':', '/')
619                        filename = os.path.basename(filename)
620
621                        # We try to normalize the filename to utf-8 NFC if we can.
622                        # Files uploaded from OS X might be in NFD.
623                        # Check python version and then try it
624                        #
625                        if sys.version_info[0] > 2 or (sys.version_info[0] == 2 and sys.version_info[1] >= 3):
626                                try:
627                                        filename = unicodedata.normalize('NFC', unicode(filename, 'utf-8')).encode('utf-8') 
628                                except TypeError:
629                                        pass
630
631                        url_filename = urllib.quote(filename)
632                        if self.VERSION == 0.8:
633                                dir = os.path.join(self.env.get_attachments_dir(), 'ticket',
634                                                        urllib.quote(str(ticket['id'])))
635                                if not os.path.exists(dir):
636                                        mkdir_p(dir, 0755)
637                        else:
638                                dir = '/tmp'
639
640                        path, fd =  util.create_unique_file(os.path.join(dir, url_filename))
641                        text = part.get_payload(decode=1)
642                        if not text:
643                                text = '(None)'
644                        fd.write(text)
645                        fd.close()
646
647                        # get the filesize
648                        #
649                        stats = os.lstat(path)
650                        filesize = stats[stat.ST_SIZE]
651
652                        # Insert the attachment it differs for the different TRAC versions
653                        #
654                        if self.VERSION == 0.8:
655                                cursor = self.db.cursor()
656                                try:
657                                        cursor.execute('INSERT INTO attachment VALUES("%s","%s","%s",%d,%d,"%s","%s","%s")'
658                                                %('ticket', urllib.quote(str(ticket['id'])), filename + '?format=raw', filesize,
659                                                int(time.time()),'', self.author, 'e-mail') )
660
661                                # Attachment is already known
662                                #
663                                except sqlite.IntegrityError:   
664                                        #self.db.close()
665                                        return count
666
667                                self.db.commit()
668
669                        else:
670                                fd = open(path)
671                                att = attachment.Attachment(self.env, 'ticket', ticket['id'])
672                                att.insert(url_filename, fd, filesize)
673                                fd.close()
674
675                # Return how many attachments
676                #
677                return count
678
679
680def mkdir_p(dir, mode):
681        '''do a mkdir -p'''
682
683        arr = string.split(dir, '/')
684        path = ''
685        for part in arr:
686                path = '%s/%s' % (path, part)
687                try:
688                        stats = os.stat(path)
689                except OSError:
690                        os.mkdir(path, mode)
691
692
693def ReadConfig(file, name):
694        """
695        Parse the config file
696        """
697
698        if not os.path.isfile(file):
699                print 'File %s does not exist' %file
700                sys.exit(1)
701
702        config = ConfigParser.ConfigParser()
703        try:
704                config.read(file)
705        except ConfigParser.MissingSectionHeaderError,detail:
706                print detail
707                sys.exit(1)
708
709
710        # Use given project name else use defaults
711        #
712        if name:
713                if not config.has_section(name):
714                        print "Not a valid project name: %s" %name
715                        print "Valid names: %s" %config.sections()
716                        sys.exit(1)
717
718                project =  dict()
719                for option in  config.options(name):
720                        project[option] = config.get(name, option)
721
722        else:
723                project = config.defaults()
724
725        return project
726
727
728if __name__ == '__main__':
729        # Default config file
730        #
731        configfile = '@email2trac_conf@'
732        project = ''
733        component = ''
734        ENABLE_SYSLOG = 0
735               
736        try:
737                opts, args = getopt.getopt(sys.argv[1:], 'chf:p:', ['component=','help', 'file=', 'project='])
738        except getopt.error,detail:
739                print __doc__
740                print detail
741                sys.exit(1)
742       
743        project_name = None
744        for opt,value in opts:
745                if opt in [ '-h', '--help']:
746                        print __doc__
747                        sys.exit(0)
748                elif opt in ['-c', '--component']:
749                        component = value
750                elif opt in ['-f', '--file']:
751                        configfile = value
752                elif opt in ['-p', '--project']:
753                        project_name = value
754       
755        settings = ReadConfig(configfile, project_name)
756        if not settings.has_key('project'):
757                print __doc__
758                print 'No Trac project is defined in the email2trac config file.'
759                sys.exit(1)
760       
761        if component:
762                settings['component'] = component
763       
764        if settings.has_key('trac_version'):
765                version = float(settings['trac_version'])
766        else:
767                version = trac_default_version
768
769        if settings.has_key('enable_syslog'):
770                ENABLE_SYSLOG =  float(settings['enable_syslog'])
771                       
772        #debug HvB
773        #print settings
774       
775        try:
776                if version == 0.8:
777                        from trac.Environment import Environment
778                        from trac.Ticket import Ticket
779                        from trac.Notify import TicketNotifyEmail
780                        from trac.Href import Href
781                        from trac import util
782                        import sqlite
783                elif version == 0.9:
784                        from trac import attachment
785                        from trac.env import Environment
786                        from trac.ticket import Ticket
787                        from trac.web.href import Href
788                        from trac import util
789                        from trac.Notify import TicketNotifyEmail
790                elif version == 0.10:
791                        from trac import attachment
792                        from trac.env import Environment
793                        from trac.ticket import Ticket
794                        from trac.web.href import Href
795                        from trac import util
796                        # see http://projects.edgewall.com/trac/changeset/2799
797                        from trac.ticket.notification import TicketNotifyEmail
798       
799                env = Environment(settings['project'], create=0)
800                tktparser = TicketEmailParser(env, settings, version)
801                tktparser.parse(sys.stdin)
802
803        # Catch all errors ans log to SYSLOG if we have enabled this
804        # else stdout
805        #
806        except Exception, error:
807                if ENABLE_SYSLOG:
808                        syslog.openlog('email2trac', syslog.LOG_NOWAIT)
809                        etype, evalue, etb = sys.exc_info()
810                        for e in traceback.format_exception(etype, evalue, etb):
811                                syslog.syslog(e)
812                        syslog.closelog()
813                else:
814                        traceback.print_exc()
815
816                if m:
817                        tktparser.save_email_for_debug(m, True)
818
819# EOB
Note: See TracBrowser for help on using the repository browser.