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

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

EmailtoTracScript?:

email2trac.py.in:

  • Fixed a bug for trac versions greater then 0.8, with ticket update and attachments See bugs #449 and #258
  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 19.8 KB
RevLine 
[22]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#
[80]20# For vi/emacs or other use tabstop=4 (vi: set ts=4)
21#
[22]22"""
[54]23email2trac.py -- Email tickets to Trac.
[22]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:
[74]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
[75]58        mailto_cc    : basv@sara.nl    # OPTIONAL, use this address as CC in mailto line
[74]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
[22]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 86 2006-06-21 07:20:07Z 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
90
91trac_default_version = 0.9
92
93class TicketEmailParser(object):
94        env = None
95        comment = '> '
96   
97        def __init__(self, env, parameters, version):
98                self.env = env
99
100                # Database connection
101                #
102                self.db = None
103
[72]104                # Some useful mail constants
105                #
106                self.author = None
107                self.email_addr = None
108                self.email_field = None
109
[22]110                self.VERSION = version
[68]111                if self.VERSION == 0.8:
112                        self.get_config = self.env.get_config
113                else:
[22]114                        self.get_config = self.env.config.get
115
116                if parameters.has_key('umask'):
117                        os.umask(int(parameters['umask'], 8))
118
119                if parameters.has_key('debug'):
120                        self.DEBUG = int(parameters['debug'])
121                else:
122                        self.DEBUG = 0
123
124                if parameters.has_key('mailto_link'):
125                        self.MAILTO = int(parameters['mailto_link'])
[74]126                        if parameters.has_key('mailto_cc'):
127                                self.MAILTO_CC = parameters['mailto_cc']
128                        else:
129                                self.MAILTO_CC = ''
[22]130                else:
131                        self.MAILTO = 0
132
133                if parameters.has_key('spam_level'):
134                        self.SPAM_LEVEL = int(parameters['spam_level'])
135                else:
136                        self.SPAM_LEVEL = 0
137
138                if parameters.has_key('email_comment'):
139                        self.comment = str(parameters['email_comment'])
140
141                if parameters.has_key('email_header'):
142                        self.EMAIL_HEADER = int(parameters['email_header'])
143                else:
144                        self.EMAIL_HEADER = 0
145
[42]146                if parameters.has_key('alternate_notify_template'):
147                        self.notify_template = str(parameters['alternate_notify_template'])
148                else:
149                        self.notify_template = None
[22]150
[43]151                if parameters.has_key('reply_all'):
152                        self.REPLY_ALL = int(parameters['reply_all'])
153                else:
154                        self.REPLY_ALL = 0
[42]155
[74]156                if parameters.has_key('ticket_update'):
157                        self.TICKET_UPDATE = int(parameters['ticket_update'])
158                else:
159                        self.TICKET_UPDATE = 0
[43]160
[74]161
[22]162        # X-Spam-Score: *** (3.255) BAYES_50,DNS_FROM_AHBL_RHSBL,HTML_
163        # Note if Spam_level then '*' are included
164        def spam(self, message):
165                if message.has_key('X-Spam-Score'):
166                        spam_l = string.split(message['X-Spam-Score'])
167                        number = spam_l[0].count('*')
168
169                        if number >= self.SPAM_LEVEL:
[41]170                                return 'Spam'
[22]171
[67]172                elif message.has_key('X-Virus-found'):                  # treat virus mails as spam
173                        return 'Spam'
174
[41]175                return self.get_config('ticket', 'default_component')
[22]176
177        def to_unicode(self, str):
178                """
179                Email has 7 bit ASCII code, convert it to unicode with the charset
[79]180        that is encoded in 7-bit ASCII code and encode it as utf-8 so Trac
[22]181                understands it.
182                """
183                results =  Header.decode_header(str)
184                str = None
185                for text,format in results:
186                        if format:
187                                try:
188                                        temp = unicode(text, format)
189                                except UnicodeError:
190                                        # This always works
191                                        #
192                                        temp = unicode(text, 'iso-8859-15')
193                                temp =  temp.encode('utf-8')
194                        else:
195                                temp = string.strip(text)
196
197                        if str:
198                                str = '%s %s' %(str, temp)
199                        else:
200                                str = temp
201
202                return str
203
204        def debug_attachments(self, message):
205                n = 0
206                for part in message.walk():
207                        if part.get_content_maintype() == 'multipart':      # multipart/* is just a container
208                                print 'TD: multipart container'
209                                continue
210
211                        n = n + 1
212                        print 'TD: part%d: Content-Type: %s' % (n, part.get_content_type())
213                        print 'TD: part%d: filename: %s' % (n, part.get_filename())
214
215                        if part.is_multipart():
216                                print 'TD: this part is multipart'
217                                payload = part.get_payload(decode=1)
218                                print 'TD: payload:', payload
219                        else:
220                                print 'TD: this part is not multipart'
221
222                        part_file = '/var/tmp/part%d' % n
223                        print 'TD: writing part%d (%s)' % (n,part_file)
224                        fx = open(part_file, 'wb')
225                        text = part.get_payload(decode=1)
226                        if not text:
227                                text = '(None)'
228                        fx.write(text)
229                        fx.close()
230                        try:
231                                os.chmod(part_file,S_IRWXU|S_IRWXG|S_IRWXO)
232                        except OSError:
233                                pass
234
235        def email_header_txt(self, m):
[72]236                """
237                Display To and CC addresses in description field
238                """
[22]239                str = ''
240                if m['To'] and len(m['To']) > 0 and m['To'] != 'hic@sara.nl':
241                        str = "'''To:''' %s [[BR]]" %(m['To'])
242                if m['Cc'] and len(m['Cc']) > 0:
243                        str = "%s'''Cc:''' %s [[BR]]" % (str, m['Cc'])
244
245                return str
246
[43]247        def set_owner(self, ticket):
[45]248                """
249                Select default owner for ticket component
250                """
[43]251                cursor = self.db.cursor()
252                sql = "SELECT owner FROM component WHERE name='%s'" % ticket['component']
253                cursor.execute(sql)
[61]254                try:
255                        ticket['owner'] = cursor.fetchone()[0]
256                except TypeError, detail:
257                        ticket['owner'] = "UNKNOWN"
[43]258
[72]259        def get_author_emailaddrs(self, message):
[45]260                """
[72]261                Get the default author name and email address from the message
[45]262                """
[72]263                self.author, self.email_addr  = email.Utils.parseaddr(message['from'])
[43]264
[45]265                # Look for email address in registered trac users
266                #
[68]267                if self.VERSION == 0.8:
268                        users = []
269                else:
[45]270                        users = [ u for (u, n, e) in self.env.get_known_users(self.db)
[72]271                                if e == self.email_addr ]
[43]272
[45]273                if len(users) == 1:
[72]274                        self.email_field = users[0]
[45]275                else:
[72]276                        self.email_field =  self.to_unicode(message['from'])
[45]277
[72]278        def set_reply_fields(self, ticket, message):
279                """
280                Set all the right fields for a new ticket
281                """
282                ticket['reporter'] = self.email_field
283
[45]284                # Put all CC-addresses in ticket CC field
[43]285                #
286                if self.REPLY_ALL:
[45]287                        #tos = message.get_all('to', [])
[43]288                        ccs = message.get_all('cc', [])
289
[45]290                        addrs = email.Utils.getaddresses(ccs)
[43]291
292                        # Remove reporter email address if notification is
293                        # on
294                        #
295                        if self.notification:
296                                try:
[72]297                                        addrs.remove((self.author, self.email_addr))
[43]298                                except ValueError, detail:
299                                        pass
300
[45]301                        for name,mail in addrs:
302                                        try:
303                                                ticket['cc'] = '%s,%s' %(ticket['cc'], mail)
304                                        except:
305                                                ticket['cc'] = mail
[43]306
[44]307        def save_email_for_debug(self, message):
308                msg_file = '/var/tmp/msg.txt'
309                print 'TD: saving email to %s' % msg_file
310                fx = open(msg_file, 'wb')
311                fx.write('%s' % message)
312                fx.close()
313                try:
314                        os.chmod(msg_file,S_IRWXU|S_IRWXG|S_IRWXO)
315                except OSError:
316                        pass
317
[71]318        def ticket_update(self, m):
[78]319                """
[79]320                If the current email is a reply to an existing ticket, this function
321                will append the contents of this email to that ticket, instead of
322                creating a new one.
[78]323                """
[71]324                if not m['Subject']:
325                        return False
326                else:
327                        subject  = self.to_unicode(m['Subject'])
328
329                TICKET_RE = re.compile(r"""
330                                        (?P<ticketnr>[#][0-9]+:)
331                                        """, re.VERBOSE)
332
333                result =  TICKET_RE.search(subject)
334                if not result:
335                        return False
336
[78]337                body_text = self.get_body_text(m)
338                body_text = '{{{\n%s\n}}}' %body_text
339
[72]340                # Strip '#' and ':' from ticket_id
[75]341                #
[71]342                ticket_id = result.group('ticketnr')
343                ticket_id = int(ticket_id[1:-1])
344
[77]345                # Get current time
346                #
347                when = int(time.time())
348
[76]349                if self.VERSION  == 0.8:
[78]350                        tkt = Ticket(self.db, ticket_id)
[77]351                        tkt.save_changes(self.db, self.author, body_text, when)
[76]352                else:
[78]353                        tkt = Ticket(self.env, ticket_id, self.db)
[77]354                        tkt.save_changes(self.author, body_text, when)
[86]355                        tkt['id'] = ticket_id
[76]356
[77]357                self.attachments(m, tkt)
[76]358
[72]359                if self.notification:
[77]360                        self.notify(tkt, False, when)
[72]361
[71]362                return True
363
[84]364        def new_ticket(self, msg):
[77]365                """
366                Create a new ticket
367                """
[41]368                tkt = Ticket(self.env)
[22]369                tkt['status'] = 'new'
370
371                # Some defaults
372                #
373                tkt['milestone'] = self.get_config('ticket', 'default_milestone')
374                tkt['priority'] = self.get_config('ticket', 'default_priority')
375                tkt['severity'] = self.get_config('ticket', 'default_severity')
376                tkt['version'] = self.get_config('ticket', 'default_version')
377
378                if not msg['Subject']:
379                        tkt['summary'] = '(geen subject)'
380                else:
381                        tkt['summary'] = self.to_unicode(msg['Subject'])
382
383
[41]384                if settings.has_key('component'):
[22]385                        tkt['component'] = settings['component']
386                else:
[41]387                        tkt['component'] = self.spam(msg)
[22]388
[38]389                # Must make this an option or so, discard SPAM messages or save then
390                # and delete later
391                #
392                #if self.SPAM_LEVEL and self.spam(msg):
393                #       print 'This message is a SPAM. Automatic ticket insertion refused (SPAM level > %d' % self.SPAM_LEVEL
394                #       sys.exit(1)
395
[43]396                # Set default owner for component
[22]397                #
[43]398                self.set_owner(tkt)
[72]399                self.set_reply_fields(tkt, msg)
[22]400
[45]401                # produce e-mail like header
402                #
[22]403                head = ''
404                if self.EMAIL_HEADER > 0:
405                        head = self.email_header_txt(msg)
406
407
[72]408                body_text = self.get_body_text(msg)
[77]409                tkt['description'] = ''
[45]410
[77]411                # Insert ticket in database with empty description
[45]412                #
[77]413                when = int(time.time())
[68]414                if self.VERSION == 0.8:
415                        tkt['id'] = tkt.insert(self.db)
416                else:
[45]417                        tkt['id'] = tkt.insert()
418
[77]419                n =  self.attachments(msg, tkt)
420                if n:
421                        attach_str = '\nThis message has %d attachment(s)\n' %(n)
422                else:
423                        attach_str = ''
424
425                # Always update the description else we get two emails one for the new ticket
426                # and for the attachments. It is an ugly hack but with trac you can not add
427                # attachments without an ticket id
[45]428                #
[72]429                mailto = ''
430                if self.MAILTO:
431                        mailto = self.html_mailto_link(self.to_unicode(msg['subject']), tkt['id'], body_text)
[77]432                        tkt['description'] = '%s%s%s\n{{{\n%s\n}}}\n' %(head, attach_str, mailto, body_text)
433                        comment = 'Added mailto: link + description'
434                else:
435                        tkt['description'] = '%s%s\n{{{\n%s\n}}}\n' %(head, attach_str, body_text)
436                        comment = 'Added description'
[45]437
[77]438                # Save the real description and other changes
439                #
440                if self.VERSION  == 0.8:
441                        tkt.save_changes(self.db, self.author, comment, when)
442                else:
443                        tkt.save_changes(self.author, comment, when)
444
[45]445                if self.notification:
[77]446                        self.notify(tkt, True, when)
[45]447
[77]448        def parse(self, fp):
449                m = email.message_from_file(fp)
450                if not m:
451                        return
452
453                if self.DEBUG > 1:        # save the entire e-mail message text
454                        self.save_email_for_debug(m)
455                        self.debug_attachments(m)
456
457                self.db = self.env.get_db_cnx()
458                self.get_author_emailaddrs(m)
459
460                if self.get_config('notification', 'smtp_enabled') in ['true']:
461                        self.notification = 1
462                else:
463                        self.notification = 0
464
465                # Must we update existing tickets
466                #
467                if self.TICKET_UPDATE > 0:
468                        if self.ticket_update(m):
469                                return True
470
[84]471                self.new_ticket(m)
[77]472
[72]473        def get_body_text(self, msg):
[45]474                """
[79]475                put the message text in the ticket description or in the changes field.
[45]476                message text can be plain text or html or something else
477                """
[22]478                has_description = 0
[62]479                ubody_text = '\n{{{\nNo plain text message\n}}}\n'
[22]480                for part in msg.walk():
[45]481
482                        # 'multipart/*' is a container for multipart messages
483                        #
484                        if part.get_content_maintype() == 'multipart':
[22]485                                continue
486
487                        if part.get_content_type() == 'text/plain':
[45]488                                # Try to decode, if fails then do not decode
489                                #
490                                body_text = part.get_payload(decode=1)         
491                                if not body_text:                       
492                                        body_text = part.get_payload(decode=0) 
[22]493
[45]494                                # Get contents charset (iso-8859-15 if not defined in mail headers)
495                                # UTF-8 encode body_text
496                                #
497                                charset = msg.get_content_charset('iso-8859-15')
498                                ubody_text = unicode(body_text, charset).encode('utf-8')
[22]499
500                        elif part.get_content_type() == 'text/html':
[72]501                                ubody_text = '\n\n(see attachment for HTML mail message)\n'
[22]502
503                        else:
[72]504                                ubody_text = '\n\n(see attachment for message)\n'
[22]505
506                        has_description = 1
507                        break           # we have the description, so break
508
509                if not has_description:
[72]510                        ubody_text = '\n\n(see attachment for message)\n'
[22]511
[72]512                return ubody_text
[22]513
[77]514        def notify(self, tkt , new=True, modtime=0):
[79]515                """
516                A wrapper for the TRAC notify function. So we can use templates
517                """
[41]518                try:
519                        # create false {abs_}href properties, to trick Notify()
520                        #
521                        self.env.abs_href = Href(self.get_config('project', 'url'))
522                        self.env.href = Href(self.get_config('project', 'url'))
[22]523
[41]524                        tn = TicketNotifyEmail(self.env)
[42]525                        if self.notify_template:
526                                tn.template_name = self.notify_template;
527
[77]528                        tn.notify(tkt, new, modtime)
[41]529
530                except Exception, e:
[79]531                        print 'TD: Failure sending notification on creation of ticket #%s: %s' %(tkt['id'], e)
[41]532
[22]533        def mail_line(self, str):
534                return '%s %s' % (self.comment, str)
535
536
[72]537        def html_mailto_link(self, subject, id, body):
538                if not self.author:
539                        author = self.mail_addr
[22]540                else:   
[72]541                        author = self.to_unicode(self.author)
[22]542
543                # Must find a fix
544                #
545                #arr = string.split(body, '\n')
546                #arr = map(self.mail_line, arr)
547                #body = string.join(arr, '\n')
548                #body = '%s wrote:\n%s' %(author, body)
549
550                # Temporary fix
[74]551                str = 'mailto:%s?Subject=%s&Cc=%s' %(
552                       urllib.quote(self.email_addr),
553                           urllib.quote('Re: #%s: %s' %(id, subject)),
554                           urllib.quote(self.MAILTO_CC)
555                           )
556
[22]557                str = '\n{{{\n#!html\n<a href="%s">Reply to: %s</a>\n}}}\n' %(str, author)
558
559                return str
560
[72]561        def attachments(self, message, ticket):
[79]562                '''
563                save any attachments as files in the ticket's directory
564                '''
[22]565                count = 0
[77]566                first = 0
567                number = 0
[22]568                for part in message.walk():
569                        if part.get_content_maintype() == 'multipart':          # multipart/* is just a container
570                                continue
571
572                        if not first:                                                                           # first content is the message
573                                first = 1
574                                if part.get_content_type() == 'text/plain':             # if first is text, is was already put in the description
575                                        continue
576
577                        filename = part.get_filename()
[77]578                        count = count + 1
[22]579                        if not filename:
[77]580                                number = number + 1
581                                filename = 'part%04d' % number
[22]582
[72]583                                ext = mimetypes.guess_extension(part.get_content_type())
[22]584                                if not ext:
585                                        ext = '.bin'
586
587                                filename = '%s%s' % (filename, ext)
588                        else:
589                                filename = self.to_unicode(filename)
590
[48]591                        # From the trac code
592                        #
593                        filename = filename.replace('\\', '/').replace(':', '/')
594                        filename = os.path.basename(filename)
[22]595
[48]596                        # We try to normalize the filename to utf-8 NFC if we can.
597                        # Files uploaded from OS X might be in NFD.
598                        #
599                        if sys.version_info[0] > 2 or (sys.version_info[0] == 2 and sys.version_info[1] >= 3):
600                                filename = unicodedata.normalize('NFC', unicode(filename, 'utf-8')).encode('utf-8') 
601
[22]602                        url_filename = urllib.quote(filename)
[68]603                        if self.VERSION == 0.8:
[22]604                                dir = os.path.join(self.env.get_attachments_dir(), 'ticket',
605                                                        urllib.quote(str(ticket['id'])))
606                                if not os.path.exists(dir):
607                                        mkdir_p(dir, 0755)
[68]608                        else:
609                                dir = '/tmp'
[22]610
[48]611                        path, fd =  util.create_unique_file(os.path.join(dir, url_filename))
[22]612                        text = part.get_payload(decode=1)
613                        if not text:
614                                text = '(None)'
[48]615                        fd.write(text)
616                        fd.close()
[22]617
618                        # get the filesize
619                        #
[48]620                        stats = os.lstat(path)
[22]621                        filesize = stats[stat.ST_SIZE]
622
623                        # Insert the attachment it differs for the different TRAC versions
[73]624                        #
[68]625                        if self.VERSION == 0.8:
[22]626                                cursor = self.db.cursor()
[73]627                                try:
628                                        cursor.execute('INSERT INTO attachment VALUES("%s","%s","%s",%d,%d,"%s","%s","%s")'
629                                                %('ticket', urllib.quote(str(ticket['id'])), filename + '?format=raw', filesize,
630                                                int(time.time()),'', self.author, 'e-mail') )
631
632                                # Attachment is already known
633                                #
634                                except sqlite.IntegrityError:   
[84]635                                        #self.db.close()
636                                        return count
[73]637
[22]638                                self.db.commit()
[73]639
[68]640                        else:
641                                fd = open(path)
642                                att = attachment.Attachment(self.env, 'ticket', ticket['id'])
643                                att.insert(url_filename, fd, filesize)
644                                fd.close()
[22]645
[77]646                # Return how many attachments
647                #
648                return count
[22]649
[77]650
[22]651def mkdir_p(dir, mode):
652        '''do a mkdir -p'''
653
654        arr = string.split(dir, '/')
655        path = ''
656        for part in arr:
657                path = '%s/%s' % (path, part)
658                try:
659                        stats = os.stat(path)
660                except OSError:
661                        os.mkdir(path, mode)
662
663
664def ReadConfig(file, name):
665        """
666        Parse the config file
667        """
668
669        if not os.path.isfile(file):
[79]670                print 'File %s does not exist' %file
[22]671                sys.exit(1)
672
673        config = ConfigParser.ConfigParser()
674        try:
675                config.read(file)
676        except ConfigParser.MissingSectionHeaderError,detail:
677                print detail
678                sys.exit(1)
679
680
681        # Use given project name else use defaults
682        #
683        if name:
684                if not config.has_section(name):
[79]685                        print "Not a valid project name: %s" %name
[22]686                        print "Valid names: %s" %config.sections()
687                        sys.exit(1)
688
689                project =  dict()
690                for option in  config.options(name):
691                        project[option] = config.get(name, option)
692
693        else:
694                project = config.defaults()
695
696        return project
697
698if __name__ == '__main__':
699        # Default config file
700        #
[24]701        configfile = '@email2trac_conf@'
[22]702        project = ''
703        component = ''
704       
705        try:
706                opts, args = getopt.getopt(sys.argv[1:], 'chf:p:', ['component=','help', 'file=', 'project='])
707        except getopt.error,detail:
708                print __doc__
709                print detail
710                sys.exit(1)
711
712        project_name = None
713        for opt,value in opts:
714                if opt in [ '-h', '--help']:
715                        print __doc__
716                        sys.exit(0)
717                elif opt in ['-c', '--component']:
718                        component = value
719                elif opt in ['-f', '--file']:
720                        configfile = value
721                elif opt in ['-p', '--project']:
722                        project_name = value
723
724        settings = ReadConfig(configfile, project_name)
725        if not settings.has_key('project'):
726                print __doc__
[79]727                print 'No Trac project is defined in the email2trac config file.'
[22]728                sys.exit(1)
729
730        if component:
731                settings['component'] = component
732
733        if settings.has_key('trac_version'):
734                version = float(settings['trac_version'])
735        else:
736                version = trac_default_version
737
738        #debug HvB
739        #print settings
740
[68]741        if version == 0.8:
742                from trac.Environment import Environment
743                from trac.Ticket import Ticket
744                from trac.Notify import TicketNotifyEmail
745                from trac.Href import Href
746                from trac import util
[75]747                import sqlite
[68]748        elif version == 0.9:
[41]749                from trac import attachment
750                from trac.env import Environment
751                from trac.ticket import Ticket
752                from trac.web.href import Href
[48]753                from trac import util
[41]754                from trac.Notify import TicketNotifyEmail
[68]755        elif version == 0.10:
756                from trac import attachment
757                from trac.env import Environment
758                from trac.ticket import Ticket
759                from trac.web.href import Href
[48]760                from trac import util
[68]761                # see http://projects.edgewall.com/trac/changeset/2799
762                from trac.ticket.notification import TicketNotifyEmail
[41]763
764        env = Environment(settings['project'], create=0)
[22]765        tktparser = TicketEmailParser(env, settings, version)
766        tktparser.parse(sys.stdin)
767
768# EOB
Note: See TracBrowser for help on using the repository browser.