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

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

EmailtoTracScript?:

Updating Changelog's

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