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

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

EmailtoTracScript?:

email2trac.py.in:

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