OSDN Git Service

scm: use shell quote for scm command at adapter level (#7517, #4273).
authorToshi MARUYAMA <marutosijp2@yahoo.co.jp>
Mon, 14 Feb 2011 06:14:34 +0000 (06:14 +0000)
committerToshi MARUYAMA <marutosijp2@yahoo.co.jp>
Mon, 14 Feb 2011 06:14:34 +0000 (06:14 +0000)
"C:\Program Files\TortoiseHg\hg.exe" can be used in config/configuration.yml.

In Ruby 1.9 IO.popen, if cmd is an Array of String,
it will be used as the subprocess‘s argv bypassing a shell.

See http://www.ruby-doc.org/core/classes/IO.html

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4821 e93f8b46-1217-0410-a6f0-8f06a7374b81

config/configuration.yml.example
lib/redmine/scm/adapters/abstract_adapter.rb
lib/redmine/scm/adapters/bazaar_adapter.rb
lib/redmine/scm/adapters/cvs_adapter.rb
lib/redmine/scm/adapters/darcs_adapter.rb
lib/redmine/scm/adapters/filesystem_adapter.rb
lib/redmine/scm/adapters/git_adapter.rb
lib/redmine/scm/adapters/mercurial_adapter.rb
lib/redmine/scm/adapters/subversion_adapter.rb

index f00d97f..b893014 100644 (file)
@@ -111,7 +111,7 @@ default:
   # Absolute path (e.g. /usr/local/bin/hg) or command name (e.g. hg.exe, bzr.exe)
   # On Windows, *.cmd, *.bat (e.g. hg.cmd, bzr.bat) does not work.
   scm_subversion_command: svn                                       # (default: svn)
-  scm_mercurial_command:  "\"C:\Program Files\TortoiseHg\hg.exe\""  # (default: hg)
+  scm_mercurial_command:  C:\Program Files\TortoiseHg\hg.exe        # (default: hg)
   scm_git_command:        /usr/local/bin/git                        # (default: git)
   scm_cvs_command:        cvs                                       # (default: cvs)
   scm_bazaar_command:     bzr.exe                                   # (default: bzr)
index f98e909..3442276 100644 (file)
@@ -25,6 +25,10 @@ module Redmine
       
       class AbstractAdapter #:nodoc:
         class << self
+          def client_command
+            ""
+          end
+
           # Returns the version of the scm client
           # Eg: [1, 5, 0] or [] if unknown
           def client_version
@@ -45,8 +49,20 @@ module Redmine
           def client_version_above?(v, options={})
             ((client_version <=> v) >= 0) || (client_version.empty? && options[:unknown])
           end
+
+          def client_available
+            true
+          end
+
+          def shell_quote(str)
+            if Redmine::Platform.mswin?
+              '"' + str.gsub(/"/, '\\"') + '"'
+            else
+              "'" + str.gsub(/'/, "'\"'\"'") + "'"
+            end
+          end
         end
-                
+
         def initialize(url, root_url=nil, login=nil, password=nil)
           @url = url
           @login = login if login && !login.empty?
@@ -138,7 +154,7 @@ module Redmine
           path ||= ''
           (path[-1,1] == "/") ? path : "#{path}/"
         end
-        
+
         def without_leading_slash(path)
           path ||= ''
           path.gsub(%r{^/+}, '')
@@ -148,13 +164,9 @@ module Redmine
           path ||= ''
           (path[-1,1] == "/") ? path[0..-2] : path
          end
-        
+
         def shell_quote(str)
-          if Redmine::Platform.mswin?
-            '"' + str.gsub(/"/, '\\"') + '"'
-          else
-            "'" + str.gsub(/'/, "'\"'\"'") + "'"
-          end
+          self.class.shell_quote(str)
         end
 
       private
@@ -168,11 +180,11 @@ module Redmine
           base = path.match(/^\//) ? root_url : url
           shell_quote("#{base}/#{path}".gsub(/[?<>\*]/, ''))
         end
-            
+
         def logger
           self.class.logger
         end
-        
+
         def shellout(cmd, &block)
           self.class.shellout(cmd, &block)
         end
index 96ec869..1a11f63 100644 (file)
@@ -19,15 +19,25 @@ require 'redmine/scm/adapters/abstract_adapter'
 
 module Redmine
   module Scm
-    module Adapters    
+    module Adapters
       class BazaarAdapter < AbstractAdapter
-      
+
         # Bazaar executable name
         BZR_BIN = Redmine::Configuration['scm_bazaar_command'] || "bzr"
-        
+
+        class << self
+          def client_command
+            @@bin    ||= BZR_BIN
+          end
+
+          def sq_bin
+            @@sq_bin ||= shell_quote(BZR_BIN)
+          end
+        end
+
         # Get info about the repository
         def info
-          cmd = "#{BZR_BIN} revno #{target('')}"
+          cmd = "#{self.class.sq_bin} revno #{target('')}"
           info = nil
           shellout(cmd) do |io|
             if io.read =~ %r{^(\d+)\r?$}
@@ -43,13 +53,13 @@ module Redmine
         rescue CommandFailed
           return nil
         end
-        
+
         # Returns an Entries collection
         # or nil if the given path doesn't exist in the repository
         def entries(path=nil, identifier=nil)
           path ||= ''
           entries = Entries.new
-          cmd = "#{BZR_BIN} ls -v --show-ids"
+          cmd = "#{self.class.sq_bin} ls -v --show-ids"
           identifier = -1 unless identifier && identifier.to_i > 0 
           cmd << " -r#{identifier.to_i}" 
           cmd << " #{target(path)}"
@@ -71,13 +81,13 @@ module Redmine
           logger.debug("Found #{entries.size} entries in the repository for #{target(path)}") if logger && logger.debug?
           entries.sort_by_name
         end
-    
+
         def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
           path ||= ''
           identifier_from = (identifier_from and identifier_from.to_i > 0) ? identifier_from.to_i : 'last:1'
           identifier_to = (identifier_to and identifier_to.to_i > 0) ? identifier_to.to_i : 1
           revisions = Revisions.new
-          cmd = "#{BZR_BIN} log -v --show-ids -r#{identifier_to}..#{identifier_from} #{target(path)}"
+          cmd = "#{self.class.sq_bin} log -v --show-ids -r#{identifier_to}..#{identifier_from} #{target(path)}"
           shellout(cmd) do |io|
             revision = nil
             parsing = nil
@@ -132,7 +142,7 @@ module Redmine
           return nil if $? && $?.exitstatus != 0
           revisions
         end
-        
+
         def diff(path, identifier_from, identifier_to=nil)
           path ||= ''
           if identifier_to
@@ -143,7 +153,7 @@ module Redmine
           if identifier_from
             identifier_from = identifier_from.to_i
           end
-          cmd = "#{BZR_BIN} diff -r#{identifier_to}..#{identifier_from} #{target(path)}"
+          cmd = "#{self.class.sq_bin} diff -r#{identifier_to}..#{identifier_from} #{target(path)}"
           diff = []
           shellout(cmd) do |io|
             io.each_line do |line|
@@ -153,9 +163,9 @@ module Redmine
           #return nil if $? && $?.exitstatus != 0
           diff
         end
-        
+
         def cat(path, identifier=nil)
-          cmd = "#{BZR_BIN} cat"
+          cmd = "#{self.class.sq_bin} cat"
           cmd << " -r#{identifier.to_i}" if identifier && identifier.to_i > 0
           cmd << " #{target(path)}"
           cat = nil
@@ -166,9 +176,9 @@ module Redmine
           return nil if $? && $?.exitstatus != 0
           cat
         end
-        
+
         def annotate(path, identifier=nil)
-          cmd = "#{BZR_BIN} annotate --all"
+          cmd = "#{self.class.sq_bin} annotate --all"
           cmd << " -r#{identifier.to_i}" if identifier && identifier.to_i > 0
           cmd << " #{target(path)}"
           blame = Annotate.new
index 1cda302..c3d3bf7 100644 (file)
@@ -24,7 +24,17 @@ module Redmine
 
         # CVS executable name
         CVS_BIN = Redmine::Configuration['scm_cvs_command'] || "cvs"
-    
+
+        class << self
+          def client_command
+            @@bin    ||= CVS_BIN
+          end
+
+          def sq_bin
+            @@sq_bin ||= shell_quote(CVS_BIN)
+          end
+        end
+
         # Guidelines for the input:
         #  url -> the project-path, relative to the cvsroot (eg. module name)
         #  root_url -> the good old, sometimes damned, CVSROOT
@@ -38,20 +48,20 @@ module Redmine
           raise CommandFailed if root_url.blank?
           @root_url = root_url
         end
-        
+
         def root_url
           @root_url
         end
-        
+
         def url
           @url
         end
-        
+
         def info
           logger.debug "<cvs> info"
           Info.new({:root_url => @root_url, :lastrev => nil})
         end
-        
+
         def get_previous_revision(revision)
           CvsRevisionHelper.new(revision).prevRev
         end
@@ -63,7 +73,7 @@ module Redmine
           logger.debug "<cvs> entries '#{path}' with identifier '#{identifier}'"
           path_with_project="#{url}#{with_leading_slash(path)}"
           entries = Entries.new
-          cmd = "#{CVS_BIN} -d #{shell_quote root_url} rls -e"
+          cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rls -e"
           cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier
           cmd << " #{shell_quote path_with_project}"
           shellout(cmd) do |io|
@@ -108,7 +118,7 @@ module Redmine
           logger.debug "<cvs> revisions path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
 
           path_with_project="#{url}#{with_leading_slash(path)}"
-          cmd = "#{CVS_BIN} -d #{shell_quote root_url} rlog"
+          cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rlog"
           cmd << " -d\">#{time_to_cvstime_rlog(identifier_from)}\"" if identifier_from
           cmd << " #{shell_quote path_with_project}"
           shellout(cmd) do |io|
@@ -229,7 +239,7 @@ module Redmine
         def diff(path, identifier_from, identifier_to=nil)
           logger.debug "<cvs> diff path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
           path_with_project="#{url}#{with_leading_slash(path)}"
-          cmd = "#{CVS_BIN} -d #{shell_quote root_url} rdiff -u -r#{identifier_to} -r#{identifier_from} #{shell_quote path_with_project}"
+          cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rdiff -u -r#{identifier_to} -r#{identifier_from} #{shell_quote path_with_project}"
           diff = []
           shellout(cmd) do |io|
             io.each_line do |line|
@@ -244,7 +254,7 @@ module Redmine
           identifier = (identifier) ? identifier : "HEAD"
           logger.debug "<cvs> cat path:'#{path}',identifier #{identifier}"
           path_with_project="#{url}#{with_leading_slash(path)}"
-          cmd = "#{CVS_BIN} -d #{shell_quote root_url} co"
+          cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} co"
           cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier
           cmd << " -p #{shell_quote path_with_project}"
           cat = nil
@@ -260,7 +270,7 @@ module Redmine
           identifier = (identifier) ? identifier.to_i : "HEAD"
           logger.debug "<cvs> annotate path:'#{path}',identifier #{identifier}"
           path_with_project="#{url}#{with_leading_slash(path)}"
-          cmd = "#{CVS_BIN} -d #{shell_quote root_url} rannotate -r#{identifier} #{shell_quote path_with_project}"
+          cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rannotate -r#{identifier} #{shell_quote path_with_project}"
           blame = Annotate.new
           shellout(cmd) do |io|
             io.each_line do |line|
index 4eaf4ff..4eed61e 100644 (file)
@@ -20,16 +20,24 @@ require 'rexml/document'
 
 module Redmine
   module Scm
-    module Adapters    
+    module Adapters
       class DarcsAdapter < AbstractAdapter      
         # Darcs executable name
         DARCS_BIN = Redmine::Configuration['scm_darcs_command'] || "darcs"
-        
+
         class << self
+          def client_command
+            @@bin    ||= DARCS_BIN
+          end
+
+          def sq_bin
+            @@sq_bin ||= shell_quote(DARCS_BIN)
+          end
+
           def client_version
             @@client_version ||= (darcs_binary_version || [])
           end
-         
+
           def darcs_binary_version
             darcsversion = darcs_binary_version_from_command_line
             if m = darcsversion.match(%r{\A(.*?)((\d+\.)+\d+)})
@@ -38,7 +46,7 @@ module Redmine
           end
 
           def darcs_binary_version_from_command_line
-            shellout("#{DARCS_BIN} --version") { |io| io.read }.to_s
+            shellout("#{sq_bin} --version") { |io| io.read }.to_s
           end
         end
 
@@ -57,7 +65,7 @@ module Redmine
           rev = revisions(nil,nil,nil,{:limit => 1})
           rev ? Info.new({:root_url => @url, :lastrev => rev.last}) : nil
         end
-        
+
         # Returns an Entries collection
         # or nil if the given path doesn't exist in the repository
         def entries(path=nil, identifier=nil)
@@ -66,7 +74,7 @@ module Redmine
             path = ( self.class.client_version_above?([2, 2, 0]) ? @url : '.' )
           end
           entries = Entries.new          
-          cmd = "#{DARCS_BIN} annotate --repodir #{shell_quote @url} --xml-output"
+          cmd = "#{self.class.sq_bin} annotate --repodir #{shell_quote @url} --xml-output"
           cmd << " --match #{shell_quote("hash #{identifier}")}" if identifier
           cmd << " #{shell_quote path}"
           shellout(cmd) do |io|
@@ -86,11 +94,11 @@ module Redmine
           return nil if $? && $?.exitstatus != 0
           entries.compact.sort_by_name
         end
-    
+
         def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
           path = '.' if path.blank?
           revisions = Revisions.new
-          cmd = "#{DARCS_BIN} changes --repodir #{shell_quote @url} --xml-output"
+          cmd = "#{self.class.sq_bin} changes --repodir #{shell_quote @url} --xml-output"
           cmd << " --from-match #{shell_quote("hash #{identifier_from}")}" if identifier_from
           cmd << " --last #{options[:limit].to_i}" if options[:limit]
           shellout(cmd) do |io|
@@ -113,10 +121,10 @@ module Redmine
           return nil if $? && $?.exitstatus != 0
           revisions
         end
-        
+
         def diff(path, identifier_from, identifier_to=nil)
           path = '*' if path.blank?
-          cmd = "#{DARCS_BIN} diff --repodir #{shell_quote @url}"
+          cmd = "#{self.class.sq_bin} diff --repodir #{shell_quote @url}"
           if identifier_to.nil?
             cmd << " --match #{shell_quote("hash #{identifier_from}")}"
           else
@@ -133,9 +141,9 @@ module Redmine
           return nil if $? && $?.exitstatus != 0
           diff
         end
-        
+
         def cat(path, identifier=nil)
-          cmd = "#{DARCS_BIN} show content --repodir #{shell_quote @url}"
+          cmd = "#{self.class.sq_bin} show content --repodir #{shell_quote @url}"
           cmd << " --match #{shell_quote("hash #{identifier}")}" if identifier
           cmd << " #{shell_quote path}"
           cat = nil
@@ -148,7 +156,7 @@ module Redmine
         end
 
         private
-        
+
         # Returns an Entry from the given XML element
         # or nil if the entry was deleted
         def entry_from_xml(element, path_prefix)
@@ -196,10 +204,10 @@ module Redmine
           end
           paths
         end
-        
+
         # Retrieve changed paths for a single patch
         def get_paths_for_patch_raw(hash)
-          cmd = "#{DARCS_BIN} annotate --repodir #{shell_quote @url} --summary --xml-output"
+          cmd = "#{self.class.sq_bin} annotate --repodir #{shell_quote @url} --summary --xml-output"
           cmd << " --match #{shell_quote("hash #{hash}")} "
           paths = []
           shellout(cmd) do |io|
index 99296a0..b88892b 100644 (file)
@@ -25,7 +25,12 @@ module Redmine
   module Scm
     module Adapters    
       class FilesystemAdapter < AbstractAdapter
-        
+
+        class << self
+          def client_available
+            true
+          end
+        end
 
         def initialize(url, root_url=nil, login=nil, password=nil)
           @url = with_trailling_slash(url)
index a40f450..a241f83 100644 (file)
@@ -19,11 +19,25 @@ require 'redmine/scm/adapters/abstract_adapter'
 
 module Redmine
   module Scm
-    module Adapters    
+    module Adapters
       class GitAdapter < AbstractAdapter
         # Git executable name
         GIT_BIN = Redmine::Configuration['scm_git_command'] || "git"
 
+        class << self
+          def client_command
+            @@bin    ||= GIT_BIN
+          end
+
+          def sq_bin
+            @@sq_bin ||= shell_quote(GIT_BIN)
+          end
+
+          def client_available
+            !client_version.empty?
+          end
+        end
+
         def info
           begin
             Info.new(:root_url => url, :lastrev => lastrev('',nil))
@@ -35,7 +49,7 @@ module Redmine
         def branches
           return @branches if @branches
           @branches = []
-          cmd = "#{GIT_BIN} --git-dir #{target('')} branch --no-color"
+          cmd = "#{self.class.sq_bin} --git-dir #{target('')} branch --no-color"
           shellout(cmd) do |io|
             io.each_line do |line|
               @branches << line.match('\s*\*?\s*(.*)$')[1]
@@ -46,7 +60,7 @@ module Redmine
 
         def tags
           return @tags if @tags
-          cmd = "#{GIT_BIN} --git-dir #{target('')} tag"
+          cmd = "#{self.class.sq_bin} --git-dir #{target('')} tag"
           shellout(cmd) do |io|
             @tags = io.readlines.sort!.map{|t| t.strip}
           end
@@ -59,7 +73,7 @@ module Redmine
         def entries(path=nil, identifier=nil)
           path ||= ''
           entries = Entries.new
-          cmd = "#{GIT_BIN} --git-dir #{target('')} ls-tree -l "
+          cmd = "#{self.class.sq_bin} --git-dir #{target('')} ls-tree -l "
           cmd << shell_quote("HEAD:" + path) if identifier.nil?
           cmd << shell_quote(identifier + ":" + path) if identifier
           shellout(cmd)  do |io|
@@ -86,7 +100,7 @@ module Redmine
 
         def lastrev(path,rev)
           return nil if path.nil?
-          cmd = "#{GIT_BIN} --git-dir #{target('')} log --no-color --date=iso --pretty=fuller --no-merges -n 1 "
+          cmd = "#{self.class.sq_bin} --git-dir #{target('')} log --no-color --date=iso --pretty=fuller --no-merges -n 1 "
           cmd << " #{shell_quote rev} " if rev 
           cmd <<  "-- #{shell_quote path} " unless path.empty?
           lines = []
@@ -114,7 +128,7 @@ module Redmine
         def revisions(path, identifier_from, identifier_to, options={})
           revisions = Revisions.new
 
-          cmd = "#{GIT_BIN} --git-dir #{target('')} log --no-color --raw --date=iso --pretty=fuller "
+          cmd = "#{self.class.sq_bin} --git-dir #{target('')} log --no-color --raw --date=iso --pretty=fuller "
           cmd << " --reverse " if options[:reverse]
           cmd << " --all " if options[:all]
           cmd << " -n #{options[:limit].to_i} " if options[:limit]
@@ -209,9 +223,9 @@ module Redmine
           path ||= ''
 
           if identifier_to
-            cmd = "#{GIT_BIN} --git-dir #{target('')} diff --no-color #{shell_quote identifier_to} #{shell_quote identifier_from}" 
+            cmd = "#{self.class.sq_bin} --git-dir #{target('')} diff --no-color #{shell_quote identifier_to} #{shell_quote identifier_from}" 
           else
-            cmd = "#{GIT_BIN} --git-dir #{target('')} show --no-color #{shell_quote identifier_from}"
+            cmd = "#{self.class.sq_bin} --git-dir #{target('')} show --no-color #{shell_quote identifier_from}"
           end
 
           cmd << " -- #{shell_quote path}" unless path.empty?
@@ -227,7 +241,7 @@ module Redmine
         
         def annotate(path, identifier=nil)
           identifier = 'HEAD' if identifier.blank?
-          cmd = "#{GIT_BIN} --git-dir #{target('')} blame -p #{shell_quote identifier} -- #{shell_quote path}"
+          cmd = "#{self.class.sq_bin} --git-dir #{target('')} blame -p #{shell_quote identifier} -- #{shell_quote path}"
           blame = Annotate.new
           content = nil
           shellout(cmd) { |io| io.binmode; content = io.read }
@@ -255,7 +269,7 @@ module Redmine
           if identifier.nil?
             identifier = 'HEAD'
           end
-          cmd = "#{GIT_BIN} --git-dir #{target('')} show --no-color #{shell_quote(identifier + ':' + path)}"
+          cmd = "#{self.class.sq_bin} --git-dir #{target('')} show --no-color #{shell_quote(identifier + ':' + path)}"
           cat = nil
           shellout(cmd) do |io|
             io.binmode
index f64e70b..f8f925d 100644 (file)
@@ -20,7 +20,7 @@ require 'cgi'
 
 module Redmine
   module Scm
-    module Adapters    
+    module Adapters
       class MercurialAdapter < AbstractAdapter
 
         # Mercurial executable name
@@ -30,11 +30,23 @@ module Redmine
         TEMPLATE_EXTENSION = "tmpl"
 
         class << self
+          def client_command
+            @@bin    ||= HG_BIN
+          end
+
+          def sq_bin
+            @@sq_bin ||= shell_quote(HG_BIN)
+          end
+
           def client_version
             @@client_version ||= (hgversion || [])
           end
 
-          def hgversion  
+          def client_available
+            !client_version.empty?
+          end
+
+          def hgversion
             # The hg version is expressed either as a
             # release number (eg 0.9.5 or 1.0) or as a revision
             # id composed of 12 hexa characters.
@@ -45,7 +57,7 @@ module Redmine
           end
 
           def hgversion_from_command_line
-            shellout("#{HG_BIN} --version") { |io| io.read }.to_s
+            shellout("#{sq_bin} --version") { |io| io.read }.to_s
           end
 
           def template_path
@@ -63,7 +75,7 @@ module Redmine
         end
 
         def info
-          cmd = "#{HG_BIN} -R #{target('')} root"
+          cmd = "#{self.class.sq_bin} -R #{target('')} root"
           root_url = nil
           shellout(cmd) do |io|
             root_url = io.read
@@ -80,7 +92,7 @@ module Redmine
         def entries(path=nil, identifier=nil)
           path ||= ''
           entries = Entries.new
-          cmd = "#{HG_BIN} -R #{target('')} --cwd #{target('')} locate"
+          cmd = "#{self.class.sq_bin} -R #{target('')} --cwd #{target('')} locate"
           cmd << " -r #{hgrev(identifier)}"
           cmd << " " + shell_quote("path:#{path}") unless path.empty?
           shellout(cmd) do |io|
@@ -106,7 +118,7 @@ module Redmine
         # makes Mercurial produce a xml output.
         def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})  
           revisions = Revisions.new
-          cmd = "#{HG_BIN} --debug --encoding utf8 -R #{target('')} log -C --style #{shell_quote self.class.template_path}"
+          cmd = "#{self.class.sq_bin} --debug --encoding utf8 -R #{target('')} log -C --style #{shell_quote self.class.template_path}"
           if identifier_from && identifier_to
             cmd << " -r #{hgrev(identifier_from)}:#{hgrev(identifier_to)}"
           elsif identifier_from
@@ -164,7 +176,7 @@ module Redmine
               return []
             end
           end
-          cmd = "#{HG_BIN} -R #{target('')} --config diff.git=false diff --nodates #{diff_args}"
+          cmd = "#{self.class.sq_bin} -R #{target('')} --config diff.git=false diff --nodates #{diff_args}"
           cmd << " -I #{target(path)}" unless path.empty?
           shellout(cmd) do |io|
             io.each_line do |line|
@@ -176,7 +188,7 @@ module Redmine
         end
 
         def cat(path, identifier=nil)
-          cmd = "#{HG_BIN} -R #{target('')} cat"
+          cmd = "#{self.class.sq_bin} -R #{target('')} cat"
           cmd << " -r #{hgrev(identifier)}"
           cmd << " #{target(path)}"
           cat = nil
@@ -190,7 +202,7 @@ module Redmine
 
         def annotate(path, identifier=nil)
           path ||= ''
-          cmd = "#{HG_BIN} -R #{target('')}"
+          cmd = "#{self.class.sq_bin} -R #{target('')}"
           cmd << " annotate -ncu"
           cmd << " -r #{hgrev(identifier)}"
           cmd << " #{target(path)}"
index 28ea55e..c17d7a5 100644 (file)
@@ -20,19 +20,27 @@ require 'uri'
 \r
 module Redmine\r
   module Scm\r
-    module Adapters    \r
+    module Adapters\r
       class SubversionAdapter < AbstractAdapter\r
-      \r
+\r
         # SVN executable name\r
         SVN_BIN = Redmine::Configuration['scm_subversion_command'] || "svn"\r
-        \r
+\r
         class << self\r
+          def client_command\r
+            @@bin    ||= SVN_BIN\r
+          end\r
+\r
+          def sq_bin\r
+            @@sq_bin ||= shell_quote(SVN_BIN)\r
+          end\r
+\r
           def client_version\r
             @@client_version ||= (svn_binary_version || [])\r
           end\r
-          \r
+\r
           def svn_binary_version\r
-            cmd = "#{SVN_BIN} --version"\r
+            cmd = "#{sq_bin} --version"\r
             version = nil\r
             shellout(cmd) do |io|\r
               # Read svn version in first returned line\r
@@ -44,10 +52,10 @@ module Redmine
             version\r
           end\r
         end\r
-        \r
+\r
         # Get info about the svn repository\r
         def info\r
-          cmd = "#{SVN_BIN} info --xml #{target}"\r
+          cmd = "#{self.class.sq_bin} info --xml #{target}"\r
           cmd << credentials_string\r
           info = nil\r
           shellout(cmd) do |io|\r
@@ -70,14 +78,14 @@ module Redmine
         rescue CommandFailed\r
           return nil\r
         end\r
-        \r
+\r
         # Returns an Entries collection\r
         # or nil if the given path doesn't exist in the repository\r
         def entries(path=nil, identifier=nil)\r
           path ||= ''\r
           identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"\r
           entries = Entries.new\r
-          cmd = "#{SVN_BIN} list --xml #{target(path)}@#{identifier}"\r
+          cmd = "#{self.class.sq_bin} list --xml #{target(path)}@#{identifier}"\r
           cmd << credentials_string\r
           shellout(cmd) do |io|\r
             output = io.read\r
@@ -110,13 +118,13 @@ module Redmine
           logger.debug("Found #{entries.size} entries in the repository for #{target(path)}") if logger && logger.debug?\r
           entries.sort_by_name\r
         end\r
-        \r
+\r
         def properties(path, identifier=nil)\r
           # proplist xml output supported in svn 1.5.0 and higher\r
           return nil unless self.class.client_version_above?([1, 5, 0])\r
           \r
           identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"\r
-          cmd = "#{SVN_BIN} proplist --verbose --xml #{target(path)}@#{identifier}"\r
+          cmd = "#{self.class.sq_bin} proplist --verbose --xml #{target(path)}@#{identifier}"\r
           cmd << credentials_string\r
           properties = {}\r
           shellout(cmd) do |io|\r
@@ -132,13 +140,13 @@ module Redmine
           return nil if $? && $?.exitstatus != 0\r
           properties\r
         end\r
-        \r
+\r
         def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})\r
           path ||= ''\r
           identifier_from = (identifier_from && identifier_from.to_i > 0) ? identifier_from.to_i : "HEAD"\r
           identifier_to = (identifier_to && identifier_to.to_i > 0) ? identifier_to.to_i : 1\r
           revisions = Revisions.new\r
-          cmd = "#{SVN_BIN} log --xml -r #{identifier_from}:#{identifier_to}"\r
+          cmd = "#{self.class.sq_bin} log --xml -r #{identifier_from}:#{identifier_to}"\r
           cmd << credentials_string\r
           cmd << " --verbose " if  options[:with_paths]\r
           cmd << " --limit #{options[:limit].to_i}" if options[:limit]\r
@@ -171,13 +179,13 @@ module Redmine
           return nil if $? && $?.exitstatus != 0\r
           revisions\r
         end\r
-        \r
+\r
         def diff(path, identifier_from, identifier_to=nil, type="inline")\r
           path ||= ''\r
           identifier_from = (identifier_from and identifier_from.to_i > 0) ? identifier_from.to_i : ''\r
           identifier_to = (identifier_to and identifier_to.to_i > 0) ? identifier_to.to_i : (identifier_from.to_i - 1)\r
-          \r
-          cmd = "#{SVN_BIN} diff -r "\r
+\r
+          cmd = "#{self.class.sq_bin} diff -r "\r
           cmd << "#{identifier_to}:"\r
           cmd << "#{identifier_from}"\r
           cmd << " #{target(path)}@#{identifier_from}"\r
@@ -191,10 +199,10 @@ module Redmine
           return nil if $? && $?.exitstatus != 0\r
           diff\r
         end\r
-        \r
+\r
         def cat(path, identifier=nil)\r
           identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"\r
-          cmd = "#{SVN_BIN} cat #{target(path)}@#{identifier}"\r
+          cmd = "#{self.class.sq_bin} cat #{target(path)}@#{identifier}"\r
           cmd << credentials_string\r
           cat = nil\r
           shellout(cmd) do |io|\r
@@ -204,10 +212,10 @@ module Redmine
           return nil if $? && $?.exitstatus != 0\r
           cat\r
         end\r
-        \r
+\r
         def annotate(path, identifier=nil)\r
           identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"\r
-          cmd = "#{SVN_BIN} blame #{target(path)}@#{identifier}"\r
+          cmd = "#{self.class.sq_bin} blame #{target(path)}@#{identifier}"\r
           cmd << credentials_string\r
           blame = Annotate.new\r
           shellout(cmd) do |io|\r