root/trunk/email2trac/email2trac.py

Revision 19, 26.2 kB (checked in by nextime, 5 years ago)

Little bugfix to prevent ticket loop

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