source: tags/0.9/email2trac.py.in @ 290

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

EmailtoTracScript?:

email2trac.py.in:

  • default version 0.10

INSTALL:

  • Only list http sites
  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 22.6 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 146 2006-12-08 16:48: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.10
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                temp = self.email_to_unicode(message['from'])
293                #print temp.encode('utf-8')
294
295                self.author, self.email_addr  = email.Utils.parseaddr(temp)
296                #print self.author.encode('utf-8', 'replace')
297
298                # Look for email address in registered trac users
299                #
300                if self.VERSION == 0.8:
301                        users = []
302                else:
303                        users = [ u for (u, n, e) in self.env.get_known_users(self.db)
304                                if e == self.email_addr ]
305
306                if len(users) == 1:
307                        self.email_field = users[0]
308                else:
309                        self.email_field =  self.email_to_unicode(message['from'])
310
311        def set_reply_fields(self, ticket, message):
312                """
313                Set all the right fields for a new ticket
314                """
315                ticket['reporter'] = self.email_field
316
317                # Put all CC-addresses in ticket CC field
318                #
319                if self.REPLY_ALL:
320                        #tos = message.get_all('to', [])
321                        ccs = message.get_all('cc', [])
322
323                        addrs = email.Utils.getaddresses(ccs)
324                        if not addrs:
325                                return
326
327                        # Remove reporter email address if notification is
328                        # on
329                        #
330                        if self.notification:
331                                try:
332                                        addrs.remove((self.author, self.email_addr))
333                                except ValueError, detail:
334                                        pass
335
336                        for name,mail in addrs:
337                                try:
338                                        mail_list = '%s, %s' %(mail_list, mail)
339                                except:
340                                        mail_list = mail
341
342                        if mail_list:
343                                ticket['cc'] = self.email_to_unicode(mail_list)
344
345        def save_email_for_debug(self, message, tempfile=False):
346                if tempfile:
347                        import tempfile
348                        msg_file = tempfile.mktemp('.email2trac')
349                else:
350                        msg_file = '/var/tmp/msg.txt'
351                print 'TD: saving email to %s' % msg_file
352                fx = open(msg_file, 'wb')
353                fx.write('%s' % message)
354                fx.close()
355                try:
356                        os.chmod(msg_file,S_IRWXU|S_IRWXG|S_IRWXO)
357                except OSError:
358                        pass
359
360        def ticket_update(self, m):
361                """
362                If the current email is a reply to an existing ticket, this function
363                will append the contents of this email to that ticket, instead of
364                creating a new one.
365                """
366                if not m['Subject']:
367                        return False
368                else:
369                        subject  = self.email_to_unicode(m['Subject'])
370
371                TICKET_RE = re.compile(r"""
372                                        (?P<ticketnr>[#][0-9]+:)
373                                        """, re.VERBOSE)
374
375                result =  TICKET_RE.search(subject)
376                if not result:
377                        return False
378
379                body_text = self.get_body_text(m)
380
381                # Strip '#' and ':' from ticket_id
382                #
383                ticket_id = result.group('ticketnr')
384                ticket_id = int(ticket_id[1:-1])
385
386                # Get current time
387                #
388                when = int(time.time())
389
390                if self.VERSION  == 0.8:
391                        tkt = Ticket(self.db, ticket_id)
392                        tkt.save_changes(self.db, self.author, body_text, when)
393                else:
394                        try:
395                                tkt = Ticket(self.env, ticket_id, self.db)
396                        except util.TracError, detail:
397                                return False
398
399                        tkt.save_changes(self.author, body_text, when)
400                        tkt['id'] = ticket_id
401
402                if self.VERSION  == 0.9:
403                        self.attachments(m, tkt, True)
404                else:
405                        self.attachments(m, tkt)
406
407                if self.notification:
408                        self.notify(tkt, False, when)
409
410                return True
411
412        def new_ticket(self, msg):
413                """
414                Create a new ticket
415                """
416                tkt = Ticket(self.env)
417                tkt['status'] = 'new'
418
419                # Some defaults
420                #
421                tkt['milestone'] = self.get_config('ticket', 'default_milestone')
422                tkt['priority'] = self.get_config('ticket', 'default_priority')
423                tkt['severity'] = self.get_config('ticket', 'default_severity')
424                tkt['version'] = self.get_config('ticket', 'default_version')
425
426                if not msg['Subject']:
427                        tkt['summary'] = u'(geen subject)'
428                else:
429                        tkt['summary'] = self.email_to_unicode(msg['Subject'])
430
431
432                if settings.has_key('component'):
433                        tkt['component'] = settings['component']
434                else:
435                        tkt['component'] = self.spam(msg)
436
437                # Discard SPAM messages.
438                #
439                if self.DROP_SPAM and (tkt['component'] == 'Spam'):
440                        # print 'This message is a SPAM. Automatic ticket insertion refused (SPAM level > %d' % self.SPAM_LEVEL
441                        return False   
442
443                # Set default owner for component
444                #
445                self.set_owner(tkt)
446                self.set_reply_fields(tkt, msg)
447
448                # produce e-mail like header
449                #
450                head = ''
451                if self.EMAIL_HEADER > 0:
452                        head = self.email_header_txt(msg)
453                       
454                body_text = self.get_body_text(msg)
455
456                tkt['description'] = '\r\n%s\r\n%s' \
457                        %(head, body_text)
458
459                when = int(time.time())
460                if self.VERSION == 0.8:
461                        ticket_id = tkt.insert(self.db)
462                else:
463                        ticket_id = tkt.insert()
464                        tkt['id'] = ticket_id
465
466                changed = False
467                comment = ''
468
469                # Rewrite the description if we have mailto enabled
470                #
471                if self.MAILTO:
472                        changed = True
473                        comment = u'\nadded mailto line\n'
474                        mailto = self.html_mailto_link(tkt['summary'], ticket_id, body_text)
475                        tkt['description'] = u'\r\n%s\r\n%s%s\r\n' \
476                                %(head, mailto, body_text)
477
478                n =  self.attachments(msg, tkt)
479                if n:
480                        changed = True
481                        comment = '%s\nThis message has %d attachment(s)\n' %(comment, n)
482
483                if changed:
484                        if self.VERSION  == 0.8:
485                                tkt.save_changes(self.db, self.author, comment)
486                        else:
487                                tkt.save_changes(self.author, comment)
488
489                #print tkt.get_changelog(self.db, when)
490
491                if self.notification:
492                        self.notify(tkt, True)
493                        #self.notify(tkt, False)
494
495        def parse(self, fp):
496                global m
497
498                m = email.message_from_file(fp)
499                if not m:
500                        return
501
502                if self.DEBUG > 1:        # save the entire e-mail message text
503                        self.save_email_for_debug(m)
504                        self.debug_attachments(m)
505
506                self.db = self.env.get_db_cnx()
507                self.get_author_emailaddrs(m)
508
509                if self.get_config('notification', 'smtp_enabled') in ['true']:
510                        self.notification = 1
511                else:
512                        self.notification = 0
513
514                # Must we update existing tickets
515                #
516                if self.TICKET_UPDATE > 0:
517                        if self.ticket_update(m):
518                                return True
519
520                self.new_ticket(m)
521
522        def strip_signature(self, text):
523                """
524                Strip signature from message, inspired by Mailman software
525                """
526                body = []
527                for line in text.splitlines():
528                        if line == '-- ':
529                                break
530                        body.append(line)
531
532                return ('\n'.join(body))
533
534        def get_body_text(self, msg):
535                """
536                put the message text in the ticket description or in the changes field.
537                message text can be plain text or html or something else
538                """
539                has_description = 0
540                encoding = True
541                ubody_text = u'No plain text message'
542                for part in msg.walk():
543
544                        # 'multipart/*' is a container for multipart messages
545                        #
546                        if part.get_content_maintype() == 'multipart':
547                                continue
548
549                        if part.get_content_type() == 'text/plain':
550                                # Try to decode, if fails then do not decode
551                                #
552                                body_text = part.get_payload(decode=1)
553                                if not body_text:                       
554                                        body_text = part.get_payload(decode=0)
555               
556                                if self.STRIP_SIGNATURE:
557                                        body_text = self.strip_signature(body_text)
558
559                                # Get contents charset (iso-8859-15 if not defined in mail headers)
560                                #
561                                charset = part.get_content_charset()
562                                if not charset:
563                                        charset = 'iso-8859-15'
564
565                                try:
566                                        ubody_text = unicode(body_text, charset)
567
568                                except UnicodeError, detail:
569                                        ubody_text = unicode(body_text, 'iso-8859-15')
570
571                                except LookupError, detail:
572                                        ubody_text = 'ERROR: Could not find charset: %s, please install' %(charset)
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.author
635
636                # Must find a fix
637                #
638                #arr = string.split(body, '\n')
639                #arr = map(self.mail_line, arr)
640                #body = string.join(arr, '\n')
641                #body = '%s wrote:\n%s' %(author, body)
642
643                # Temporary fix
644                #
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                return str
653
654        def attachments(self, message, ticket, update=False):
655                '''
656                save any attachments as files in the ticket's directory
657                '''
658                count = 0
659                first = 0
660                number = 0
661                for part in message.walk():
662                        if part.get_content_maintype() == 'multipart':          # multipart/* is just a container
663                                continue
664
665                        if not first:                                                                           # first content is the message
666                                first = 1
667                                if part.get_content_type() == 'text/plain':             # if first is text, is was already put in the description
668                                        continue
669
670                        filename = part.get_filename()
671                        count = count + 1
672                        if not filename:
673                                number = number + 1
674                                filename = 'part%04d' % number
675
676                                ext = mimetypes.guess_extension(part.get_content_type())
677                                if not ext:
678                                        ext = '.bin'
679
680                                filename = '%s%s' % (filename, ext)
681                        else:
682                                filename = self.email_to_unicode(filename)
683
684                        # From the trac code
685                        #
686                        filename = filename.replace('\\', '/').replace(':', '/')
687                        filename = os.path.basename(filename)
688
689                        # We try to normalize the filename to utf-8 NFC if we can.
690                        # Files uploaded from OS X might be in NFD.
691                        # Check python version and then try it
692                        #
693                        if sys.version_info[0] > 2 or (sys.version_info[0] == 2 and sys.version_info[1] >= 3):
694                                try:
695                                        filename = unicodedata.normalize('NFC', unicode(filename, 'utf-8')).encode('utf-8') 
696                                except TypeError:
697                                        pass
698
699                        url_filename = urllib.quote(filename)
700                        if self.VERSION == 0.8:
701                                dir = os.path.join(self.env.get_attachments_dir(), 'ticket',
702                                                        urllib.quote(str(ticket['id'])))
703                                if not os.path.exists(dir):
704                                        mkdir_p(dir, 0755)
705                        else:
706                                dir = '/tmp'
707
708                        path, fd =  util.create_unique_file(os.path.join(dir, url_filename))
709                        text = part.get_payload(decode=1)
710                        if not text:
711                                text = '(None)'
712                        fd.write(text)
713                        fd.close()
714
715                        # get the filesize
716                        #
717                        stats = os.lstat(path)
718                        filesize = stats[stat.ST_SIZE]
719
720                        # Insert the attachment it differs for the different TRAC versions
721                        #
722                        if self.VERSION == 0.8:
723                                cursor = self.db.cursor()
724                                try:
725                                        cursor.execute('INSERT INTO attachment VALUES("%s","%s","%s",%d,%d,"%s","%s","%s")'
726                                                %('ticket', urllib.quote(str(ticket['id'])), filename + '?format=raw', filesize,
727                                                int(time.time()),'', self.author, 'e-mail') )
728
729                                # Attachment is already known
730                                #
731                                except sqlite.IntegrityError:   
732                                        #self.db.close()
733                                        return count
734
735                                self.db.commit()
736
737                        else:
738                                fd = open(path)
739                                att = attachment.Attachment(self.env, 'ticket', ticket['id'])
740
741                                # This will break the ticket_update system, the body_text is vaporized
742                                # ;-(
743                                #
744                                if not update:
745                                        att.author = self.author
746                                        att.description = self.email_to_unicode('Added by email2trac')
747
748                                att.insert(url_filename, fd, filesize)
749                                fd.close()
750
751                        # Remove the created temporary filename
752                        #
753                        os.unlink(path)
754
755                # Return how many attachments
756                #
757                return count
758
759
760def mkdir_p(dir, mode):
761        '''do a mkdir -p'''
762
763        arr = string.split(dir, '/')
764        path = ''
765        for part in arr:
766                path = '%s/%s' % (path, part)
767                try:
768                        stats = os.stat(path)
769                except OSError:
770                        os.mkdir(path, mode)
771
772
773def ReadConfig(file, name):
774        """
775        Parse the config file
776        """
777
778        if not os.path.isfile(file):
779                print 'File %s does not exist' %file
780                sys.exit(1)
781
782        config = ConfigParser.ConfigParser()
783        try:
784                config.read(file)
785        except ConfigParser.MissingSectionHeaderError,detail:
786                print detail
787                sys.exit(1)
788
789
790        # Use given project name else use defaults
791        #
792        if name:
793                if not config.has_section(name):
794                        print "Not a valid project name: %s" %name
795                        print "Valid names: %s" %config.sections()
796                        sys.exit(1)
797
798                project =  dict()
799                for option in  config.options(name):
800                        project[option] = config.get(name, option)
801
802        else:
803                project = config.defaults()
804
805        return project
806
807
808if __name__ == '__main__':
809        # Default config file
810        #
811        configfile = '@email2trac_conf@'
812        project = ''
813        component = ''
814        ENABLE_SYSLOG = 0
815               
816        try:
817                opts, args = getopt.getopt(sys.argv[1:], 'chf:p:', ['component=','help', 'file=', 'project='])
818        except getopt.error,detail:
819                print __doc__
820                print detail
821                sys.exit(1)
822       
823        project_name = None
824        for opt,value in opts:
825                if opt in [ '-h', '--help']:
826                        print __doc__
827                        sys.exit(0)
828                elif opt in ['-c', '--component']:
829                        component = value
830                elif opt in ['-f', '--file']:
831                        configfile = value
832                elif opt in ['-p', '--project']:
833                        project_name = value
834       
835        settings = ReadConfig(configfile, project_name)
836        if not settings.has_key('project'):
837                print __doc__
838                print 'No Trac project is defined in the email2trac config file.'
839                sys.exit(1)
840       
841        if component:
842                settings['component'] = component
843       
844        if settings.has_key('trac_version'):
845                version = float(settings['trac_version'])
846        else:
847                version = trac_default_version
848
849        if settings.has_key('enable_syslog'):
850                ENABLE_SYSLOG =  float(settings['enable_syslog'])
851                       
852        #debug HvB
853        #print settings
854       
855        try:
856                if version == 0.8:
857                        from trac.Environment import Environment
858                        from trac.Ticket import Ticket
859                        from trac.Notify import TicketNotifyEmail
860                        from trac.Href import Href
861                        from trac import util
862                        import sqlite
863                elif version == 0.9:
864                        from trac import attachment
865                        from trac.env import Environment
866                        from trac.ticket import Ticket
867                        from trac.web.href import Href
868                        from trac import util
869                        from trac.Notify import TicketNotifyEmail
870                elif version == 0.10:
871                        from trac import attachment
872                        from trac.env import Environment
873                        from trac.ticket import Ticket
874                        from trac.web.href import Href
875                        from trac import util
876                        #
877                        # return  util.text.to_unicode(str)
878                        #
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.