##// END OF EJS Templates
Mail Handler: option for specifying a custom CA bundle (#19182)....
Jean-Philippe Lang -
r13907:9e39ad471994
parent child
Show More
@@ -1,179 +1,183
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 if options[:certificate_bundle]
34 http.ca_file = options[:certificate_bundle]
35 end
33 36 if options[:no_check_certificate]
34 37 http.verify_mode = OpenSSL::SSL::VERIFY_NONE
35 38 end
36 39 http.start {|h| h.request(request) }
37 40 end
38 41 end
39 42 end
40 43
41 44 class RedmineMailHandler
42 45 VERSION = '0.2.3'
43 46
44 47 attr_accessor :verbose, :issue_attributes, :allow_override, :unknown_user, :default_group, :no_permission_check,
45 :url, :key, :no_check_certificate, :no_account_notice, :no_notification
48 :url, :key, :no_check_certificate, :certificate_bundle, :no_account_notice, :no_notification
46 49
47 50 def initialize
48 51 self.issue_attributes = {}
49 52
50 53 optparse = OptionParser.new do |opts|
51 54 opts.banner = "Usage: rdm-mailhandler.rb [options] --url=<Redmine URL> --key=<API key>"
52 55 opts.separator("")
53 56 opts.separator("Reads an email from standard input and forwards it to a Redmine server through a HTTP request.")
54 57 opts.separator("")
55 58 opts.separator("Required arguments:")
56 59 opts.on("-u", "--url URL", "URL of the Redmine server") {|v| self.url = v}
57 60 opts.on("-k", "--key KEY", "Redmine API key") {|v| self.key = v}
58 61 opts.separator("")
59 62 opts.separator("General options:")
60 63 opts.on("--no-permission-check", "disable permission checking when receiving",
61 64 "the email") {self.no_permission_check = '1'}
62 65 opts.on("--key-file FILE", "full path to a file that contains your Redmine",
63 66 "API key (use this option instead of --key if",
64 67 "you don't want the key to appear in the command",
65 68 "line)") {|v| read_key_from_file(v)}
66 69 opts.on("--no-check-certificate", "do not check server certificate") {self.no_check_certificate = true}
70 opts.on("--certificate-bundle FILE", "certificate bundle to use") {|v| self.certificate_bundle = v}
67 71 opts.on("-h", "--help", "show this help") {puts opts; exit 1}
68 72 opts.on("-v", "--verbose", "show extra information") {self.verbose = true}
69 73 opts.on("-V", "--version", "show version information and exit") {puts VERSION; exit}
70 74 opts.separator("")
71 75 opts.separator("User creation options:")
72 76 opts.on("--unknown-user ACTION", "how to handle emails from an unknown user",
73 77 "ACTION can be one of the following values:",
74 78 "* ignore: email is ignored (default)",
75 79 "* accept: accept as anonymous user",
76 80 "* create: create a user account") {|v| self.unknown_user = v}
77 81 opts.on("--default-group GROUP", "add created user to GROUP (none by default)",
78 82 "GROUP can be a comma separated list of groups") { |v| self.default_group = v}
79 83 opts.on("--no-account-notice", "don't send account information to the newly",
80 84 "created user") { |v| self.no_account_notice = '1'}
81 85 opts.on("--no-notification", "disable email notifications for the created",
82 86 "user") { |v| self.no_notification = '1'}
83 87 opts.separator("")
84 88 opts.separator("Issue attributes control options:")
85 89 opts.on("-p", "--project PROJECT", "identifier of the target project") {|v| self.issue_attributes['project'] = v}
86 90 opts.on("-s", "--status STATUS", "name of the target status") {|v| self.issue_attributes['status'] = v}
87 91 opts.on("-t", "--tracker TRACKER", "name of the target tracker") {|v| self.issue_attributes['tracker'] = v}
88 92 opts.on( "--category CATEGORY", "name of the target category") {|v| self.issue_attributes['category'] = v}
89 93 opts.on( "--priority PRIORITY", "name of the target priority") {|v| self.issue_attributes['priority'] = v}
90 94 opts.on( "--private", "create new issues as private") {|v| self.issue_attributes['is_private'] = '1'}
91 95 opts.on("-o", "--allow-override ATTRS", "allow email content to override attributes",
92 96 "specified by previous options",
93 97 "ATTRS is a comma separated list of attributes") {|v| self.allow_override = v}
94 98 opts.separator("")
95 99 opts.separator("Examples:")
96 100 opts.separator("No project specified, emails MUST contain the 'Project' keyword:")
97 101 opts.separator(" rdm-mailhandler.rb --url http://redmine.domain.foo --key secret")
98 102 opts.separator("")
99 103 opts.separator("Fixed project and default tracker specified, but emails can override")
100 104 opts.separator("both tracker and priority attributes using keywords:")
101 105 opts.separator(" rdm-mailhandler.rb --url https://domain.foo/redmine --key secret \\")
102 106 opts.separator(" --project foo \\")
103 107 opts.separator(" --tracker bug \\")
104 108 opts.separator(" --allow-override tracker,priority")
105 109
106 110 opts.summary_width = 27
107 111 end
108 112 optparse.parse!
109 113
110 114 unless url && key
111 115 puts "Some arguments are missing. Use `rdm-mailhandler.rb --help` for getting help."
112 116 exit 1
113 117 end
114 118 end
115 119
116 120 def submit(email)
117 121 uri = url.gsub(%r{/*$}, '') + '/mail_handler'
118 122
119 123 headers = { 'User-Agent' => "Redmine mail handler/#{VERSION}" }
120 124
121 125 data = { 'key' => key, 'email' => email,
122 126 'allow_override' => allow_override,
123 127 'unknown_user' => unknown_user,
124 128 'default_group' => default_group,
125 129 'no_account_notice' => no_account_notice,
126 130 'no_notification' => no_notification,
127 131 'no_permission_check' => no_permission_check}
128 132 issue_attributes.each { |attr, value| data["issue[#{attr}]"] = value }
129 133
130 134 debug "Posting to #{uri}..."
131 135 begin
132 response = Net::HTTPS.post_form(URI.parse(uri), data, headers, :no_check_certificate => no_check_certificate)
136 response = Net::HTTPS.post_form(URI.parse(uri), data, headers, :no_check_certificate => no_check_certificate, :certificate_bundle => certificate_bundle)
133 137 rescue SystemCallError, IOError => e # connection refused, etc.
134 138 warn "An error occured while contacting your Redmine server: #{e.message}"
135 139 return 75 # temporary failure
136 140 end
137 141 debug "Response received: #{response.code}"
138 142
139 143 case response.code.to_i
140 144 when 403
141 145 warn "Request was denied by your Redmine server. " +
142 146 "Make sure that 'WS for incoming emails' is enabled in application settings and that you provided the correct API key."
143 147 return 77
144 148 when 422
145 149 warn "Request was denied by your Redmine server. " +
146 150 "Possible reasons: email is sent from an invalid email address or is missing some information."
147 151 return 77
148 152 when 400..499
149 153 warn "Request was denied by your Redmine server (#{response.code})."
150 154 return 77
151 155 when 500..599
152 156 warn "Failed to contact your Redmine server (#{response.code})."
153 157 return 75
154 158 when 201
155 159 debug "Proccessed successfully"
156 160 return 0
157 161 else
158 162 return 1
159 163 end
160 164 end
161 165
162 166 private
163 167
164 168 def debug(msg)
165 169 puts msg if verbose
166 170 end
167 171
168 172 def read_key_from_file(filename)
169 173 begin
170 174 self.key = File.read(filename).strip
171 175 rescue Exception => e
172 176 $stderr.puts "Unable to read the key from #{filename}:\n#{e.message}"
173 177 exit 1
174 178 end
175 179 end
176 180 end
177 181
178 182 handler = RedmineMailHandler.new
179 183 exit(handler.submit(STDIN.read))
General Comments 0
You need to be logged in to leave comments. Login now