##// END OF EJS Templates
Adds an option to rdm-mailhandler for reading the API key from a file (#4976)....
Jean-Philippe Lang -
r7834:d3a25649649a
parent child
Show More
@@ -1,175 +1,187
1 #!/usr/bin/env ruby
1 #!/usr/bin/env ruby
2
2
3 # == Synopsis
3 # == Synopsis
4 #
4 #
5 # Reads an email from standard input and forward it to a Redmine server
5 # Reads an email from standard input and forward it to a Redmine server
6 # through a HTTP request.
6 # through a HTTP request.
7 #
7 #
8 # == Usage
8 # == Usage
9 #
9 #
10 # rdm-mailhandler [options] --url=<Redmine URL> --key=<API key>
10 # rdm-mailhandler [options] --url=<Redmine URL> --key=<API key>
11 #
11 #
12 # == Arguments
12 # == Arguments
13 #
13 #
14 # -u, --url URL of the Redmine server
14 # -u, --url URL of the Redmine server
15 # -k, --key Redmine API key
15 # -k, --key Redmine API key
16 #
16 #
17 # General options:
17 # General options:
18 # --unknown-user=ACTION how to handle emails from an unknown user
18 # --unknown-user=ACTION how to handle emails from an unknown user
19 # ACTION can be one of the following values:
19 # ACTION can be one of the following values:
20 # ignore: email is ignored (default)
20 # ignore: email is ignored (default)
21 # accept: accept as anonymous user
21 # accept: accept as anonymous user
22 # create: create a user account
22 # create: create a user account
23 # --no-permission-check disable permission checking when receiving
23 # --no-permission-check disable permission checking when receiving
24 # the email
24 # the email
25 # --key-file=PATH path to a file that contains the Redmine
26 # API key (use this option instead of --key
27 # if you don't the key to appear in the
28 # command line)
25 # --no-check-certificate do not check server certificate
29 # --no-check-certificate do not check server certificate
26 # -h, --help show this help
30 # -h, --help show this help
27 # -v, --verbose show extra information
31 # -v, --verbose show extra information
28 # -V, --version show version information and exit
32 # -V, --version show version information and exit
29 #
33 #
30 # Issue attributes control options:
34 # Issue attributes control options:
31 # -p, --project=PROJECT identifier of the target project
35 # -p, --project=PROJECT identifier of the target project
32 # -s, --status=STATUS name of the target status
36 # -s, --status=STATUS name of the target status
33 # -t, --tracker=TRACKER name of the target tracker
37 # -t, --tracker=TRACKER name of the target tracker
34 # --category=CATEGORY name of the target category
38 # --category=CATEGORY name of the target category
35 # --priority=PRIORITY name of the target priority
39 # --priority=PRIORITY name of the target priority
36 # -o, --allow-override=ATTRS allow email content to override attributes
40 # -o, --allow-override=ATTRS allow email content to override attributes
37 # specified by previous options
41 # specified by previous options
38 # ATTRS is a comma separated list of attributes
42 # ATTRS is a comma separated list of attributes
39 #
43 #
40 # == Examples
44 # == Examples
41 # No project specified. Emails MUST contain the 'Project' keyword:
45 # No project specified. Emails MUST contain the 'Project' keyword:
42 #
46 #
43 # rdm-mailhandler --url http://redmine.domain.foo --key secret
47 # rdm-mailhandler --url http://redmine.domain.foo --key secret
44 #
48 #
45 # Fixed project and default tracker specified, but emails can override
49 # Fixed project and default tracker specified, but emails can override
46 # both tracker and priority attributes using keywords:
50 # both tracker and priority attributes using keywords:
47 #
51 #
48 # rdm-mailhandler --url https://domain.foo/redmine --key secret \\
52 # rdm-mailhandler --url https://domain.foo/redmine --key secret \\
49 # --project foo \\
53 # --project foo \\
50 # --tracker bug \\
54 # --tracker bug \\
51 # --allow-override tracker,priority
55 # --allow-override tracker,priority
52
56
53 require 'net/http'
57 require 'net/http'
54 require 'net/https'
58 require 'net/https'
55 require 'uri'
59 require 'uri'
56 require 'getoptlong'
60 require 'getoptlong'
57 require 'rdoc/usage'
61 require 'rdoc/usage'
58
62
59 module Net
63 module Net
60 class HTTPS < HTTP
64 class HTTPS < HTTP
61 def self.post_form(url, params, headers, options={})
65 def self.post_form(url, params, headers, options={})
62 request = Post.new(url.path)
66 request = Post.new(url.path)
63 request.form_data = params
67 request.form_data = params
64 request.basic_auth url.user, url.password if url.user
68 request.basic_auth url.user, url.password if url.user
65 request.initialize_http_header(headers)
69 request.initialize_http_header(headers)
66 http = new(url.host, url.port)
70 http = new(url.host, url.port)
67 http.use_ssl = (url.scheme == 'https')
71 http.use_ssl = (url.scheme == 'https')
68 if options[:no_check_certificate]
72 if options[:no_check_certificate]
69 http.verify_mode = OpenSSL::SSL::VERIFY_NONE
73 http.verify_mode = OpenSSL::SSL::VERIFY_NONE
70 end
74 end
71 http.start {|h| h.request(request) }
75 http.start {|h| h.request(request) }
72 end
76 end
73 end
77 end
74 end
78 end
75
79
76 class RedmineMailHandler
80 class RedmineMailHandler
77 VERSION = '0.1'
81 VERSION = '0.1'
78
82
79 attr_accessor :verbose, :issue_attributes, :allow_override, :unknown_user, :no_permission_check, :url, :key, :no_check_certificate
83 attr_accessor :verbose, :issue_attributes, :allow_override, :unknown_user, :no_permission_check, :url, :key, :no_check_certificate
80
84
81 def initialize
85 def initialize
82 self.issue_attributes = {}
86 self.issue_attributes = {}
83
87
84 opts = GetoptLong.new(
88 opts = GetoptLong.new(
85 [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
89 [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
86 [ '--version', '-V', GetoptLong::NO_ARGUMENT ],
90 [ '--version', '-V', GetoptLong::NO_ARGUMENT ],
87 [ '--verbose', '-v', GetoptLong::NO_ARGUMENT ],
91 [ '--verbose', '-v', GetoptLong::NO_ARGUMENT ],
88 [ '--url', '-u', GetoptLong::REQUIRED_ARGUMENT ],
92 [ '--url', '-u', GetoptLong::REQUIRED_ARGUMENT ],
89 [ '--key', '-k', GetoptLong::REQUIRED_ARGUMENT],
93 [ '--key', '-k', GetoptLong::REQUIRED_ARGUMENT],
94 [ '--key-file', GetoptLong::REQUIRED_ARGUMENT],
90 [ '--project', '-p', GetoptLong::REQUIRED_ARGUMENT ],
95 [ '--project', '-p', GetoptLong::REQUIRED_ARGUMENT ],
91 [ '--status', '-s', GetoptLong::REQUIRED_ARGUMENT ],
96 [ '--status', '-s', GetoptLong::REQUIRED_ARGUMENT ],
92 [ '--tracker', '-t', GetoptLong::REQUIRED_ARGUMENT],
97 [ '--tracker', '-t', GetoptLong::REQUIRED_ARGUMENT],
93 [ '--category', GetoptLong::REQUIRED_ARGUMENT],
98 [ '--category', GetoptLong::REQUIRED_ARGUMENT],
94 [ '--priority', GetoptLong::REQUIRED_ARGUMENT],
99 [ '--priority', GetoptLong::REQUIRED_ARGUMENT],
95 [ '--allow-override', '-o', GetoptLong::REQUIRED_ARGUMENT],
100 [ '--allow-override', '-o', GetoptLong::REQUIRED_ARGUMENT],
96 [ '--unknown-user', GetoptLong::REQUIRED_ARGUMENT],
101 [ '--unknown-user', GetoptLong::REQUIRED_ARGUMENT],
97 [ '--no-permission-check', GetoptLong::NO_ARGUMENT],
102 [ '--no-permission-check', GetoptLong::NO_ARGUMENT],
98 [ '--no-check-certificate', GetoptLong::NO_ARGUMENT]
103 [ '--no-check-certificate', GetoptLong::NO_ARGUMENT]
99 )
104 )
100
105
101 opts.each do |opt, arg|
106 opts.each do |opt, arg|
102 case opt
107 case opt
103 when '--url'
108 when '--url'
104 self.url = arg.dup
109 self.url = arg.dup
105 when '--key'
110 when '--key'
106 self.key = arg.dup
111 self.key = arg.dup
112 when '--key-file'
113 begin
114 self.key = File.read(arg).strip
115 rescue Exception => e
116 $stderr.puts "Unable to read the key from #{arg}: #{e.message}"
117 exit 1
118 end
107 when '--help'
119 when '--help'
108 usage
120 usage
109 when '--verbose'
121 when '--verbose'
110 self.verbose = true
122 self.verbose = true
111 when '--version'
123 when '--version'
112 puts VERSION; exit
124 puts VERSION; exit
113 when '--project', '--status', '--tracker', '--category', '--priority'
125 when '--project', '--status', '--tracker', '--category', '--priority'
114 self.issue_attributes[opt.gsub(%r{^\-\-}, '')] = arg.dup
126 self.issue_attributes[opt.gsub(%r{^\-\-}, '')] = arg.dup
115 when '--allow-override'
127 when '--allow-override'
116 self.allow_override = arg.dup
128 self.allow_override = arg.dup
117 when '--unknown-user'
129 when '--unknown-user'
118 self.unknown_user = arg.dup
130 self.unknown_user = arg.dup
119 when '--no-permission-check'
131 when '--no-permission-check'
120 self.no_permission_check = '1'
132 self.no_permission_check = '1'
121 when '--no-check-certificate'
133 when '--no-check-certificate'
122 self.no_check_certificate = true
134 self.no_check_certificate = true
123 end
135 end
124 end
136 end
125
137
126 RDoc.usage if url.nil?
138 RDoc.usage if url.nil?
127 end
139 end
128
140
129 def submit(email)
141 def submit(email)
130 uri = url.gsub(%r{/*$}, '') + '/mail_handler'
142 uri = url.gsub(%r{/*$}, '') + '/mail_handler'
131
143
132 headers = { 'User-Agent' => "Redmine mail handler/#{VERSION}" }
144 headers = { 'User-Agent' => "Redmine mail handler/#{VERSION}" }
133
145
134 data = { 'key' => key, 'email' => email,
146 data = { 'key' => key, 'email' => email,
135 'allow_override' => allow_override,
147 'allow_override' => allow_override,
136 'unknown_user' => unknown_user,
148 'unknown_user' => unknown_user,
137 'no_permission_check' => no_permission_check}
149 'no_permission_check' => no_permission_check}
138 issue_attributes.each { |attr, value| data["issue[#{attr}]"] = value }
150 issue_attributes.each { |attr, value| data["issue[#{attr}]"] = value }
139
151
140 debug "Posting to #{uri}..."
152 debug "Posting to #{uri}..."
141 response = Net::HTTPS.post_form(URI.parse(uri), data, headers, :no_check_certificate => no_check_certificate)
153 response = Net::HTTPS.post_form(URI.parse(uri), data, headers, :no_check_certificate => no_check_certificate)
142 debug "Response received: #{response.code}"
154 debug "Response received: #{response.code}"
143
155
144 case response.code.to_i
156 case response.code.to_i
145 when 403
157 when 403
146 warn "Request was denied by your Redmine server. " +
158 warn "Request was denied by your Redmine server. " +
147 "Make sure that 'WS for incoming emails' is enabled in application settings and that you provided the correct API key."
159 "Make sure that 'WS for incoming emails' is enabled in application settings and that you provided the correct API key."
148 return 77
160 return 77
149 when 422
161 when 422
150 warn "Request was denied by your Redmine server. " +
162 warn "Request was denied by your Redmine server. " +
151 "Possible reasons: email is sent from an invalid email address or is missing some information."
163 "Possible reasons: email is sent from an invalid email address or is missing some information."
152 return 77
164 return 77
153 when 400..499
165 when 400..499
154 warn "Request was denied by your Redmine server (#{response.code})."
166 warn "Request was denied by your Redmine server (#{response.code})."
155 return 77
167 return 77
156 when 500..599
168 when 500..599
157 warn "Failed to contact your Redmine server (#{response.code})."
169 warn "Failed to contact your Redmine server (#{response.code})."
158 return 75
170 return 75
159 when 201
171 when 201
160 debug "Proccessed successfully"
172 debug "Proccessed successfully"
161 return 0
173 return 0
162 else
174 else
163 return 1
175 return 1
164 end
176 end
165 end
177 end
166
178
167 private
179 private
168
180
169 def debug(msg)
181 def debug(msg)
170 puts msg if verbose
182 puts msg if verbose
171 end
183 end
172 end
184 end
173
185
174 handler = RedmineMailHandler.new
186 handler = RedmineMailHandler.new
175 exit(handler.submit(STDIN.read))
187 exit(handler.submit(STDIN.read))
General Comments 0
You need to be logged in to leave comments. Login now