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
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
[87]56        umask        : 022             # OPTIONAL, if set then use this umask for creation of the attachments
[74]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 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
[96]90import syslog
91import traceback
[22]92
[91]93
[96]94# Some global variables
95#
[22]96trac_default_version = 0.9
[96]97m = None
[22]98
[96]99
[22]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
[72]111                # Some useful mail constants
112                #
113                self.author = None
114                self.email_addr = None
115                self.email_field = None
116
[22]117                self.VERSION = version
[68]118                if self.VERSION == 0.8:
119                        self.get_config = self.env.get_config
120                else:
[22]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'])
[74]133                        if parameters.has_key('mailto_cc'):
134                                self.MAILTO_CC = parameters['mailto_cc']
135                        else:
136                                self.MAILTO_CC = ''
[22]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
[42]153                if parameters.has_key('alternate_notify_template'):
154                        self.notify_template = str(parameters['alternate_notify_template'])
155                else:
156                        self.notify_template = None
[22]157
[43]158                if parameters.has_key('reply_all'):
159                        self.REPLY_ALL = int(parameters['reply_all'])
160                else:
161                        self.REPLY_ALL = 0
[42]162
[74]163                if parameters.has_key('ticket_update'):
164                        self.TICKET_UPDATE = int(parameters['ticket_update'])
165                else:
166                        self.TICKET_UPDATE = 0
[43]167
[74]168
[22]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:
[41]177                                return 'Spam'
[22]178
[67]179                elif message.has_key('X-Virus-found'):                  # treat virus mails as spam
180                        return 'Spam'
181
[41]182                return self.get_config('ticket', 'default_component')
[22]183
184        def to_unicode(self, str):
185                """
186                Email has 7 bit ASCII code, convert it to unicode with the charset
[79]187        that is encoded in 7-bit ASCII code and encode it as utf-8 so Trac
[22]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)
[87]196                                except (UnicodeError,LookupError):
[22]197                                        # This always works
198                                        #
199                                        temp = unicode(text, 'iso-8859-15')
[87]200                                       
[22]201                                temp =  temp.encode('utf-8')
202                        else:
203                                temp = string.strip(text)
[92]204                                temp = unicode(text, 'iso-8859-15')
[22]205
206                        if str:
[96]207                                str = u'%s %s' %(str, temp)
[22]208                        else:
[96]209                                str = u'%s' %temp
[22]210
[96]211                str = str.encode('utf-8')
[22]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):
[72]246                """
247                Display To and CC addresses in description field
248                """
[22]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
[91]255                return  self.to_unicode(str)
[22]256
[43]257        def set_owner(self, ticket):
[45]258                """
259                Select default owner for ticket component
260                """
[43]261                cursor = self.db.cursor()
262                sql = "SELECT owner FROM component WHERE name='%s'" % ticket['component']
263                cursor.execute(sql)
[61]264                try:
265                        ticket['owner'] = cursor.fetchone()[0]
266                except TypeError, detail:
267                        ticket['owner'] = "UNKNOWN"
[43]268
[72]269        def get_author_emailaddrs(self, message):
[45]270                """
[72]271                Get the default author name and email address from the message
[45]272                """
[72]273                self.author, self.email_addr  = email.Utils.parseaddr(message['from'])
[43]274
[45]275                # Look for email address in registered trac users
276                #
[68]277                if self.VERSION == 0.8:
278                        users = []
279                else:
[45]280                        users = [ u for (u, n, e) in self.env.get_known_users(self.db)
[72]281                                if e == self.email_addr ]
[43]282
[45]283                if len(users) == 1:
[72]284                        self.email_field = users[0]
[45]285                else:
[72]286                        self.email_field =  self.to_unicode(message['from'])
[45]287
[72]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
[45]294                # Put all CC-addresses in ticket CC field
[43]295                #
296                if self.REPLY_ALL:
[45]297                        #tos = message.get_all('to', [])
[43]298                        ccs = message.get_all('cc', [])
299
[45]300                        addrs = email.Utils.getaddresses(ccs)
[43]301
302                        # Remove reporter email address if notification is
303                        # on
304                        #
305                        if self.notification:
306                                try:
[72]307                                        addrs.remove((self.author, self.email_addr))
[43]308                                except ValueError, detail:
309                                        pass
310
[45]311                        for name,mail in addrs:
312                                        try:
[96]313                                                mail_list = '%s,%s' %(mail_list, mail)
[45]314                                        except:
[96]315                                                mail_list = mail
[43]316
[96]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'
[44]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
[71]334        def ticket_update(self, m):
[78]335                """
[79]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.
[78]339                """
[71]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
[78]353                body_text = self.get_body_text(m)
354                body_text = '{{{\n%s\n}}}' %body_text
355
[72]356                # Strip '#' and ':' from ticket_id
[75]357                #
[71]358                ticket_id = result.group('ticketnr')
359                ticket_id = int(ticket_id[1:-1])
360
[77]361                # Get current time
362                #
363                when = int(time.time())
364
[76]365                if self.VERSION  == 0.8:
[78]366                        tkt = Ticket(self.db, ticket_id)
[77]367                        tkt.save_changes(self.db, self.author, body_text, when)
[76]368                else:
[78]369                        tkt = Ticket(self.env, ticket_id, self.db)
[77]370                        tkt.save_changes(self.author, body_text, when)
[86]371                        tkt['id'] = ticket_id
[76]372
[77]373                self.attachments(m, tkt)
[76]374
[72]375                if self.notification:
[77]376                        self.notify(tkt, False, when)
[72]377
[71]378                return True
379
[84]380        def new_ticket(self, msg):
[77]381                """
382                Create a new ticket
383                """
[41]384                tkt = Ticket(self.env)
[22]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']:
[96]395                        tkt['summary'] = u'(geen subject)'
[22]396                else:
397                        tkt['summary'] = self.to_unicode(msg['Subject'])
398
399
[41]400                if settings.has_key('component'):
[22]401                        tkt['component'] = settings['component']
402                else:
[41]403                        tkt['component'] = self.spam(msg)
[22]404
[38]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
[43]412                # Set default owner for component
[22]413                #
[43]414                self.set_owner(tkt)
[72]415                self.set_reply_fields(tkt, msg)
[22]416
[45]417                # produce e-mail like header
418                #
[22]419                head = ''
420                if self.EMAIL_HEADER > 0:
421                        head = self.email_header_txt(msg)
[92]422                       
[22]423
[72]424                body_text = self.get_body_text(msg)
[90]425                body_text = ('\r\n'.join(body_text.splitlines()))
[45]426
[90]427                # A patch so that the web-interface will not update the description
428                # field of a ticket
[45]429                #
[96]430                tkt['description'] = u'email2trac:%s\r\n{{{\r\n%s\r\n}}}' \
[90]431                        %(head, body_text)
432
[77]433                when = int(time.time())
[68]434                if self.VERSION == 0.8:
[90]435                        ticket_id = tkt.insert(self.db)
[68]436                else:
[90]437                        ticket_id = tkt.insert()
[98]438                        tkt['id'] = ticket_id
[45]439
[90]440                changed = False
441                comment = ''
[77]442
[90]443                # Rewrite the description if we have mailto enabled
[45]444                #
[72]445                if self.MAILTO:
[90]446                        mailto = self.html_mailto_link(self.to_unicode(msg['subject']), ticket_id, body_text)
[96]447                        tkt['description'] = u'email2trac:%s%s\r\n{{{\r\n%s\r\n}}}' %(head, mailto, body_text)
[90]448                        comment = '\nadded mailto line\n'
449                        changed = True
[45]450
[90]451                n =  self.attachments(msg, tkt)
452                if n:
453                        comment = '%s\nThis message has %d attachment(s)\n' %(comment, n)
454                        changed = True
[77]455
[90]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
[45]464                if self.notification:
[90]465                        self.notify(tkt, True)
466                        #self.notify(tkt, False)
[45]467
[77]468        def parse(self, fp):
[96]469                global m
470
[77]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
[84]493                self.new_ticket(m)
[77]494
[72]495        def get_body_text(self, msg):
[45]496                """
[79]497                put the message text in the ticket description or in the changes field.
[45]498                message text can be plain text or html or something else
499                """
[22]500                has_description = 0
[96]501                ubody_text = u'\r\n{{{\nNo plain text message\n}}}\r\n'
[22]502                for part in msg.walk():
[45]503
504                        # 'multipart/*' is a container for multipart messages
505                        #
506                        if part.get_content_maintype() == 'multipart':
[22]507                                continue
508
509                        if part.get_content_type() == 'text/plain':
[45]510                                # Try to decode, if fails then do not decode
511                                #
[90]512                                body_text = part.get_payload(decode=1)
[45]513                                if not body_text:                       
[90]514                                        body_text = part.get_payload(decode=0)
[22]515
[45]516                                # Get contents charset (iso-8859-15 if not defined in mail headers)
517                                # UTF-8 encode body_text
518                                #
[87]519                                charset = part.get_content_charset('iso-8859-15')
[89]520                                try:
[96]521                                        ubody_text = unicode(body_text, charset)
[89]522                                except (UnicodeError,LookupError):
[96]523                                        ubody_text = unicode(body_text, 'iso-8859-15')
[89]524
[22]525                        elif part.get_content_type() == 'text/html':
[96]526                                ubody_text = u'\r\n(see attachment for HTML mail message)\r\n'
[22]527
528                        else:
[96]529                                ubody_text = u'\r\n(see attachment for message)\r\n'
[22]530
531                        has_description = 1
532                        break           # we have the description, so break
533
534                if not has_description:
[96]535                        ubody_text = u'\r\n(see attachment for message)\r\n'
[22]536
[96]537                return ubody_text.encode('utf-8')
[22]538
[77]539        def notify(self, tkt , new=True, modtime=0):
[79]540                """
541                A wrapper for the TRAC notify function. So we can use templates
542                """
[41]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'))
[22]548
[41]549                        tn = TicketNotifyEmail(self.env)
[42]550                        if self.notify_template:
551                                tn.template_name = self.notify_template;
552
[77]553                        tn.notify(tkt, new, modtime)
[41]554
555                except Exception, e:
[79]556                        print 'TD: Failure sending notification on creation of ticket #%s: %s' %(tkt['id'], e)
[41]557
[22]558        def mail_line(self, str):
559                return '%s %s' % (self.comment, str)
560
561
[72]562        def html_mailto_link(self, subject, id, body):
563                if not self.author:
[88]564                        author = self.email_addr
[22]565                else:   
[72]566                        author = self.to_unicode(self.author)
[22]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
[74]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
[90]582                str = '\r\n{{{\r\n#!html\r\n<a href="%s">Reply to: %s</a>\r\n}}}\r\n' %(str, author)
[22]583
584                return str
585
[72]586        def attachments(self, message, ticket):
[79]587                '''
588                save any attachments as files in the ticket's directory
589                '''
[22]590                count = 0
[77]591                first = 0
592                number = 0
[22]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()
[77]603                        count = count + 1
[22]604                        if not filename:
[77]605                                number = number + 1
606                                filename = 'part%04d' % number
[22]607
[72]608                                ext = mimetypes.guess_extension(part.get_content_type())
[22]609                                if not ext:
610                                        ext = '.bin'
611
612                                filename = '%s%s' % (filename, ext)
613                        else:
614                                filename = self.to_unicode(filename)
615
[48]616                        # From the trac code
617                        #
618                        filename = filename.replace('\\', '/').replace(':', '/')
619                        filename = os.path.basename(filename)
[22]620
[48]621                        # We try to normalize the filename to utf-8 NFC if we can.
622                        # Files uploaded from OS X might be in NFD.
[92]623                        # Check python version and then try it
[48]624                        #
625                        if sys.version_info[0] > 2 or (sys.version_info[0] == 2 and sys.version_info[1] >= 3):
[92]626                                try:
627                                        filename = unicodedata.normalize('NFC', unicode(filename, 'utf-8')).encode('utf-8') 
628                                except TypeError:
629                                        pass
[48]630
[22]631                        url_filename = urllib.quote(filename)
[68]632                        if self.VERSION == 0.8:
[22]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)
[68]637                        else:
638                                dir = '/tmp'
[22]639
[48]640                        path, fd =  util.create_unique_file(os.path.join(dir, url_filename))
[22]641                        text = part.get_payload(decode=1)
642                        if not text:
643                                text = '(None)'
[48]644                        fd.write(text)
645                        fd.close()
[22]646
647                        # get the filesize
648                        #
[48]649                        stats = os.lstat(path)
[22]650                        filesize = stats[stat.ST_SIZE]
651
652                        # Insert the attachment it differs for the different TRAC versions
[73]653                        #
[68]654                        if self.VERSION == 0.8:
[22]655                                cursor = self.db.cursor()
[73]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:   
[84]664                                        #self.db.close()
665                                        return count
[73]666
[22]667                                self.db.commit()
[73]668
[68]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()
[22]674
[77]675                # Return how many attachments
676                #
677                return count
[22]678
[77]679
[22]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):
[79]699                print 'File %s does not exist' %file
[22]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):
[79]714                        print "Not a valid project name: %s" %name
[22]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
[87]727
[22]728if __name__ == '__main__':
729        # Default config file
730        #
[24]731        configfile = '@email2trac_conf@'
[22]732        project = ''
733        component = ''
[87]734        ENABLE_SYSLOG = 0
735               
[22]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)
[87]742       
[22]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
[87]754       
[22]755        settings = ReadConfig(configfile, project_name)
756        if not settings.has_key('project'):
757                print __doc__
[79]758                print 'No Trac project is defined in the email2trac config file.'
[22]759                sys.exit(1)
[87]760       
[22]761        if component:
762                settings['component'] = component
[87]763       
[22]764        if settings.has_key('trac_version'):
765                version = float(settings['trac_version'])
766        else:
767                version = trac_default_version
768
[87]769        if settings.has_key('enable_syslog'):
770                ENABLE_SYSLOG =  float(settings['enable_syslog'])
771                       
[22]772        #debug HvB
773        #print settings
[87]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)
[22]802
[87]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()
[22]815
[97]816                if m:
[98]817                        tktparser.save_email_for_debug(m, True)
[97]818
[22]819# EOB
Note: See TracBrowser for help on using the repository browser.