Benjamin Renard commited on 2011-11-24 16:55:40
Showing 10 changed files, with 760 additions and 0 deletions.
... | ... |
@@ -0,0 +1 @@ |
1 |
+*.pyc |
... | ... |
@@ -0,0 +1,95 @@ |
1 |
+##### Mantis SMTP Gateway ##### |
|
2 |
+ |
|
3 |
+Mantis SMTP Gateway is an email gateway for Mantis Bug Tracker. |
|
4 |
+ |
|
5 |
+Run as a SMTP server, Mantis SMTP try to add email informations to Mantis Bug Tracker through its SOAP API. |
|
6 |
+ |
|
7 |
+Process : |
|
8 |
+--------- |
|
9 |
+ |
|
10 |
+Parsing subject to detect issue id : if present, the email will be processed as a note. Also, create a new issue in project corresponding to email recipient. |
|
11 |
+ |
|
12 |
+Features : |
|
13 |
+---------- |
|
14 |
+ |
|
15 |
+- detect issue id from subject |
|
16 |
+- add note to existing issue |
|
17 |
+- create new issue in project |
|
18 |
+- multiple project support with different email recipient |
|
19 |
+- add email attachment as issue attachment |
|
20 |
+- email warning sender on error |
|
21 |
+- daemon mode (init script provide) |
|
22 |
+ |
|
23 |
+#### Installation #### |
|
24 |
+ |
|
25 |
+### Python Requirements ### |
|
26 |
+ |
|
27 |
+# System lib : |
|
28 |
+- sys |
|
29 |
+- os |
|
30 |
+- signal |
|
31 |
+ |
|
32 |
+# Mail lib : |
|
33 |
|
|
34 |
+- smtplib |
|
35 |
+- smtpd |
|
36 |
+ |
|
37 |
+# SOAP lib : |
|
38 |
+- suds |
|
39 |
+ |
|
40 |
+# Daemon lib : |
|
41 |
+- logging |
|
42 |
+- simplejson : to load config file |
|
43 |
+- asyncore |
|
44 |
+ |
|
45 |
+# Utils lib : |
|
46 |
+- re |
|
47 |
+- string |
|
48 |
+- optparse.OptionParser : to parse command line parameters |
|
49 |
+ |
|
50 |
+### Redhat/Debian Packages Requirements ### |
|
51 |
+ |
|
52 |
+- python-simplejson |
|
53 |
+- python-suds |
|
54 |
+ |
|
55 |
+### Scripted installation ### |
|
56 |
+ |
|
57 |
+An installation script is provided : |
|
58 |
+ |
|
59 |
+ ~/mantisbt-smtp-gateway/$ ./install.sh install |
|
60 |
+ |
|
61 |
+### Manual installation ### |
|
62 |
+ |
|
63 |
+- Install requirements |
|
64 |
+- Copy all file in src directory in root : |
|
65 |
+ |
|
66 |
+ ~/mantisbt-smtp-gateway/$ cd src/ |
|
67 |
+ ~/mantisbt-smtp-gateway/src/$ cp -r * / |
|
68 |
+ |
|
69 |
+#### Configuration #### |
|
70 |
+ |
|
71 |
+Mantis SMTP Gateway use an single JSON configuration file : |
|
72 |
+ |
|
73 |
+ /etc/mantis-smtp.conf |
|
74 |
+ |
|
75 |
+Example : |
|
76 |
+--------- |
|
77 |
+ |
|
78 |
+{ |
|
79 |
+"mail_from": "postmaster-mantis@example.net", |
|
80 |
+"ws_url": "http://mantis.example.net/api/soap/mantisconnect.php?wsdl", |
|
81 |
+"ws_user": "MAIL", |
|
82 |
+"ws_pwd": "password", |
|
83 |
+"ws_default_category": "other", |
|
84 |
+"projects": { |
|
85 |
+ "mantis-project1@example.net": { |
|
86 |
+ "project_name": "Project 1" |
|
87 |
+ }, |
|
88 |
+ "mantis-dev@example.net": { |
|
89 |
+ "project_name": "Development", |
|
90 |
+ "category": "dev" |
|
91 |
+ } |
|
92 |
+} |
|
93 |
+} |
|
94 |
+ |
|
95 |
+ |
... | ... |
@@ -0,0 +1,74 @@ |
1 |
+#!/bin/bash |
|
2 |
+ |
|
3 |
+cd $( dirname $0 ) |
|
4 |
+dir=$( pwd ) |
|
5 |
+ |
|
6 |
+files=" |
|
7 |
+/etc/mantis-smtp.conf |
|
8 |
+/etc/init.d/mantis-smtp |
|
9 |
+/usr/bin/mantis-smtp |
|
10 |
+/usr/share/mantis-smtp |
|
11 |
+" |
|
12 |
+ |
|
13 |
+function install_file() { |
|
14 |
+ from=$1 |
|
15 |
+ dst=$2 |
|
16 |
+ if [ -e "$dst" ] |
|
17 |
+ then |
|
18 |
+ echo -e "\t- backuping $file in ${file}.old" |
|
19 |
+ mv $dst $dst.old |
|
20 |
+ fi |
|
21 |
+ echo -e "\t- $file" |
|
22 |
+ ln -s "$from" "$dst" |
|
23 |
+} |
|
24 |
+ |
|
25 |
+ |
|
26 |
+case "$1" in |
|
27 |
+ install-req) |
|
28 |
+ echo "Install requirements :" |
|
29 |
+ |
|
30 |
+ if [ -f /usr/bin/apt-get ] |
|
31 |
+ then |
|
32 |
+ apt-get install python-suds python-simplejson |
|
33 |
+ elif [ -x /usr/bin/yum ] |
|
34 |
+ then |
|
35 |
+ yum install python-suds python-simplejson |
|
36 |
+ else |
|
37 |
+ echo "Can't package manager." |
|
38 |
+ exit 1 |
|
39 |
+ fi |
|
40 |
+ |
|
41 |
+ ;; |
|
42 |
+ install) |
|
43 |
+ $0 install-req |
|
44 |
+ |
|
45 |
+ echo "Files installation :" |
|
46 |
+ |
|
47 |
+ for file in $files |
|
48 |
+ do |
|
49 |
+ install_file ${dir}/src${file} ${file} |
|
50 |
+ done |
|
51 |
+ if [ -f /etc/redhat-release ] |
|
52 |
+ then |
|
53 |
+ install_file ${dir}/src/etc/default/mantis-smtp /etc/sysconfig/mantis-smtp |
|
54 |
+ elif [ -f /etc/debian_version ] |
|
55 |
+ then |
|
56 |
+ install_file ${dir}/src/etc/default/mantis-smtp /etc/default/mantis-smtp |
|
57 |
+ fi |
|
58 |
+ |
|
59 |
+ echo done. |
|
60 |
+ ;; |
|
61 |
+ uninstall) |
|
62 |
+ echo "Uninstalling files :" |
|
63 |
+ for file in $files /etc/sysconfig/mantis-smtp /etc/default/mantis-smtp |
|
64 |
+ do |
|
65 |
+ [ ! -e $file ] && continue |
|
66 |
+ echo -e "\t- Uninstall file $file :" |
|
67 |
+ rm -rf "$file" |
|
68 |
+ done |
|
69 |
+ |
|
70 |
+ echo done. |
|
71 |
+ ;; |
|
72 |
+ *) |
|
73 |
+ echo "Usage : $0 {install|install-req|uninstall}" |
|
74 |
+esac |
... | ... |
@@ -0,0 +1,17 @@ |
1 |
+# Mantis SMTP Server configuration file |
|
2 |
+# |
|
3 |
+ |
|
4 |
+# PID file |
|
5 |
+#pidfile=/var/run/mantis-smtp.pid |
|
6 |
+ |
|
7 |
+# Config file |
|
8 |
+configfile=/etc/mantis-smtp.conf |
|
9 |
+ |
|
10 |
+# Log file |
|
11 |
+#logfile=/var/log/mantis-smtp.log |
|
12 |
+ |
|
13 |
+# Command line mantis-smtp options |
|
14 |
+OPTIONS="-c $configfile --daemon --pid ${pidfile} -l $logfile -H 127.0.0.1 -p 10029" |
|
15 |
+ |
|
16 |
+# Stop timeout |
|
17 |
+#STOP_TIMEOUT=10 |
... | ... |
@@ -0,0 +1,110 @@ |
1 |
+#!/bin/bash |
|
2 |
+### BEGIN INIT INFO |
|
3 |
+# Provides: mantis-smtp |
|
4 |
+# Required-Start: $local_fs $network |
|
5 |
+# Required-Stop: $local_fs $network |
|
6 |
+# Default-Start: 2 3 4 5 |
|
7 |
+# Default-Stop: 0 1 6 |
|
8 |
+# Short-Description: Start/stop Mantis SMTP gateway server |
|
9 |
+# Description: Mantis SMTP Server is a SMTP Gateway for Mantis |
|
10 |
+# bug tracker |
|
11 |
+### END INIT INFO |
|
12 |
+# mantis-smtp Startup script for the Mantis SMTP Server |
|
13 |
+# |
|
14 |
+# chkconfig: 2345 80 30 |
|
15 |
+# description: Mantis SMTP Server is a SMTP Gateway for Mantis |
|
16 |
+# bug tracker |
|
17 |
+# processname: mantis-smtp |
|
18 |
+# config: /etc/sysconfig/mantis-smtp |
|
19 |
+# config: /etc/mantis-smtp.conf |
|
20 |
+# pidfile: /var/run/mantis-smtp.pid |
|
21 |
+ |
|
22 |
+# Source function library. |
|
23 |
+if [ -f /etc/redhat-release ] |
|
24 |
+then |
|
25 |
+ . /etc/rc.d/init.d/functions |
|
26 |
+elif [ -f /etc/debian_version ] |
|
27 |
+then |
|
28 |
+ . /lib/lsb/init-functions |
|
29 |
+fi |
|
30 |
+ |
|
31 |
+prog="Mantis SMTP Server" |
|
32 |
+pidfile=/var/run/mantis-smtp.pid |
|
33 |
+mantis_smtp_bin=/usr/bin/mantis-smtp |
|
34 |
+configfile=/etc/mantis-smtp.conf |
|
35 |
+logfile=/var/log/mantis-smtp.log |
|
36 |
+ |
|
37 |
+OPTIONS="-c $configfile --daemon --pid ${pidfile} -l $logfile -H 127.0.0.1 -p 10029" |
|
38 |
+STOP_TIMEOUT=10 |
|
39 |
+ |
|
40 |
+if [ -f /etc/redhat-release -a -f /etc/sysconfig/mantis-smtp ] |
|
41 |
+then |
|
42 |
+ . /etc/sysconfig/mantis-smtp |
|
43 |
+elif [ -f /etc/debian_version ] |
|
44 |
+then |
|
45 |
+ . /etc/default/mantis-smtp |
|
46 |
+fi |
|
47 |
+ |
|
48 |
+start() { |
|
49 |
+ if [ -f /etc/redhat-release ] |
|
50 |
+ then |
|
51 |
+ echo -n $"Starting $prog: " |
|
52 |
+ daemon --pidfile=${pidfile} $mantis_smtp_bin $OPTIONS |
|
53 |
+ echo |
|
54 |
+ elif [ -f /etc/debian_version ] |
|
55 |
+ then |
|
56 |
+ log_daemon_msg "Starting $prog" $( basename $mantis_smtp_bin ) |
|
57 |
+ start-stop-daemon --start --quiet --pidfile ${pidfile} --exec $mantis_smtp_bin -- $OPTIONS |
|
58 |
+ log_end_msg $? |
|
59 |
+ fi |
|
60 |
+} |
|
61 |
+ |
|
62 |
+stop() { |
|
63 |
+ if [ -f /etc/redhat-release ] |
|
64 |
+ then |
|
65 |
+ echo -n $"Stopping $prog: " |
|
66 |
+ killproc -p ${pidfile} -d ${STOP_TIMEOUT} $mantis_smtp_bin |
|
67 |
+ [ $? = 0 ] && rm -f ${pidfile} |
|
68 |
+ echo |
|
69 |
+ elif [ -f /etc/debian_version ] |
|
70 |
+ then |
|
71 |
+ log_daemon_msg "Stopping $prog" $( basename $mantis_smtp_bin ) |
|
72 |
+ start-stop-daemon --stop --quiet --pidfile ${pidfile} |
|
73 |
+ log_end_msg $? |
|
74 |
+ fi |
|
75 |
+} |
|
76 |
+ |
|
77 |
+show_status() { |
|
78 |
+ if [ -f /etc/redhat-release ] |
|
79 |
+ then |
|
80 |
+ status -p ${pidfile} $mantis_smtp_bin |
|
81 |
+ elif [ -f /etc/debian_version ] |
|
82 |
+ then |
|
83 |
+ status_of_proc -p ${pidfile} $( basename $mantis_smtp_bin ) "$prog" |
|
84 |
+ fi |
|
85 |
+} |
|
86 |
+ |
|
87 |
+case "$1" in |
|
88 |
+ start) |
|
89 |
+ start |
|
90 |
+ ;; |
|
91 |
+ stop) |
|
92 |
+ stop |
|
93 |
+ ;; |
|
94 |
+ status) |
|
95 |
+ show_status |
|
96 |
+ ;; |
|
97 |
+ restart|reload|force-reload) |
|
98 |
+ stop |
|
99 |
+ start |
|
100 |
+ ;; |
|
101 |
+ condrestart|cond-restart) |
|
102 |
+ if [ -f ${pidfile} ] ; then |
|
103 |
+ stop |
|
104 |
+ start |
|
105 |
+ fi |
|
106 |
+ ;; |
|
107 |
+ *) |
|
108 |
+ echo $"Usage: $prog {start|stop|restart|force-reload|condrestart|reload|status}" |
|
109 |
+ exit 1 |
|
110 |
+esac |
... | ... |
@@ -0,0 +1,16 @@ |
1 |
+{ |
|
2 |
+"mail_from": "postmaster-mantis@example.net", |
|
3 |
+"ws_url": "http://mantis.example.net/api/soap/mantisconnect.php?wsdl", |
|
4 |
+"ws_user": "MAIL", |
|
5 |
+"ws_pwd": "password", |
|
6 |
+"ws_default_category": "other", |
|
7 |
+"projects": { |
|
8 |
+ "mantis-project1@example.net": { |
|
9 |
+ "project_name": "Project 1" |
|
10 |
+ }, |
|
11 |
+ "mantis-dev@example.net": { |
|
12 |
+ "project_name": "Development", |
|
13 |
+ "category": "dev" |
|
14 |
+ } |
|
15 |
+} |
|
16 |
+} |
... | ... |
@@ -0,0 +1,128 @@ |
1 |
+#!/usr/bin/python |
|
2 |
+ |
|
3 |
+import sys |
|
4 |
+ |
|
5 |
+sys.path.append('/usr/local/share/mantis-smtp') |
|
6 |
+sys.path.append('/usr/share/mantis-smtp') |
|
7 |
+ |
|
8 |
+import MantisSMTPServer |
|
9 |
+import logging |
|
10 |
+from optparse import OptionParser |
|
11 |
+import simplejson as json |
|
12 |
+ |
|
13 |
+parser = OptionParser() |
|
14 |
+ |
|
15 |
+parser.add_option('-v', |
|
16 |
+ '--verbose', |
|
17 |
+ action="store_true", |
|
18 |
+ dest="verbose", |
|
19 |
+ help="Enable verbose mode") |
|
20 |
+ |
|
21 |
+parser.add_option('-d', |
|
22 |
+ '--daemon', |
|
23 |
+ action="store_true", |
|
24 |
+ dest="daemon", |
|
25 |
+ help="Start in daemon mode") |
|
26 |
+ |
|
27 |
+parser.add_option('--pid', |
|
28 |
+ action="store", |
|
29 |
+ type="string", |
|
30 |
+ dest="pidfile", |
|
31 |
+ help="The logfile path", |
|
32 |
+ default=None) |
|
33 |
+ |
|
34 |
+parser.add_option('-c', |
|
35 |
+ '--configfile', |
|
36 |
+ action="store", |
|
37 |
+ type="string", |
|
38 |
+ dest="configfile", |
|
39 |
+ help="The logfile path", |
|
40 |
+ default=None) |
|
41 |
+ |
|
42 |
+parser.add_option('-l', |
|
43 |
+ '--logfile', |
|
44 |
+ action="store", |
|
45 |
+ type="string", |
|
46 |
+ dest="logfile", |
|
47 |
+ help="The logfile path", |
|
48 |
+ default=None) |
|
49 |
+ |
|
50 |
+parser.add_option('-H', |
|
51 |
+ '--address', |
|
52 |
+ action="store", |
|
53 |
+ type="string", |
|
54 |
+ dest="bind_address", |
|
55 |
+ help="The bind address", |
|
56 |
+ default="127.0.0.1") |
|
57 |
+ |
|
58 |
+parser.add_option('-p', |
|
59 |
+ '--port', |
|
60 |
+ action="store", |
|
61 |
+ type="int", |
|
62 |
+ dest="port", |
|
63 |
+ help="The bind port", |
|
64 |
+ default=1025) |
|
65 |
+ |
|
66 |
+(options, args) = parser.parse_args() |
|
67 |
+ |
|
68 |
+ |
|
69 |
+logformat = '%(asctime)s - MantisMailServer - %(levelname)s - %(message)s' |
|
70 |
+if options.verbose: |
|
71 |
+ loglevel = logging.DEBUG |
|
72 |
+else: |
|
73 |
+ loglevel = logging.INFO |
|
74 |
+ |
|
75 |
+if options.logfile: |
|
76 |
+ logging.basicConfig(filename=options.logfile,level=loglevel,format=logformat) |
|
77 |
+else: |
|
78 |
+ logging.basicConfig(level=loglevel,format=logformat) |
|
79 |
+ |
|
80 |
+server = MantisSMTPServer.MantisSMTPServer((options.bind_address, options.port), None) |
|
81 |
+ |
|
82 |
+ |
|
83 |
+if options.daemon and options.pidfile is None: |
|
84 |
+ logging.error("Can't daemonize without pid file parameter (--pid)") |
|
85 |
+ sys.exit(3) |
|
86 |
+ |
|
87 |
+if options.configfile: |
|
88 |
+ try: |
|
89 |
+ fd=open(options.configfile,'rb') |
|
90 |
+ config_text=fd.read() |
|
91 |
+ fd.close() |
|
92 |
+ except: |
|
93 |
+ logging.error('Error reading config file %s.' % options.configfile) |
|
94 |
+ sys.exit(1) |
|
95 |
+ |
|
96 |
+ try: |
|
97 |
+ config=json.loads(config_text) |
|
98 |
+ except: |
|
99 |
+ logging.error('Error parsing config file %s.' % options.configfile) |
|
100 |
+ sys.exit(2) |
|
101 |
+ |
|
102 |
+ try: |
|
103 |
+ for param in ['mail_from','ws_url','ws_user','ws_pwd','ws_default_category']: |
|
104 |
+ if param in config: |
|
105 |
+ server.set_option(param,config[param]) |
|
106 |
+ |
|
107 |
+ if 'projects' in config: |
|
108 |
+ for project in config['projects']: |
|
109 |
+ if 'project_name' not in config['projects'][project]: |
|
110 |
+ logging.warning('Project %s in config file does not have "project_name" parameter. Pass.' % project) |
|
111 |
+ continue |
|
112 |
+ if 'category' in config['projects'][project]: |
|
113 |
+ server.add_project(project,config['projects'][project]['project_name'],category=config['projects'][project]['category']) |
|
114 |
+ else: |
|
115 |
+ server.add_project(project,config['projects'][project]['project_name']) |
|
116 |
+ except: |
|
117 |
+ logging.error('Error loading config parameters from %s' % options.configfile) |
|
118 |
+ sys.exit(3) |
|
119 |
+else: |
|
120 |
+ logging.warning('No config file supply. Running with default configuration...') |
|
121 |
+ |
|
122 |
+try: |
|
123 |
+ if options.daemon: |
|
124 |
+ server.startDaemon(options.pidfile) |
|
125 |
+ else: |
|
126 |
+ server.start() |
|
127 |
+except KeyboardInterrupt: |
|
128 |
+ server.stop() |
... | ... |
@@ -0,0 +1,70 @@ |
1 |
+#!/usr/bin/python |
|
2 |
+ |
|
3 |
+import email |
|
4 |
+import re |
|
5 |
+import string |
|
6 |
+from email.MIMEText import MIMEText |
|
7 |
+import smtplib |
|
8 |
+import logging |
|
9 |
+ |
|
10 |
+ |
|
11 |
+class MantisMail(object): |
|
12 |
+ |
|
13 |
+ def __init__(self,mailfrom,mailto,data): |
|
14 |
+ self.mailfrom = mailfrom |
|
15 |
+ self.mailto = mailto |
|
16 |
+ self.msg = email.message_from_string(data) |
|
17 |
+ |
|
18 |
+ def get_issue_id_from_subject(self): |
|
19 |
+ res=re.search('\[[^\]]* ([0-9]*)\]',self.subject()) |
|
20 |
+ if res: |
|
21 |
+ issue_id=res.group(1) |
|
22 |
+ return string.atoi(issue_id) |
|
23 |
+ return None |
|
24 |
+ |
|
25 |
+ def subject(self): |
|
26 |
+ return self.msg['Subject'] |
|
27 |
+ |
|
28 |
+ def get_text(self): |
|
29 |
+ content='From : %s' % self.msg['From'] |
|
30 |
+ for part in self.msg.walk(): |
|
31 |
+ if part.get_content_maintype() == 'text': |
|
32 |
+ content='%s\n\n---------------------------\n\n%s' % (content,part.get_payload(decode=True)) |
|
33 |
+ return content |
|
34 |
+ |
|
35 |
+ def get_attachments(self): |
|
36 |
+ attachments=[] |
|
37 |
+ counter=1 |
|
38 |
+ for part in self.msg.walk(): |
|
39 |
+ part_type = part.get_content_maintype() |
|
40 |
+ if part_type == 'text' or part_type == 'multipart': |
|
41 |
+ continue |
|
42 |
+ filename = part.get_filename() |
|
43 |
+ if not filename: |
|
44 |
+ ext = mimetypes.guess_extension(part.get_content_type()) |
|
45 |
+ if not ext: |
|
46 |
+ ext = '.bin' |
|
47 |
+ filename = 'part-%03d%s' % (counter, ext) |
|
48 |
+ counter += 1 |
|
49 |
+ content=part.get_payload(decode=False) |
|
50 |
+ attachments.append({ |
|
51 |
+ 'filename': filename, |
|
52 |
+ 'type': part_type, |
|
53 |
+ 'content': content |
|
54 |
+ }) |
|
55 |
+ return attachments |
|
56 |
+ |
|
57 |
+def send(mail_from,to,subject,content,smtp_host='127.0.0.1',smtp_port=25): |
|
58 |
+ msg = MIMEText(content) |
|
59 |
+ msg['To'] = to |
|
60 |
+ msg['From'] = mail_from |
|
61 |
+ msg['Subject'] = subject |
|
62 |
+ |
|
63 |
+ server = smtplib.SMTP(smtp_host,smtp_port) |
|
64 |
+ |
|
65 |
+ try: |
|
66 |
+ server.sendmail(mail_from, [to], msg.as_string()) |
|
67 |
+ except: |
|
68 |
+ logging.error('Error sending mail to %s - %s : %s' % (to,subject,sys.exc_info()[0])) |
|
69 |
+ return |
|
70 |
+ |
... | ... |
@@ -0,0 +1,165 @@ |
1 |
+#!/usr/bin/python |
|
2 |
+ |
|
3 |
+import smtpd |
|
4 |
+import logging |
|
5 |
+import sys |
|
6 |
+import smtplib |
|
7 |
+import MantisMail |
|
8 |
+import MantisWS |
|
9 |
+import asyncore |
|
10 |
+import os |
|
11 |
+import signal |
|
12 |
+ |
|
13 |
+class MantisSMTPServer(smtpd.SMTPServer): |
|
14 |
+ |
|
15 |
+ _mail2project = {} |
|
16 |
+ |
|
17 |
+ _options={} |
|
18 |
+ _options['mail_from']='postmaster-mantis@example.net' |
|
19 |
+ _options['smtp_host']='127.0.0.1' |
|
20 |
+ _options['smtp_port']='25' |
|
21 |
+ _options['ws_url']='http://mantis.example.net/api/soap/mantisconnect.php?wsdl' |
|
22 |
+ _options['ws_user']='mantis' |
|
23 |
+ _options['ws_pwd']='password' |
|
24 |
+ _options['ws_default_category']='other' |
|
25 |
+ |
|
26 |
+ ws=None |
|
27 |
+ |
|
28 |
+ _pidfile = None |
|
29 |
+ |
|
30 |
+ def start(self): |
|
31 |
+ logging.info('Start Mantis SMTP Server') |
|
32 |
+ self._start() |
|
33 |
+ |
|
34 |
+ def startDaemon(self,pidfile): |
|
35 |
+ self._pidfile=pidfile |
|
36 |
+ try: |
|
37 |
+ pid = os.fork() |
|
38 |
+ if pid > 0: |
|
39 |
+ # Exit parent |
|
40 |
+ sys.exit(0) |
|
41 |
+ except OSError, e: |
|
42 |
+ sys.stderr.write("Daemonize failed: %d (%s)\n" % (e.errno, e.strerror)) |
|
43 |
+ sys.exit(1) |
|
44 |
+ |
|
45 |
+ logging.info('Start Mantis SMTP Server as daemon') |
|
46 |
+ pid = str(os.getpid()) |
|
47 |
+ file(self._pidfile,'w+').write("%s\n" % pid) |
|
48 |
+ signal.signal(signal.SIGTERM,self.stopDaemon) |
|
49 |
+ self._start() |
|
50 |
+ |
|
51 |
+ def stopDaemon(self,signum, frame): |
|
52 |
+ logging.info('Stop Mantis SMTP Server by SIGTERM') |
|
53 |
+ os.remove(self._pidfile) |
|
54 |
+ sys.exit(0) |
|
55 |
+ |
|
56 |
+ def _start(self): |
|
57 |
+ asyncore.loop() |
|
58 |
+ |
|
59 |
+ def stop(self): |
|
60 |
+ logging.info('Stop Mantis SMTP Server') |
|
61 |
+ |
|
62 |
+ def process_message(self, peer, mailfrom, rcpttos, data): |
|
63 |
+ |
|
64 |
+ logging.info('Message receive from %s, send by %s to %s' % (peer,mailfrom,rcpttos)) |
|
65 |
+ |
|
66 |
+ try: |
|
67 |
+ msg = MantisMail.MantisMail(mailfrom,rcpttos,data) |
|
68 |
+ except: |
|
69 |
+ logging.error('Error parsing message receive from %s, send by %s to %s : %s' % (peer,mailfrom,rcpttos,sys.exc_info()[0])) |
|
70 |
+ return |
|
71 |
+ |
|
72 |
+ issue_id=msg.get_issue_id_from_subject() |
|
73 |
+ |
|
74 |
+ if issue_id: |
|
75 |
+ logging.info("Answer to issue #%s" % issue_id) |
|
76 |
+ self.add_note_to_issue_from_msg(msg) |
|
77 |
+ else: |
|
78 |
+ project_infos=self.get_mantis_project_infos_from_recipient(rcpttos) |
|
79 |
+ if project_infos is None: |
|
80 |
+ logging.warning("Unknown recipient : Can't determine project to create issue. This email can't be processed. Warning message will be send to sender.") |
|
81 |
+ MantisMail.send( |
|
82 |
+ self._options['mail_from'], |
|
83 |
+ msg.mailfrom, |
|
84 |
+ '[Mantis] Unknown recipient %s' % rcpttos[0], |
|
85 |
+ "Your email can't be inserted in Mantis because the recipient address of your message can't be recognized.\n\nPlease call your system administrator.", |
|
86 |
+ smtp_host=self._options['smtp_host'], |
|
87 |
+ smtp_port=self._options['smtp_port'] |
|
88 |
+ ) |
|
89 |
+ else: |
|
90 |
+ project=project_infos['project'] |
|
91 |
+ logging.info("New issue for project %s" % project) |
|
92 |
+ self.add_new_issue_to_project_from_msg(project_infos,msg) |
|
93 |
+ |
|
94 |
+ return |
|
95 |
+ |
|
96 |
+ def add_project(self,email,project,category=None): |
|
97 |
+ self._mail2project[email]={ |
|
98 |
+ 'project': project |
|
99 |
+ } |
|
100 |
+ if category is not None: |
|
101 |
+ self._mail2project[email]['category']=category |
|
102 |
+ |
|
103 |
+ def get_mantis_project_infos_from_recipient(self,tos): |
|
104 |
+ for to in tos: |
|
105 |
+ if to in self._mail2project: |
|
106 |
+ return self._mail2project[to] |
|
107 |
+ return None |
|
108 |
+ |
|
109 |
+ |
|
110 |
+ def set_option(self,option,value): |
|
111 |
+ if option in self._options: |
|
112 |
+ self._options[option]=value |
|
113 |
+ |
|
114 |
+ def start_ws(self): |
|
115 |
+ self.ws=MantisWS.MantisWS(self._options['ws_url'],self._options['ws_user'],self._options['ws_pwd']) |
|
116 |
+ |
|
117 |
+ self.ws.set_option('default_category',self._options['ws_default_category']) |
|
118 |
+ |
|
119 |
+ for option in ['mail_from','smtp_host','smtp_port']: |
|
120 |
+ self.ws.set_option(option,self._options[option]) |
|
121 |
+ |
|
122 |
+ |
|
123 |
+ def add_new_issue_to_project_from_msg(self,project_infos,msg): |
|
124 |
+ if self.ws is None: |
|
125 |
+ self.start_ws() |
|
126 |
+ issue_id = self.ws.add_issue_to_project(msg.mailfrom,project_infos,msg.subject(),msg.get_text()) |
|
127 |
+ if issue_id: |
|
128 |
+ logging.info('Issue added with id %s' % issue_id) |
|
129 |
+ return issue_id |
|
130 |
+ else: |
|
131 |
+ logging.error('Error during add issue') |
|
132 |
+ |
|
133 |
+ def add_note_to_issue_from_msg(self,msg): |
|
134 |
+ if self.ws is None: |
|
135 |
+ self.start_ws() |
|
136 |
+ |
|
137 |
+ issue_id=msg.get_issue_id_from_subject() |
|
138 |
+ |
|
139 |
+ note_id = self.ws.add_note_to_issue(issue_id,msg.get_text()) |
|
140 |
+ if note_id: |
|
141 |
+ logging.info('Note add to issue %s with id %s' % (issue_id,note_id)) |
|
142 |
+ |
|
143 |
+ for attachment in msg.get_attachments(): |
|
144 |
+ try: |
|
145 |
+ logging.info('Add attachment to issue %s : %s' % (issue_id,attachment['filename'])) |
|
146 |
+ self.ws.add_attachment_to_issue(issue_id,attachment) |
|
147 |
+ except MantisWS.MantisWSError, e: |
|
148 |
+ if e.type == 'duplicate_attachment_filename': |
|
149 |
+ logging.error('Duplicate filename %s in issue %s. Warning sender by mail.' % (attachment['filename'],issue_id)) |
|
150 |
+ MantisMail.send( |
|
151 |
+ self._options['mail_from'], |
|
152 |
+ msg.mailfrom, |
|
153 |
+ '[Mantis] Duplicate filename %s in issue %s' % (attachment['filename'],issue_id), |
|
154 |
+ "Your file %s attach in your email '%s' can't be attached to Mantis issue %s because a file with the same name is currently attached to this issue." % (attachment['filename'],msg.subject(),issue_id), |
|
155 |
+ smtp_host=self._options['smtp_host'], |
|
156 |
+ smtp_port=self._options['smtp_port'] |
|
157 |
+ ) |
|
158 |
+ |
|
159 |
+ except: |
|
160 |
+ logging.error('Error adding attachment %s to issue %s' % (attachment['filename'],issue_id)) |
|
161 |
+# |
|
162 |
+ |
|
163 |
+ return issue_id |
|
164 |
+ else: |
|
165 |
+ logging.error('Error during add issue') |
... | ... |
@@ -0,0 +1,84 @@ |
1 |
+#!/usr/bin/python |
|
2 |
+ |
|
3 |
+import suds |
|
4 |
+import logging |
|
5 |
+import re |
|
6 |
+import MantisMail |
|
7 |
+ |
|
8 |
+class MantisWS(object): |
|
9 |
+ |
|
10 |
+ _options = {} |
|
11 |
+ _options['default_category']='other' |
|
12 |
+ _options['mail_from']='postmaster-mantis@example.net' |
|
13 |
+ _options['smtp_host']='127.0.0.1' |
|
14 |
+ _options['smtp_port']='25' |
|
15 |
+ |
|
16 |
+ def __init__(self,url,user,password): |
|
17 |
+ self.url = url |
|
18 |
+ self.user = user |
|
19 |
+ self.password = password |
|
20 |
+ |
|
21 |
+ try: |
|
22 |
+ self.client = suds.client.Client(url) |
|
23 |
+ except: |
|
24 |
+ logging.error('Error connecting to Mantis WS with URL %s' % url) |
|
25 |
+ |
|
26 |
+ def add_issue_to_project(self,reporter_email,project_infos,summary,description): |
|
27 |
+ issue = self.client.factory.create('IssueData') |
|
28 |
+ issue.reporter.email = reporter_email |
|
29 |
+ issue.project.name = project_infos['project'] |
|
30 |
+ issue.project.id = 0 # Auto-detect |
|
31 |
+ |
|
32 |
+ if 'category' in project_infos: |
|
33 |
+ issue.category = project_infos['category'] |
|
34 |
+ else: |
|
35 |
+ issue.category = self._options['default_category'] |
|
36 |
+ issue.summary = summary |
|
37 |
+ issue.description = description |
|
38 |
+ try: |
|
39 |
+ res = self.client.service.mc_issue_add(self.user,self.password,issue) |
|
40 |
+ return res |
|
41 |
+ except: |
|
42 |
+ logging.error('Problem adding issue') |
|
43 |
+ return None |
|
44 |
+ |
|
45 |
+ def add_note_to_issue(self,issue_id,text): |
|
46 |
+ note = self.client.factory.create('IssueNoteData') |
|
47 |
+ note.text=text |
|
48 |
+ |
|
49 |
+ try: |
|
50 |
+ res = self.client.service.mc_issue_note_add(self.user,self.password,issue_id,note) |
|
51 |
+ return res |
|
52 |
+ except: |
|
53 |
+ logging.error('Problem adding note to issue %s' % issue_id) |
|
54 |
+ return None |
|
55 |
+ def add_attachment_to_issue(self,issue_id,attachment): |
|
56 |
+ try: |
|
57 |
+ res = self.client.service.mc_issue_attachment_add( |
|
58 |
+ self.user, |
|
59 |
+ self.password, |
|
60 |
+ issue_id, |
|
61 |
+ attachment['filename'], |
|
62 |
+ attachment['type'], |
|
63 |
+ attachment['content'] |
|
64 |
+ ) |
|
65 |
+ return res |
|
66 |
+ except suds.WebFault, e: |
|
67 |
+ logging.error('Problem adding attachment %s to issue %s : %s' % (attachment['filename'],issue_id,e)) |
|
68 |
+ err=str(e) |
|
69 |
+ if re.search('Duplicate filename',err): |
|
70 |
+ raise MantisWSError('duplicate_attachment_filename','Duplicate attachment filename %s in issue %s' % (attachment['filename'],issue_id)) |
|
71 |
+ except: |
|
72 |
+ logging.error('Problem adding attachment %s to issue %s' % (attachment['filename'],issue_id)) |
|
73 |
+ return None |
|
74 |
+ |
|
75 |
+ def set_option(self,option,value): |
|
76 |
+ if option in self._options: |
|
77 |
+ self._options[option]=value |
|
78 |
+ |
|
79 |
+class MantisWSError(Exception): |
|
80 |
+ |
|
81 |
+ def __init__(self,type,msg): |
|
82 |
+ Exception.__init__(self, msg) |
|
83 |
+ self.type = type |
|
84 |
+ |
|
0 | 85 |