You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

rdm-mailhandler.rb 7.6 kB

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

软件技术创新是当今国家整体创新能力的重要基础和发展引擎。近年来,大众参与的软件创新与创业活动已经成为网络时代软件开发和应用的新形态,正在快速改变着全球软件创新模式和软件产业格局。 ...