comparison .svn/pristine/ae/aec5b584466e9e60b9cc8f97f0d1ae1d90e9fe28.svn-base @ 1298:4f746d8966dd redmine_2.3_integration

Merge from redmine-2.3 branch to create new branch redmine-2.3-integration
author Chris Cannam
date Fri, 14 Jun 2013 09:28:30 +0100
parents 622f24f53b42
children
comparison
equal deleted inserted replaced
1297:0a574315af3e 1298:4f746d8966dd
1 #!/usr/bin/env ruby
2
3 require 'net/http'
4 require 'net/https'
5 require 'uri'
6 require 'optparse'
7
8 module Net
9 class HTTPS < HTTP
10 def self.post_form(url, params, headers, options={})
11 request = Post.new(url.path)
12 request.form_data = params
13 request.initialize_http_header(headers)
14 request.basic_auth url.user, url.password if url.user
15 http = new(url.host, url.port)
16 http.use_ssl = (url.scheme == 'https')
17 if options[:no_check_certificate]
18 http.verify_mode = OpenSSL::SSL::VERIFY_NONE
19 end
20 http.start {|h| h.request(request) }
21 end
22 end
23 end
24
25 class RedmineMailHandler
26 VERSION = '0.2.3'
27
28 attr_accessor :verbose, :issue_attributes, :allow_override, :unknown_user, :default_group, :no_permission_check,
29 :url, :key, :no_check_certificate, :no_account_notice, :no_notification
30
31 def initialize
32 self.issue_attributes = {}
33
34 optparse = OptionParser.new do |opts|
35 opts.banner = "Usage: rdm-mailhandler.rb [options] --url=<Redmine URL> --key=<API key>"
36 opts.separator("")
37 opts.separator("Reads an email from standard input and forward it to a Redmine server through a HTTP request.")
38 opts.separator("")
39 opts.separator("Required arguments:")
40 opts.on("-u", "--url URL", "URL of the Redmine server") {|v| self.url = v}
41 opts.on("-k", "--key KEY", "Redmine API key") {|v| self.key = v}
42 opts.separator("")
43 opts.separator("General options:")
44 opts.on("--no-permission-check", "disable permission checking when receiving",
45 "the email") {self.no_permission_check = '1'}
46 opts.on("--key-file FILE", "path to a file that contains the Redmine",
47 "API key (use this option instead of --key",
48 "if you don't the key to appear in the",
49 "command line)") {|v| read_key_from_file(v)}
50 opts.on("--no-check-certificate", "do not check server certificate") {self.no_check_certificate = true}
51 opts.on("-h", "--help", "show this help") {puts opts; exit 1}
52 opts.on("-v", "--verbose", "show extra information") {self.verbose = true}
53 opts.on("-V", "--version", "show version information and exit") {puts VERSION; exit}
54 opts.separator("")
55 opts.separator("User creation options:")
56 opts.on("--unknown-user ACTION", "how to handle emails from an unknown user",
57 "ACTION can be one of the following values:",
58 "* ignore: email is ignored (default)",
59 "* accept: accept as anonymous user",
60 "* create: create a user account") {|v| self.unknown_user = v}
61 opts.on("--default-group GROUP", "add created user to GROUP (none by default)",
62 "GROUP can be a comma separated list of groups") { |v| self.default_group = v}
63 opts.on("--no-account-notice", "don't send account information to the newly",
64 "created user") { |v| self.no_account_notice = '1'}
65 opts.on("--no-notification", "disable email notifications for the created",
66 "user") { |v| self.no_notification = '1'}
67 opts.separator("")
68 opts.separator("Issue attributes control options:")
69 opts.on("-p", "--project PROJECT", "identifier of the target project") {|v| self.issue_attributes['project'] = v}
70 opts.on("-s", "--status STATUS", "name of the target status") {|v| self.issue_attributes['status'] = v}
71 opts.on("-t", "--tracker TRACKER", "name of the target tracker") {|v| self.issue_attributes['tracker'] = v}
72 opts.on( "--category CATEGORY", "name of the target category") {|v| self.issue_attributes['category'] = v}
73 opts.on( "--priority PRIORITY", "name of the target priority") {|v| self.issue_attributes['priority'] = v}
74 opts.on("-o", "--allow-override ATTRS", "allow email content to override attributes",
75 "specified by previous options",
76 "ATTRS is a comma separated list of attributes") {|v| self.allow_override = v}
77 opts.separator("")
78 opts.separator("Examples:")
79 opts.separator("No project specified. Emails MUST contain the 'Project' keyword:")
80 opts.separator(" rdm-mailhandler.rb --url http://redmine.domain.foo --key secret")
81 opts.separator("")
82 opts.separator("Fixed project and default tracker specified, but emails can override")
83 opts.separator("both tracker and priority attributes using keywords:")
84 opts.separator(" rdm-mailhandler.rb --url https://domain.foo/redmine --key secret \\")
85 opts.separator(" --project foo \\")
86 opts.separator(" --tracker bug \\")
87 opts.separator(" --allow-override tracker,priority")
88
89 opts.summary_width = 27
90 end
91 optparse.parse!
92
93 unless url && key
94 puts "Some arguments are missing. Use `rdm-mailhandler.rb --help` for getting help."
95 exit 1
96 end
97 end
98
99 def submit(email)
100 uri = url.gsub(%r{/*$}, '') + '/mail_handler'
101
102 headers = { 'User-Agent' => "Redmine mail handler/#{VERSION}" }
103
104 data = { 'key' => key, 'email' => email,
105 'allow_override' => allow_override,
106 'unknown_user' => unknown_user,
107 'default_group' => default_group,
108 'no_account_notice' => no_account_notice,
109 'no_notification' => no_notification,
110 'no_permission_check' => no_permission_check}
111 issue_attributes.each { |attr, value| data["issue[#{attr}]"] = value }
112
113 debug "Posting to #{uri}..."
114 begin
115 response = Net::HTTPS.post_form(URI.parse(uri), data, headers, :no_check_certificate => no_check_certificate)
116 rescue SystemCallError => e # connection refused, etc.
117 warn "An error occured while contacting your Redmine server: #{e.message}"
118 return 75 # temporary failure
119 end
120 debug "Response received: #{response.code}"
121
122 case response.code.to_i
123 when 403
124 warn "Request was denied by your Redmine server. " +
125 "Make sure that 'WS for incoming emails' is enabled in application settings and that you provided the correct API key."
126 return 77
127 when 422
128 warn "Request was denied by your Redmine server. " +
129 "Possible reasons: email is sent from an invalid email address or is missing some information."
130 return 77
131 when 400..499
132 warn "Request was denied by your Redmine server (#{response.code})."
133 return 77
134 when 500..599
135 warn "Failed to contact your Redmine server (#{response.code})."
136 return 75
137 when 201
138 debug "Proccessed successfully"
139 return 0
140 else
141 return 1
142 end
143 end
144
145 private
146
147 def debug(msg)
148 puts msg if verbose
149 end
150
151 def read_key_from_file(filename)
152 begin
153 self.key = File.read(filename).strip
154 rescue Exception => e
155 $stderr.puts "Unable to read the key from #{filename}:\n#{e.message}"
156 exit 1
157 end
158 end
159 end
160
161 handler = RedmineMailHandler.new
162 exit(handler.submit(STDIN.read))