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

Last change on this file since 138 was 138, checked in by bas, 17 years ago

EmailtoTracScript?:

email2trac.py.in:

  • Use trac.util.text.to_unicode function instead of our own ;-) I run some tests with the new code

email2trac.conf:

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