# Copyright (c) 2005 Diego Petten, Edward Cho # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies of the Software and its documentation and acknowledgment shall be # given in the documentation and software packages that this Software was # used. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. require 'net/https' require 'cgi' require 'yaml' class Bugzilla attr_reader :name, :dataurl, :reporturl, :useragent def initialize( name, data_url, report_url, user_agent ) @name, @data_url, @report_url, @user_agent = name, data_url, report_url, user_agent end def ==( other ) return false unless @name == other.name return false unless @data_url == other.data_url return false unless @report_url == other.report_url return false unless @user_agent == other.user_agent return true end def get_bug_xml( bug_id ) uri = URI.parse( @data_url.gsub( "${BUGID}", bug_id ) ) bugzilla_site = Net::HTTP.new( uri.host, uri.port ) bugzilla_site.use_ssl = true if uri.port == 443 response = bugzilla_site.get( uri.request_uri ) REXML::Document.new( response.body ) end def get_bug_summary( bug_id ) # OpenOffice's issuezilla is tricky, they call it issue_status # unless bugxml # bugxml = bugdata.root.get_elements("issue")[0] # status_txt = "issue_status" # end begin bug_xml = get_bug_xml( bug_id ) rescue REXML::ParseException return "Received badly formed XML for bug #{ bug_id }" end return "Unable to load bug # #{ bug_id }." unless bug_xml bug_xmlroot = bug_xml.root.get_elements("bug")[0] return "Unable to parse bug # #{ bug_id }." unless bug_xmlroot return "Bug # #{ bug_id } not found." if bug_xmlroot.attributes["error"] bug_elements = [ 'short_desc', 'product', 'component', 'bug_status', 'resolution', 'reporter', 'assigned_to' ] bug_summary = Hash.new bug_elements.each do |element| bug_summary[element] = CGI::unescapeHTML( bug_xmlroot.get_text( element ).to_s ) end return "Bug #{ bug_id }; \"#{ bug_summary['short_desc'] }\"; #{ bug_summary['product'] } | #{ bug_summary['component'] }; #{ bug_summary['bug_status'] }, #{ bug_summary['resolution'] }; #{ bug_summary['reporter'] } -> #{ bug_summary['assigned_to'] }; #{ @report_url.gsub( '${BUGID}', bug_id ) }" end end class BugzillaPlugin < Plugin def initialize super read_database read_config return true end def database_filename File.dirname(__FILE__) + "/.rbot/plugins/bugzilla_db.yaml" end def config_filename File.dirname(__FILE__) + '/.rbot/plugins/bugzilla_conf.yaml' end def default_bugzilla @config['bugzilla.default'].to_s end def read_database @bugzillas = Hash.new bugzillas_yaml = Hash.new if FileTest.exists? database_filename File.open( database_filename ) do |database_yaml| bugzillas_yaml = YAML::load( database_yaml ) end end bugzillas_yaml.each_pair do |k, v| bugzilla_ivars = Hash.new v.ivars.each_pair { |variable, value| bugzilla_ivars[variable] = value } @bugzillas[k] = Bugzilla.new( bugzilla_ivars["name"], bugzilla_ivars["data_url"], bugzilla_ivars["report_url"], bugzilla_ivars["user_agent"] ) end end def write_database File.open( database_filename, 'w' ) do |database_yaml| YAML.dump( @bugzillas, database_yaml ) end end def read_config @config = Hash.new if FileTest.exists? config_filename File.open( config_filename ) do |config_yaml| @config = YAML::load( config_yaml ) end end end def write_config File.open( config_filename, 'w' ) do |config_yaml| YAML.dump( @config , config_yaml ) end end def help(plugin, topic = "") case plugin when "bug" return "bug => show summary data of a bug from the given bugzilla. | bug => show summary data of a bug from the default bugzilla." when "bugzilla" return "bugzilla => show a list of added bugzillas. | bugzilla add [ ] => add a bugzilla. | bugzilla remove => remove a bugzilla. | bugzilla set_default => set the default bugzilla." end end def show_bug_summary( m, params ) # if necessary, get default bugzilla if params[:bugzilla].is_a? Proc bugzilla = params[:bugzilla].call else bugzilla = params[:bugzilla] end # show summary information if @bugzillas.keys.include? bugzilla m.reply @bugzillas[bugzilla].get_bug_summary( params[:bug_id] ) else m.reply "#{ bugzilla } has not been added yet." end end def add_bugzilla( m, params ) # check for valid network responses data_uri = URI.parse( params[:data_url].gsub( "${BUGID}", "0" ) ) begin Net::HTTP.get_response( data_uri.host, data_uri.request_uri ) rescue SocketError m.reply "invalid bugzilla. cannot connect to #{ params[:data_url] }." return end report_uri = URI.parse( params[:report_url].gsub( "${BUGID}", "0" ) ) begin Net::HTTP.get_response( report_uri.host, report_uri.request_uri ) rescue SocketError m.reply "invalid bugzilla. cannot connect to #{ params[:report_url] }." return end # add the new bugzilla @bugzillas[params[:name]] = Bugzilla.new( params[:name], params[:data_url], params[:report_url], params[:user_agent] ) write_database m.reply "added #{ params[:name] } bugzilla." # first bugzilla added is set as default if @bugzillas.length == 1 @config['bugzilla.default'] = params[:name] write_config m.reply "default: #{ @config['bugzilla.default'] }." end end def remove_bugzilla( m, params ) if @bugzillas.delete( params[:name] ) write_database m.reply "removed #{ params[:name] } bugzilla." if @config['bugzilla.default'] == params[:name] and !@bugzillas.empty? @config['bugzilla.default'] = @bugzillas.keys.first else @config['bugzilla.default'] = "" end write_config else m.reply "#{ params[:name] } bugzilla has not been added yet so it cannot be removed." end end def show_bugzillas( m, params ) if @bugzillas.empty? m.reply "no bugzillas have been added yet." else if @config['bugzilla.default'] m.reply "#{ @bugzillas.keys.join(", ") }; default: #{ @config['bugzilla.default'] }" else m.reply "#{ @bugzillas.keys.join(", ") }" end end end def set_default_bugzilla( m, params ) if @bugzillas.empty? m.reply "no bugzillas have been added yet." elsif not @bugzillas.keys.include? params[:name] m.reply "#{ params[:name] } has not been added yet so it cannot be set to default." else @config['bugzilla.default'] = params[:name] write_config m.reply "default: #{ @config['bugzilla.default'] }." end end end plugin = BugzillaPlugin.new plugin.map( 'bug :bug_id :bugzilla', :action => 'show_bug_summary', :requirements => { :bug_id => /^\d+$/ }, :defaults => { :bugzilla => Proc.new { plugin.default_bugzilla } } ) plugin.map( 'bugzilla', :action => 'show_bugzillas' ) plugin.map( 'bugzilla add :name :data_url :report_url :user_agent', :action => 'add_bugzilla', :requirements => { :data_url => /^(http:\/\/|https:\/\/)\S+/, :report_url => /^(http:\/\/|https:\/\/)\S+/ }, :defaults => { :user_agent => "rBot/#{$version}" } ) plugin.map( 'bugzilla remove :name', :action => 'remove_bugzilla' ) plugin.map( 'bugzilla set_default :name', :action => 'set_default_bugzilla' )