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

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

EmailtoTracScript?:

email2trac.py.in:

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