##// END OF EJS Templates
rdm-mailhandler with project-from-subaddress fails (#21531)....
Jean-Philippe Lang -
r14604:cf59a9b62e44
parent child
Show More
@@ -1,212 +1,212
1 1 #!/usr/bin/env ruby
2 2 # Redmine - project management software
3 3 # Copyright (C) 2006-2015 Jean-Philippe Lang
4 4 #
5 5 # This program is free software; you can redistribute it and/or
6 6 # modify it under the terms of the GNU General Public License
7 7 # as published by the Free Software Foundation; either version 2
8 8 # of the License, or (at your option) any later version.
9 9 #
10 10 # This program is distributed in the hope that it will be useful,
11 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 13 # GNU General Public License for more details.
14 14 #
15 15 # You should have received a copy of the GNU General Public License
16 16 # along with this program; if not, write to the Free Software
17 17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 18
19 19 require 'net/http'
20 20 require 'net/https'
21 21 require 'uri'
22 22 require 'optparse'
23 23
24 24 module Net
25 25 class HTTPS < HTTP
26 26 def self.post_form(url, params, headers, options={})
27 27 request = Post.new(url.path)
28 28 request.form_data = params
29 29 request.initialize_http_header(headers)
30 30 request.basic_auth url.user, url.password if url.user
31 31 http = new(url.host, url.port)
32 32 http.use_ssl = (url.scheme == 'https')
33 33 if options[:certificate_bundle]
34 34 http.ca_file = options[:certificate_bundle]
35 35 end
36 36 if options[:no_check_certificate]
37 37 http.verify_mode = OpenSSL::SSL::VERIFY_NONE
38 38 end
39 39 http.start {|h| h.request(request) }
40 40 end
41 41 end
42 42 end
43 43
44 44 class RedmineMailHandler
45 45 VERSION = '0.2.3'
46 46
47 47 attr_accessor :verbose, :issue_attributes, :allow_override, :unknown_user, :default_group, :no_permission_check,
48 48 :url, :key, :no_check_certificate, :certificate_bundle, :no_account_notice, :no_notification, :project_from_subaddress
49 49
50 50 def initialize
51 51 self.issue_attributes = {}
52 52
53 53 optparse = OptionParser.new do |opts|
54 54 opts.banner = "Usage: rdm-mailhandler.rb [options] --url=<Redmine URL> --key=<API key>"
55 55 opts.separator("")
56 56 opts.separator("Reads an email from standard input and forwards it to a Redmine server through a HTTP request.")
57 57 opts.separator("")
58 58 opts.separator("Required arguments:")
59 59 opts.on("-u", "--url URL", "URL of the Redmine server") {|v| self.url = v}
60 60 opts.on("-k", "--key KEY", "Redmine API key") {|v| self.key = v}
61 61 opts.separator("")
62 62 opts.separator("General options:")
63 63 opts.on("--key-file FILE", "full path to a file that contains your Redmine",
64 64 "API key (use this option instead of --key if",
65 65 "you don't want the key to appear in the command",
66 66 "line)") {|v| read_key_from_file(v)}
67 67 opts.on("--no-check-certificate", "do not check server certificate") {self.no_check_certificate = true}
68 68 opts.on("--certificate-bundle FILE", "certificate bundle to use") {|v| self.certificate_bundle = v}
69 69 opts.on("-h", "--help", "show this help") {puts opts; exit 1}
70 70 opts.on("-v", "--verbose", "show extra information") {self.verbose = true}
71 71 opts.on("-V", "--version", "show version information and exit") {puts VERSION; exit}
72 72 opts.separator("")
73 73 opts.separator("User and permissions options:")
74 74 opts.on("--unknown-user ACTION", "how to handle emails from an unknown user",
75 75 "ACTION can be one of the following values:",
76 76 "* ignore: email is ignored (default)",
77 77 "* accept: accept as anonymous user",
78 78 "* create: create a user account") {|v| self.unknown_user = v}
79 79 opts.on("--no-permission-check", "disable permission checking when receiving",
80 80 "the email") {self.no_permission_check = '1'}
81 81 opts.on("--default-group GROUP", "add created user to GROUP (none by default)",
82 82 "GROUP can be a comma separated list of groups") { |v| self.default_group = v}
83 83 opts.on("--no-account-notice", "don't send account information to the newly",
84 84 "created user") { |v| self.no_account_notice = '1'}
85 85 opts.on("--no-notification", "disable email notifications for the created",
86 86 "user") { |v| self.no_notification = '1'}
87 87 opts.separator("")
88 88 opts.separator("Issue attributes control options:")
89 89 opts.on( "--project-from-subaddress ADDR", "select project from subadress of ADDR found",
90 "in To, Cc, Bcc headers") {|v| self.project_from_subaddress['project'] = v}
90 "in To, Cc, Bcc headers") {|v| self.project_from_subaddress = v}
91 91 opts.on("-p", "--project PROJECT", "identifier of the target project") {|v| self.issue_attributes['project'] = v}
92 92 opts.on("-s", "--status STATUS", "name of the target status") {|v| self.issue_attributes['status'] = v}
93 93 opts.on("-t", "--tracker TRACKER", "name of the target tracker") {|v| self.issue_attributes['tracker'] = v}
94 94 opts.on( "--category CATEGORY", "name of the target category") {|v| self.issue_attributes['category'] = v}
95 95 opts.on( "--priority PRIORITY", "name of the target priority") {|v| self.issue_attributes['priority'] = v}
96 96 opts.on( "--fixed-version VERSION","name of the target version") {|v| self.issue_attributes['fixed_version'] = v}
97 97 opts.on( "--private", "create new issues as private") {|v| self.issue_attributes['is_private'] = '1'}
98 98 opts.on("-o", "--allow-override ATTRS", "allow email content to set attributes values",
99 99 "ATTRS is a comma separated list of attributes",
100 100 "or 'all' to allow all attributes to be",
101 101 "overridable (see below for details)") {|v| self.allow_override = v}
102 102
103 103 opts.separator <<-END_DESC
104 104
105 105 Overrides:
106 106 ATTRS is a comma separated list of attributes among:
107 107 * project, tracker, status, priority, category, assigned_to, fixed_version,
108 108 start_date, due_date, estimated_hours, done_ratio
109 109 * custom fields names with underscores instead of spaces (case insensitive)
110 110 Example: --allow_override=project,priority,my_custom_field
111 111
112 112 If the --project option is not set, project is overridable by default for
113 113 emails that create new issues.
114 114
115 115 You can use --allow_override=all to allow all attributes to be overridable.
116 116
117 117 Examples:
118 118 No project specified, emails MUST contain the 'Project' keyword, otherwise
119 119 they will be dropped (not recommanded):
120 120
121 121 rdm-mailhandler.rb --url http://redmine.domain.foo --key secret
122 122
123 123 Fixed project and default tracker specified, but emails can override
124 124 both tracker and priority attributes using keywords:
125 125
126 126 rdm-mailhandler.rb --url https://domain.foo/redmine --key secret \\
127 127 --project myproject \\
128 128 --tracker bug \\
129 129 --allow-override tracker,priority
130 130
131 131 Project selected by subaddress of redmine@example.net. Sending the email
132 132 to redmine+myproject@example.net will add the issue to myproject:
133 133
134 134 rdm-mailhandler.rb --url http://redmine.domain.foo --key secret \\
135 135 --project-from-subaddress redmine@example.net
136 136 END_DESC
137 137
138 138 opts.summary_width = 27
139 139 end
140 140 optparse.parse!
141 141
142 142 unless url && key
143 143 puts "Some arguments are missing. Use `rdm-mailhandler.rb --help` for getting help."
144 144 exit 1
145 145 end
146 146 end
147 147
148 148 def submit(email)
149 149 uri = url.gsub(%r{/*$}, '') + '/mail_handler'
150 150
151 151 headers = { 'User-Agent' => "Redmine mail handler/#{VERSION}" }
152 152
153 153 data = { 'key' => key, 'email' => email,
154 154 'allow_override' => allow_override,
155 155 'unknown_user' => unknown_user,
156 156 'default_group' => default_group,
157 157 'no_account_notice' => no_account_notice,
158 158 'no_notification' => no_notification,
159 159 'no_permission_check' => no_permission_check,
160 160 'project_from_subaddress' => project_from_subaddress}
161 161 issue_attributes.each { |attr, value| data["issue[#{attr}]"] = value }
162 162
163 163 debug "Posting to #{uri}..."
164 164 begin
165 165 response = Net::HTTPS.post_form(URI.parse(uri), data, headers, :no_check_certificate => no_check_certificate, :certificate_bundle => certificate_bundle)
166 166 rescue SystemCallError, IOError => e # connection refused, etc.
167 167 warn "An error occured while contacting your Redmine server: #{e.message}"
168 168 return 75 # temporary failure
169 169 end
170 170 debug "Response received: #{response.code}"
171 171
172 172 case response.code.to_i
173 173 when 403
174 174 warn "Request was denied by your Redmine server. " +
175 175 "Make sure that 'WS for incoming emails' is enabled in application settings and that you provided the correct API key."
176 176 return 77
177 177 when 422
178 178 warn "Request was denied by your Redmine server. " +
179 179 "Possible reasons: email is sent from an invalid email address or is missing some information."
180 180 return 77
181 181 when 400..499
182 182 warn "Request was denied by your Redmine server (#{response.code})."
183 183 return 77
184 184 when 500..599
185 185 warn "Failed to contact your Redmine server (#{response.code})."
186 186 return 75
187 187 when 201
188 188 debug "Proccessed successfully"
189 189 return 0
190 190 else
191 191 return 1
192 192 end
193 193 end
194 194
195 195 private
196 196
197 197 def debug(msg)
198 198 puts msg if verbose
199 199 end
200 200
201 201 def read_key_from_file(filename)
202 202 begin
203 203 self.key = File.read(filename).strip
204 204 rescue Exception => e
205 205 $stderr.puts "Unable to read the key from #{filename}:\n#{e.message}"
206 206 exit 1
207 207 end
208 208 end
209 209 end
210 210
211 211 handler = RedmineMailHandler.new
212 212 exit(handler.submit(STDIN.read))
General Comments 0
You need to be logged in to leave comments. Login now