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