lib/dbi/dbrc.rb in dbi-dbrc-1.1.3 vs lib/dbi/dbrc.rb in dbi-dbrc-1.1.4
- old
+ new
@@ -1,283 +1,298 @@
-require 'rbconfig'
-
-if Config::CONFIG['host_os'].match('mswin')
- require 'win32/file'
- require 'win32/dir'
-end
-
-require 'sys/admin'
-
-# The DBI module serves as a namespace only.
-module DBI
-
- # The DBRC class encapsulates a database resource config file.
- class DBRC
-
- # This error is raised if anything fails trying to read the config file.
- # the error that is raised.
- class Error < StandardError; end
-
- # The version of this library
- VERSION = '1.1.3'
-
- # The database or host to be connected to.
- attr_accessor :database
-
- # The user name used for the database or host connection.
- attr_accessor :user
-
- # The password associated with the database or host.
- attr_accessor :password
-
- # The driver associated with the database. This is used to internally to
- # construct the DSN.
- attr_accessor :driver
-
- # Data source name, e.g. "dbi:OCI8:your_database".
- attr_accessor :dsn
-
- # The maximum number of reconnects a program should make before
- # giving up.
- attr_accessor :maximum_reconnects
-
- # The timeout, in seconds, for each connection attempt.
- attr_accessor :timeout
-
- # The interval, in seconds, between each connection attempt.
- attr_accessor :interval
-
- # The directory where the .dbrc file is stored.
- attr_accessor :dbrc_dir
-
- # The full path to the .dbrc file.
- attr_accessor :dbrc_file
-
- # Returns a new DBI::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 Error is raised. If the .dbrc file cannot
- # be found, or is setup improperly with regards to permissions or
- # properties, a DBI::DBRC::Error is raised.
- #
- # See the README for the rules regarding .dbrc files and permissions.
- #
- # Note that this library can also be used as a general password
- # storage mechanism. In that case simply treat the 'database' as the
- # host name, and ignore the DBI::DBRC#dsn and DBI::DBRC#driver methods.
- #
- # Examples:
- #
- # # Find the first match for 'some_database'
- # DBI::DBRC.new('some_database')
- #
- # # Find the first match for 'foo_user@some_database'
- # DBI::DBRC.new('some_database', 'foo_user')
- #
- # # Find the first match for 'foo_user@some_database' under /usr/local
- # DBI::DBRC.new('some_database', 'foo_usr', '/usr/local')
- #
- def initialize(database, user=nil, dbrc_dir=nil)
- if dbrc_dir.nil?
- if Config::CONFIG['host_os'].match('mswin')
- home = ENV['USERPROFILE'] || ENV['HOME']
- file = nil
-
- if home
- file = File.join(home, '.dbrc')
- else
- file = File.join(File.basename(Dir::APPDATA), '.dbrc')
- end
-
- @dbrc_file = file
- else
- @dbrc_file = File.join(
- Sys::Admin.get_user(Process.uid).dir,
- '.dbrc'
- )
- end
- else
- @dbrc_file = File.join(dbrc_dir, '.dbrc')
- end
-
- @dbrc_dir = dbrc_dir
- @database = database
- @user = user
- encrypted = false # Win32 only
-
- @driver = nil
- @interval = nil
- @timeout = nil
- @maximum_reconnects = nil
-
- check_file()
-
- # If on Win32 and the file is encrypted, decrypt it.
- if Config::CONFIG['host_os'].match("mswin") && 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 Config::CONFIG['host_os'].match("mswin") && encrypted
- File.encrypt(@dbrc_file)
- end
- end
-
- private
-
- # Ensure that the user/password has been set
- def validate_data
- unless @user
- raise Error, "no user found associated with #{@database}"
- end
-
- unless @password
- raise Error, "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 Config::CONFIG['host_os'].match("mswin")
- unless File.hidden?(file)
- raise Error, "The .dbrc file must be hidden"
- end
- else
- unless (f.stat.mode & 077) == 0
- raise Error, "Bad .dbrc file permissions"
- end
- end
-
- # Only the owner may use it
- unless f.stat.owned?
- raise Error, "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
- if @user
- err = "no record found for #{@user}@#{@database}"
- else
- err = "no record found for #{@database}"
- end
-
- raise Error, err
- 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=)
- alias_method(:host, :database)
- 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 Error, "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 Error, "No entry found for #{@user}@#{@database}"
- end
- end
-end
+require 'rbconfig'
+
+if Config::CONFIG['host_os'].match('mswin')
+ require 'win32/file'
+ require 'win32/dir'
+end
+
+require 'sys/admin'
+
+# The DBI module serves as a namespace only.
+module DBI
+
+ # The DBRC class encapsulates a database resource config file.
+ class DBRC
+
+ # This error is raised if anything fails trying to read the config file.
+ # the error that is raised.
+ class Error < StandardError; end
+
+ # The version of this library
+ VERSION = '1.1.4'
+
+ # The database or host to be connected to.
+ attr_accessor :database
+
+ # The user name used for the database or host connection.
+ attr_accessor :user
+
+ # The password associated with the database or host.
+ attr_accessor :password
+
+ # The driver associated with the database. This is used to internally to
+ # construct the DSN.
+ attr_accessor :driver
+
+ # Data source name, e.g. "dbi:OCI8:your_database".
+ attr_accessor :dsn
+
+ # The maximum number of reconnects a program should make before
+ # giving up.
+ attr_accessor :maximum_reconnects
+
+ # The timeout, in seconds, for each connection attempt.
+ attr_accessor :timeout
+
+ # The interval, in seconds, between each connection attempt.
+ attr_accessor :interval
+
+ # The directory where the .dbrc file is stored.
+ attr_accessor :dbrc_dir
+
+ # The full path to the .dbrc file.
+ attr_accessor :dbrc_file
+
+ # Returns a new DBI::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 Error is raised. If the .dbrc file cannot
+ # be found, or is setup improperly with regards to permissions or
+ # properties, a DBI::DBRC::Error is raised.
+ #
+ # See the README for the rules regarding .dbrc files and permissions.
+ #
+ # Note that this library can also be used as a general password
+ # storage mechanism. In that case simply treat the 'database' as the
+ # host name, and ignore the DBI::DBRC#dsn and DBI::DBRC#driver methods.
+ #
+ # Examples:
+ #
+ # # Find the first match for 'some_database'
+ # DBI::DBRC.new('some_database')
+ #
+ # # Find the first match for 'foo_user@some_database'
+ # DBI::DBRC.new('some_database', 'foo_user')
+ #
+ # # Find the first match for 'foo_user@some_database' under /usr/local
+ # DBI::DBRC.new('some_database', 'foo_usr', '/usr/local')
+ #
+ def initialize(database, user=nil, dbrc_dir=nil)
+ if dbrc_dir.nil?
+ if Config::CONFIG['host_os'].match('mswin')
+ home = ENV['USERPROFILE'] || ENV['HOME']
+ file = nil
+
+ if home
+ file = File.join(home, '.dbrc')
+ else
+ file = File.join(File.basename(Dir::APPDATA), '.dbrc')
+ end
+
+ @dbrc_file = file
+ else
+ @dbrc_file = File.join(
+ Sys::Admin.get_user(Process.uid).dir,
+ '.dbrc'
+ )
+ end
+ else
+ @dbrc_file = File.join(dbrc_dir, '.dbrc')
+ end
+
+ @dbrc_dir = dbrc_dir
+ @database = database
+ @user = user
+ encrypted = false # Win32 only
+
+ @driver = nil
+ @interval = nil
+ @timeout = nil
+ @maximum_reconnects = nil
+
+ check_file()
+
+ # If on Win32 and the file is encrypted, decrypt it.
+ if Config::CONFIG['host_os'].match("mswin") && 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 Config::CONFIG['host_os'].match("mswin") && encrypted
+ File.encrypt(@dbrc_file)
+ end
+ end
+
+ # Inspection of the DBI::DBRC object. This is identical to the standard
+ # Ruby Object#inspect, except that the password field is filtered.
+ #
+ def inspect
+ str = instance_variables.map{ |iv|
+ if iv == '@password'
+ "#{iv}=[FILTERED]"
+ else
+ "#{iv}=#{instance_variable_get(iv).inspect}"
+ end
+ }.join(', ')
+
+ "#<#{self.class}:0x#{(self.object_id*2).to_s(16)} " << str << ">"
+ end
+
+ private
+
+ # Ensure that the user/password has been set
+ def validate_data
+ unless @user
+ raise Error, "no user found associated with #{@database}"
+ end
+
+ unless @password
+ raise Error, "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 Config::CONFIG['host_os'].match("mswin")
+ unless File.hidden?(file)
+ raise Error, "The .dbrc file must be hidden"
+ end
+ else
+ unless (f.stat.mode & 077) == 0
+ raise Error, "Bad .dbrc file permissions"
+ end
+ end
+
+ # Only the owner may use it
+ unless f.stat.owned?
+ raise Error, "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
+ if @user
+ err = "no record found for #{@user}@#{@database}"
+ else
+ err = "no record found for #{@database}"
+ end
+
+ raise Error, err
+ 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=)
+ alias_method(:host, :database)
+ 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 Error, "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 Error, "No entry found for #{@user}@#{@database}"
+ end
+ end
+end