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

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

EmailtoTracScript?:

email2trac.py.in:

  • Fixed a bug in Ticket Update, when ticket does not exists threat is as a new ticket


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