if File::ALT_SEPARATOR require "win32/file" include Win32 end require "sys/admin" include Sys module DBI # The base error class for dbi-dbrc. If anything goes wrong, this is # the error that is raised. A subclass of StandardError. class DBRCError < StandardError; end # The main class. class DBRC VERSION = "1.0.0" attr_accessor :database, :user, :password, :driver, :dsn attr_accessor :maximum_reconnects, :timeout, :interval, :dbrc_dir # Returns a new DBRC object. The contents of the object depend on the # arguments passed to the constructor. If only a database name is # passed, then the first entry found in the .dbrc file that matches that # database is parsed. If a user name is also included, then the first # entry that matches both the database and user name is parsed. # # If a directory is passed as the third argument, then DBRC will look # in that directory, instead of the default directory, for the .dbrc # file. # # If an entry cannot be found for the database, or database plus user # combination, then a DBRCError is raised. If the .dbrc file cannot # be found, or is setup improperly with regards to permissions or # properties, a DBRCError is raised. def initialize(database,user=nil,dbrc_dir=nil) if dbrc_dir.nil? if File::ALT_SEPARATOR home = ENV["USERPROFILE"] || ENV["HOME"] file = nil if home file = home + "\\.dbrc" else file = "C:\\Documents and Settings\\" file += Admin.get_user(Admin.get_login).home_dir + "\\.dbrc" end @dbrc_file = file else @dbrc_file = Admin.get_user(Process.uid).dir + "/.dbrc" end else @dbrc_file = dbrc_dir + "/.dbrc" end @database = database @user = user encrypted = false # Win32 only check_file() # If on Win32 and the file is encrypted, decrypt it. if File::ALT_SEPARATOR && File.encrypted?(@dbrc_file) encrypted = true File.decrypt(@dbrc_file) end parse_dbrc_config_file() validate_data() convert_numeric_strings() create_dsn_string() # If on Win32 and the file was encrypted, re-encrypt it if File::ALT_SEPARATOR && encrypted File.encrypt(@dbrc_file) end end private # Ensure that the user/password has been set def validate_data unless @user raise DBRCError, "no user found associated with #{@database}" end unless @password raise DBRCError, "password not defined for #{@user}@#{@database}" end end # Converts strings that should be numbers into actual numbers def convert_numeric_strings @interval = @interval.to_i if @interval @timeout = @timeout.to_i if @timeout @maximum_reconnects = @maximum_reconnects.to_i if @maximum_reconnects end # Create the dsn string if the driver is defined def create_dsn_string @dsn = "dbi:#{@driver}:#{@database}" if @driver end # Check ownership and permissions def check_file(file=@dbrc_file) File.open(file){ |f| # Permissions must be set to 600 or better on Unix systems. # Must be hidden on Win32 systems. if File::ALT_SEPARATOR unless File.hidden?(file) raise DBRCError, "The .dbrc file must be hidden" end else unless (f.stat.mode & 077) == 0 raise DBRCError, "Bad .dbrc file permissions" end end # Only the owner may use it unless f.stat.owned? raise DBRCError, "Not owner of .dbrc file" end } end # Parse the text out of the .dbrc file. This is the only method you # need to redefine if writing your own config handler. def parse_dbrc_config_file(file=@dbrc_file) IO.foreach(file){ |line| next if line =~ /^#/ # Ignore comments db, user, pwd, driver, timeout, max, interval = line.split next unless @database == db if @user next unless @user == user end @user = user @password = pwd @driver = driver @timeout = timeout @maximum_reconnects = max @interval = interval return } # If we reach here it means the database and/or user wasn't found raise DBRCError, "No record found for #{@user}@#{@database}" end alias_method(:db,:database) alias_method(:db=,:database=) alias_method(:passwd,:password) alias_method(:passwd=,:password=) alias_method(:max_reconn,:maximum_reconnects) alias_method(:max_reconn=,:maximum_reconnects=) alias_method(:time_out,:timeout) alias_method(:time_out=,:timeout=) end # A subclass of DBRC designed to handle .dbrc files in XML format. The # public methods of this class are identical to DBRC. class XML < DBRC require "rexml/document" include REXML private def parse_dbrc_config_file(file=@dbrc_file) doc = Document.new(File.new(file)) fields = %w/user password driver interval timeout maximum_reconnects/ doc.elements.each("/dbrc/database"){ |element| next unless element.attributes["name"] == database if @user next unless element.elements["user"].text == @user end fields.each{ |field| val = element.elements[field] unless val.nil? send("#{field}=",val.text) end } return } # If we reach here it means the database and/or user wasn't found raise DBRCError, "No record found for #{@user}@#{@database}" end end # A subclass of DBRC designed to handle .dbrc files in YAML format. The # public methods of this class are identical to DBRC. class YML < DBRC require "yaml" private def parse_dbrc_config_file(file=@dbrc_file) config = YAML.load(File.open(file)) config.each{ |hash| hash.each{ |db,info| next unless db == @database if @user next unless @user == info["user"] end @user = info["user"] @password = info["password"] @driver = info["driver"] @interval = info["interval"] @timeout = info["timeout"] @maximum_reconnects = info["max_reconn"] return } } # If we reach this point, it means the database wasn't found raise DBRCError, "No entry found for #{@user}@#{@database}" end end end