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

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

EmailtoTracScript?:

email2trac.py.in:

  • if no charset use a default one
  • 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 102 2006-07-14 13:11:50Z 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()
[102]514                                if not charset:
515                                        charset = 'iso-8859-15'
516
[89]517                                try:
[96]518                                        ubody_text = unicode(body_text, charset)
[100]519
520                                except UnicodeError, detail:
[96]521                                        ubody_text = unicode(body_text, 'iso-8859-15')
[89]522
[100]523                                except LookupError, detail:
524                                        ubody_text = body_text
525                                        encoding = False
526
[22]527                        elif part.get_content_type() == 'text/html':
[100]528                                ubody_text = '\r\n(see attachment for HTML mail message)\r\n'
[22]529
530                        else:
[100]531                                ubody_text = '\r\n(see attachment for message)\r\n'
[22]532
533                        has_description = 1
534                        break           # we have the description, so break
535
536                if not has_description:
[100]537                        ubody_text = '\r\n(see attachment for message)\r\n'
[22]538
[100]539                # A patch so that the web-interface will not update the description
540                # field of a ticket
541                #
542                ubody_text = ('\r\n'.join(ubody_text.splitlines()))
[22]543
[100]544                #  If we can unicode it try to encode it for trac
545                #  else we a lot of garbage
546                #
547                if encoding:
548                        ubody_text = ubody_text.encode('utf-8')
549
550                return ubody_text
551
[77]552        def notify(self, tkt , new=True, modtime=0):
[79]553                """
554                A wrapper for the TRAC notify function. So we can use templates
555                """
[41]556                try:
557                        # create false {abs_}href properties, to trick Notify()
558                        #
559                        self.env.abs_href = Href(self.get_config('project', 'url'))
560                        self.env.href = Href(self.get_config('project', 'url'))
[22]561
[41]562                        tn = TicketNotifyEmail(self.env)
[42]563                        if self.notify_template:
564                                tn.template_name = self.notify_template;
565
[77]566                        tn.notify(tkt, new, modtime)
[41]567
568                except Exception, e:
[79]569                        print 'TD: Failure sending notification on creation of ticket #%s: %s' %(tkt['id'], e)
[41]570
[22]571        def mail_line(self, str):
572                return '%s %s' % (self.comment, str)
573
574
[72]575        def html_mailto_link(self, subject, id, body):
576                if not self.author:
[88]577                        author = self.email_addr
[22]578                else:   
[72]579                        author = self.to_unicode(self.author)
[22]580
581                # Must find a fix
582                #
583                #arr = string.split(body, '\n')
584                #arr = map(self.mail_line, arr)
585                #body = string.join(arr, '\n')
586                #body = '%s wrote:\n%s' %(author, body)
587
588                # Temporary fix
[74]589                str = 'mailto:%s?Subject=%s&Cc=%s' %(
590                       urllib.quote(self.email_addr),
591                           urllib.quote('Re: #%s: %s' %(id, subject)),
592                           urllib.quote(self.MAILTO_CC)
593                           )
594
[90]595                str = '\r\n{{{\r\n#!html\r\n<a href="%s">Reply to: %s</a>\r\n}}}\r\n' %(str, author)
[22]596
597                return str
598
[72]599        def attachments(self, message, ticket):
[79]600                '''
601                save any attachments as files in the ticket's directory
602                '''
[22]603                count = 0
[77]604                first = 0
605                number = 0
[22]606                for part in message.walk():
607                        if part.get_content_maintype() == 'multipart':          # multipart/* is just a container
608                                continue
609
610                        if not first:                                                                           # first content is the message
611                                first = 1
612                                if part.get_content_type() == 'text/plain':             # if first is text, is was already put in the description
613                                        continue
614
615                        filename = part.get_filename()
[77]616                        count = count + 1
[22]617                        if not filename:
[77]618                                number = number + 1
619                                filename = 'part%04d' % number
[22]620
[72]621                                ext = mimetypes.guess_extension(part.get_content_type())
[22]622                                if not ext:
623                                        ext = '.bin'
624
625                                filename = '%s%s' % (filename, ext)
626                        else:
627                                filename = self.to_unicode(filename)
628
[48]629                        # From the trac code
630                        #
631                        filename = filename.replace('\\', '/').replace(':', '/')
632                        filename = os.path.basename(filename)
[22]633
[48]634                        # We try to normalize the filename to utf-8 NFC if we can.
635                        # Files uploaded from OS X might be in NFD.
[92]636                        # Check python version and then try it
[48]637                        #
638                        if sys.version_info[0] > 2 or (sys.version_info[0] == 2 and sys.version_info[1] >= 3):
[92]639                                try:
640                                        filename = unicodedata.normalize('NFC', unicode(filename, 'utf-8')).encode('utf-8') 
641                                except TypeError:
642                                        pass
[48]643
[22]644                        url_filename = urllib.quote(filename)
[68]645                        if self.VERSION == 0.8:
[22]646                                dir = os.path.join(self.env.get_attachments_dir(), 'ticket',
647                                                        urllib.quote(str(ticket['id'])))
648                                if not os.path.exists(dir):
649                                        mkdir_p(dir, 0755)
[68]650                        else:
651                                dir = '/tmp'
[22]652
[48]653                        path, fd =  util.create_unique_file(os.path.join(dir, url_filename))
[22]654                        text = part.get_payload(decode=1)
655                        if not text:
656                                text = '(None)'
[48]657                        fd.write(text)
658                        fd.close()
[22]659
660                        # get the filesize
661                        #
[48]662                        stats = os.lstat(path)
[22]663                        filesize = stats[stat.ST_SIZE]
664
665                        # Insert the attachment it differs for the different TRAC versions
[73]666                        #
[68]667                        if self.VERSION == 0.8:
[22]668                                cursor = self.db.cursor()
[73]669                                try:
670                                        cursor.execute('INSERT INTO attachment VALUES("%s","%s","%s",%d,%d,"%s","%s","%s")'
671                                                %('ticket', urllib.quote(str(ticket['id'])), filename + '?format=raw', filesize,
672                                                int(time.time()),'', self.author, 'e-mail') )
673
674                                # Attachment is already known
675                                #
676                                except sqlite.IntegrityError:   
[84]677                                        #self.db.close()
678                                        return count
[73]679
[22]680                                self.db.commit()
[73]681
[68]682                        else:
683                                fd = open(path)
684                                att = attachment.Attachment(self.env, 'ticket', ticket['id'])
685                                att.insert(url_filename, fd, filesize)
686                                fd.close()
[22]687
[77]688                # Return how many attachments
689                #
690                return count
[22]691
[77]692
[22]693def mkdir_p(dir, mode):
694        '''do a mkdir -p'''
695
696        arr = string.split(dir, '/')
697        path = ''
698        for part in arr:
699                path = '%s/%s' % (path, part)
700                try:
701                        stats = os.stat(path)
702                except OSError:
703                        os.mkdir(path, mode)
704
705
706def ReadConfig(file, name):
707        """
708        Parse the config file
709        """
710
711        if not os.path.isfile(file):
[79]712                print 'File %s does not exist' %file
[22]713                sys.exit(1)
714
715        config = ConfigParser.ConfigParser()
716        try:
717                config.read(file)
718        except ConfigParser.MissingSectionHeaderError,detail:
719                print detail
720                sys.exit(1)
721
722
723        # Use given project name else use defaults
724        #
725        if name:
726                if not config.has_section(name):
[79]727                        print "Not a valid project name: %s" %name
[22]728                        print "Valid names: %s" %config.sections()
729                        sys.exit(1)
730
731                project =  dict()
732                for option in  config.options(name):
733                        project[option] = config.get(name, option)
734
735        else:
736                project = config.defaults()
737
738        return project
739
[87]740
[22]741if __name__ == '__main__':
742        # Default config file
743        #
[24]744        configfile = '@email2trac_conf@'
[22]745        project = ''
746        component = ''
[87]747        ENABLE_SYSLOG = 0
748               
[22]749        try:
750                opts, args = getopt.getopt(sys.argv[1:], 'chf:p:', ['component=','help', 'file=', 'project='])
751        except getopt.error,detail:
752                print __doc__
753                print detail
754                sys.exit(1)
[87]755       
[22]756        project_name = None
757        for opt,value in opts:
758                if opt in [ '-h', '--help']:
759                        print __doc__
760                        sys.exit(0)
761                elif opt in ['-c', '--component']:
762                        component = value
763                elif opt in ['-f', '--file']:
764                        configfile = value
765                elif opt in ['-p', '--project']:
766                        project_name = value
[87]767       
[22]768        settings = ReadConfig(configfile, project_name)
769        if not settings.has_key('project'):
770                print __doc__
[79]771                print 'No Trac project is defined in the email2trac config file.'
[22]772                sys.exit(1)
[87]773       
[22]774        if component:
775                settings['component'] = component
[87]776       
[22]777        if settings.has_key('trac_version'):
778                version = float(settings['trac_version'])
779        else:
780                version = trac_default_version
781
[87]782        if settings.has_key('enable_syslog'):
783                ENABLE_SYSLOG =  float(settings['enable_syslog'])
784                       
[22]785        #debug HvB
786        #print settings
[87]787       
788        try:
789                if version == 0.8:
790                        from trac.Environment import Environment
791                        from trac.Ticket import Ticket
792                        from trac.Notify import TicketNotifyEmail
793                        from trac.Href import Href
794                        from trac import util
795                        import sqlite
796                elif version == 0.9:
797                        from trac import attachment
798                        from trac.env import Environment
799                        from trac.ticket import Ticket
800                        from trac.web.href import Href
801                        from trac import util
802                        from trac.Notify import TicketNotifyEmail
803                elif version == 0.10:
804                        from trac import attachment
805                        from trac.env import Environment
806                        from trac.ticket import Ticket
807                        from trac.web.href import Href
808                        from trac import util
809                        # see http://projects.edgewall.com/trac/changeset/2799
810                        from trac.ticket.notification import TicketNotifyEmail
811       
812                env = Environment(settings['project'], create=0)
813                tktparser = TicketEmailParser(env, settings, version)
814                tktparser.parse(sys.stdin)
[22]815
[87]816        # Catch all errors ans log to SYSLOG if we have enabled this
817        # else stdout
818        #
819        except Exception, error:
820                if ENABLE_SYSLOG:
821                        syslog.openlog('email2trac', syslog.LOG_NOWAIT)
822                        etype, evalue, etb = sys.exc_info()
823                        for e in traceback.format_exception(etype, evalue, etb):
824                                syslog.syslog(e)
825                        syslog.closelog()
826                else:
827                        traceback.print_exc()
[22]828
[97]829                if m:
[98]830                        tktparser.save_email_for_debug(m, True)
[97]831
[22]832# EOB
Note: See TracBrowser for help on using the repository browser.