To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / extra / mail_handler / rdm-mailhandler.rb @ 1570:ae2f71010562

History | View | Annotate | Download (8.21 KB)

1
#!/usr/bin/env ruby
2
# Redmine - project management software
3
# Copyright (C) 2006-2014  Jean-Philippe Lang
4
#
5
# This program is free software; you can redistribute it and/or
6
# modify it under the terms of the GNU General Public License
7
# as published by the Free Software Foundation; either version 2
8
# of the License, or (at your option) any later version.
9
#
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18

    
19
require 'net/http'
20
require 'net/https'
21
require 'uri'
22
require 'optparse'
23

    
24
module Net
25
  class HTTPS < HTTP
26
    def self.post_form(url, params, headers, options={})
27
      request = Post.new(url.path)
28
      request.form_data = params
29
      request.initialize_http_header(headers)
30
      request.basic_auth url.user, url.password if url.user
31
      http = new(url.host, url.port)
32
      http.use_ssl = (url.scheme == 'https')
33
      if options[:no_check_certificate]
34
        http.verify_mode = OpenSSL::SSL::VERIFY_NONE
35
      end
36
      http.start {|h| h.request(request) }
37
    end
38
  end
39
end
40

    
41
class RedmineMailHandler
42
  VERSION = '0.2.3'
43

    
44
  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
46

    
47
  def initialize
48
    self.issue_attributes = {}
49

    
50
    optparse = OptionParser.new do |opts|
51
      opts.banner = "Usage: rdm-mailhandler.rb [options] --url=<Redmine URL> --key=<API key>"
52
      opts.separator("")
53
      opts.separator("Reads an email from standard input and forwards it to a Redmine server through a HTTP request.")
54
      opts.separator("")
55
      opts.separator("Required arguments:")
56
      opts.on("-u", "--url URL",              "URL of the Redmine server") {|v| self.url = v}
57
      opts.on("-k", "--key KEY",              "Redmine API key") {|v| self.key = v}
58
      opts.separator("")
59
      opts.separator("General options:")
60
      opts.on("--no-permission-check",        "disable permission checking when receiving",
61
                                              "the email") {self.no_permission_check = '1'}
62
      opts.on("--key-file FILE",              "full path to a file that contains your Redmine",
63
                                              "API key (use this option instead of --key if",
64
                                              "you don't want the key to appear in the command",
65
                                              "line)") {|v| read_key_from_file(v)}
66
      opts.on("--no-check-certificate",       "do not check server certificate") {self.no_check_certificate = true}
67
      opts.on("-h", "--help",                 "show this help") {puts opts; exit 1}
68
      opts.on("-v", "--verbose",              "show extra information") {self.verbose = true}
69
      opts.on("-V", "--version",              "show version information and exit") {puts VERSION; exit}
70
      opts.separator("")
71
      opts.separator("User creation options:")
72
      opts.on("--unknown-user ACTION",        "how to handle emails from an unknown user",
73
                                              "ACTION can be one of the following values:",
74
                                              "* ignore: email is ignored (default)",
75
                                              "* accept: accept as anonymous user",
76
                                              "* create: create a user account") {|v| self.unknown_user = v}
77
      opts.on("--default-group GROUP",        "add created user to GROUP (none by default)",
78
                                              "GROUP can be a comma separated list of groups") { |v| self.default_group = v}
79
      opts.on("--no-account-notice",          "don't send account information to the newly",
80
                                              "created user") { |v| self.no_account_notice = '1'}
81
      opts.on("--no-notification",            "disable email notifications for the created",
82
                                              "user") { |v| self.no_notification = '1'}
83
      opts.separator("")
84
      opts.separator("Issue attributes control options:")
85
      opts.on("-p", "--project PROJECT",      "identifier of the target project") {|v| self.issue_attributes['project'] = v}
86
      opts.on("-s", "--status STATUS",        "name of the target status") {|v| self.issue_attributes['status'] = v}
87
      opts.on("-t", "--tracker TRACKER",      "name of the target tracker") {|v| self.issue_attributes['tracker'] = v}
88
      opts.on(      "--category CATEGORY",    "name of the target category") {|v| self.issue_attributes['category'] = v}
89
      opts.on(      "--priority PRIORITY",    "name of the target priority") {|v| self.issue_attributes['priority'] = v}
90
      opts.on("-o", "--allow-override ATTRS", "allow email content to override attributes",
91
                                              "specified by previous options",
92
                                              "ATTRS is a comma separated list of attributes") {|v| self.allow_override = v}
93
      opts.separator("")
94
      opts.separator("Examples:")
95
      opts.separator("No project specified, emails MUST contain the 'Project' keyword:")
96
      opts.separator("  rdm-mailhandler.rb --url http://redmine.domain.foo --key secret")
97
      opts.separator("")
98
      opts.separator("Fixed project and default tracker specified, but emails can override")
99
      opts.separator("both tracker and priority attributes using keywords:")
100
      opts.separator("  rdm-mailhandler.rb --url https://domain.foo/redmine --key secret \\")
101
      opts.separator("    --project foo \\")
102
      opts.separator("    --tracker bug \\")
103
      opts.separator("    --allow-override tracker,priority")
104

    
105
      opts.summary_width = 27
106
    end
107
    optparse.parse!
108

    
109
    unless url && key
110
      puts "Some arguments are missing. Use `rdm-mailhandler.rb --help` for getting help."
111
      exit 1
112
    end
113
  end
114

    
115
  def submit(email)
116
    uri = url.gsub(%r{/*$}, '') + '/mail_handler'
117

    
118
    headers = { 'User-Agent' => "Redmine mail handler/#{VERSION}" }
119

    
120
    data = { 'key' => key, 'email' => email,
121
                           'allow_override' => allow_override,
122
                           'unknown_user' => unknown_user,
123
                           'default_group' => default_group,
124
                           'no_account_notice' => no_account_notice,
125
                           'no_notification' => no_notification,
126
                           'no_permission_check' => no_permission_check}
127
    issue_attributes.each { |attr, value| data["issue[#{attr}]"] = value }
128

    
129
    debug "Posting to #{uri}..."
130
    begin
131
      response = Net::HTTPS.post_form(URI.parse(uri), data, headers, :no_check_certificate => no_check_certificate)
132
    rescue SystemCallError => e # connection refused, etc.
133
      warn "An error occured while contacting your Redmine server: #{e.message}"
134
      return 75 # temporary failure
135
    end
136
    debug "Response received: #{response.code}"
137

    
138
    case response.code.to_i
139
      when 403
140
        warn "Request was denied by your Redmine server. " +
141
             "Make sure that 'WS for incoming emails' is enabled in application settings and that you provided the correct API key."
142
        return 77
143
      when 422
144
        warn "Request was denied by your Redmine server. " +
145
             "Possible reasons: email is sent from an invalid email address or is missing some information."
146
        return 77
147
      when 400..499
148
        warn "Request was denied by your Redmine server (#{response.code})."
149
        return 77
150
      when 500..599
151
        warn "Failed to contact your Redmine server (#{response.code})."
152
        return 75
153
      when 201
154
        debug "Proccessed successfully"
155
        return 0
156
      else
157
        return 1
158
    end
159
  end
160

    
161
  private
162

    
163
  def debug(msg)
164
    puts msg if verbose
165
  end
166

    
167
  def read_key_from_file(filename)
168
    begin
169
      self.key = File.read(filename).strip
170
    rescue Exception => e
171
      $stderr.puts "Unable to read the key from #{filename}:\n#{e.message}"
172
      exit 1
173
    end
174
  end
175
end
176

    
177
handler = RedmineMailHandler.new
178
exit(handler.submit(STDIN.read))