##// END OF EJS Templates
Quote subversion username and password in svn commands....
Jean-Philippe Lang -
r839:ecfc40629f1e
parent child
Show More
@@ -1,345 +1,353
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'cgi'
19 19
20 20 module Redmine
21 21 module Scm
22 22 module Adapters
23 23 class CommandFailed < StandardError #:nodoc:
24 24 end
25 25
26 26 class AbstractAdapter #:nodoc:
27 27 def initialize(url, root_url=nil, login=nil, password=nil)
28 28 @url = url
29 29 @login = login if login && !login.empty?
30 30 @password = (password || "") if @login
31 31 @root_url = root_url.blank? ? retrieve_root_url : root_url
32 32 end
33 33
34 34 def adapter_name
35 35 'Abstract'
36 36 end
37 37
38 38 def supports_cat?
39 39 true
40 40 end
41 41
42 42 def root_url
43 43 @root_url
44 44 end
45 45
46 46 def url
47 47 @url
48 48 end
49 49
50 50 # get info about the svn repository
51 51 def info
52 52 return nil
53 53 end
54 54
55 55 # Returns the entry identified by path and revision identifier
56 56 # or nil if entry doesn't exist in the repository
57 57 def entry(path=nil, identifier=nil)
58 58 e = entries(path, identifier)
59 59 e ? e.first : nil
60 60 end
61 61
62 62 # Returns an Entries collection
63 63 # or nil if the given path doesn't exist in the repository
64 64 def entries(path=nil, identifier=nil)
65 65 return nil
66 66 end
67 67
68 68 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
69 69 return nil
70 70 end
71 71
72 72 def diff(path, identifier_from, identifier_to=nil, type="inline")
73 73 return nil
74 74 end
75 75
76 76 def cat(path, identifier=nil)
77 77 return nil
78 78 end
79 79
80 80 def with_leading_slash(path)
81 81 path ||= ''
82 82 (path[0,1]!="/") ? "/#{path}" : path
83 83 end
84
85 def shell_quote(str)
86 if RUBY_PLATFORM =~ /mswin/
87 '"' + str.gsub(/"/, '\\"') + '"'
88 else
89 "'" + str.gsub(/'/, "'\"'\"'") + "'"
90 end
91 end
84 92
85 93 private
86 94 def retrieve_root_url
87 95 info = self.info
88 96 info ? info.root_url : nil
89 97 end
90 98
91 99 def target(path)
92 100 path ||= ""
93 101 base = path.match(/^\//) ? root_url : url
94 102 " \"" << "#{base}/#{path}".gsub(/["?<>\*]/, '') << "\""
95 103 end
96 104
97 105 def logger
98 106 RAILS_DEFAULT_LOGGER
99 107 end
100 108
101 109 def shellout(cmd, &block)
102 110 logger.debug "Shelling out: #{cmd}" if logger && logger.debug?
103 111 IO.popen(cmd, "r+") do |io|
104 112 io.close_write
105 113 block.call(io) if block_given?
106 114 end
107 115 end
108 116 end
109 117
110 118 class Entries < Array
111 119 def sort_by_name
112 120 sort {|x,y|
113 121 if x.kind == y.kind
114 122 x.name <=> y.name
115 123 else
116 124 x.kind <=> y.kind
117 125 end
118 126 }
119 127 end
120 128
121 129 def revisions
122 130 revisions ||= Revisions.new(collect{|entry| entry.lastrev}.compact)
123 131 end
124 132 end
125 133
126 134 class Info
127 135 attr_accessor :root_url, :lastrev
128 136 def initialize(attributes={})
129 137 self.root_url = attributes[:root_url] if attributes[:root_url]
130 138 self.lastrev = attributes[:lastrev]
131 139 end
132 140 end
133 141
134 142 class Entry
135 143 attr_accessor :name, :path, :kind, :size, :lastrev
136 144 def initialize(attributes={})
137 145 self.name = attributes[:name] if attributes[:name]
138 146 self.path = attributes[:path] if attributes[:path]
139 147 self.kind = attributes[:kind] if attributes[:kind]
140 148 self.size = attributes[:size].to_i if attributes[:size]
141 149 self.lastrev = attributes[:lastrev]
142 150 end
143 151
144 152 def is_file?
145 153 'file' == self.kind
146 154 end
147 155
148 156 def is_dir?
149 157 'dir' == self.kind
150 158 end
151 159
152 160 def is_text?
153 161 Redmine::MimeType.is_type?('text', name)
154 162 end
155 163 end
156 164
157 165 class Revisions < Array
158 166 def latest
159 167 sort {|x,y|
160 168 unless x.time.nil? or y.time.nil?
161 169 x.time <=> y.time
162 170 else
163 171 0
164 172 end
165 173 }.last
166 174 end
167 175 end
168 176
169 177 class Revision
170 178 attr_accessor :identifier, :scmid, :name, :author, :time, :message, :paths, :revision, :branch
171 179 def initialize(attributes={})
172 180 self.identifier = attributes[:identifier]
173 181 self.scmid = attributes[:scmid]
174 182 self.name = attributes[:name] || self.identifier
175 183 self.author = attributes[:author]
176 184 self.time = attributes[:time]
177 185 self.message = attributes[:message] || ""
178 186 self.paths = attributes[:paths]
179 187 self.revision = attributes[:revision]
180 188 self.branch = attributes[:branch]
181 189 end
182 190
183 191 end
184 192
185 193 # A line of Diff
186 194 class Diff
187 195 attr_accessor :nb_line_left
188 196 attr_accessor :line_left
189 197 attr_accessor :nb_line_right
190 198 attr_accessor :line_right
191 199 attr_accessor :type_diff_right
192 200 attr_accessor :type_diff_left
193 201
194 202 def initialize ()
195 203 self.nb_line_left = ''
196 204 self.nb_line_right = ''
197 205 self.line_left = ''
198 206 self.line_right = ''
199 207 self.type_diff_right = ''
200 208 self.type_diff_left = ''
201 209 end
202 210
203 211 def inspect
204 212 puts '### Start Line Diff ###'
205 213 puts self.nb_line_left
206 214 puts self.line_left
207 215 puts self.nb_line_right
208 216 puts self.line_right
209 217 end
210 218 end
211 219
212 220 class DiffTableList < Array
213 221 def initialize (diff, type="inline")
214 222 diff_table = DiffTable.new type
215 223 diff.each do |line|
216 224 if line =~ /^(---|\+\+\+) (.*)$/
217 225 self << diff_table if diff_table.length > 1
218 226 diff_table = DiffTable.new type
219 227 end
220 228 a = diff_table.add_line line
221 229 end
222 230 self << diff_table
223 231 end
224 232 end
225 233
226 234 # Class for create a Diff
227 235 class DiffTable < Hash
228 236 attr_reader :file_name, :line_num_l, :line_num_r
229 237
230 238 # Initialize with a Diff file and the type of Diff View
231 239 # The type view must be inline or sbs (side_by_side)
232 240 def initialize (type="inline")
233 241 @parsing = false
234 242 @nb_line = 1
235 243 @start = false
236 244 @before = 'same'
237 245 @second = true
238 246 @type = type
239 247 end
240 248
241 249 # Function for add a line of this Diff
242 250 def add_line(line)
243 251 unless @parsing
244 252 if line =~ /^(---|\+\+\+) (.*)$/
245 253 @file_name = $2
246 254 return false
247 255 elsif line =~ /^@@ (\+|\-)(\d+)(,\d+)? (\+|\-)(\d+)(,\d+)? @@/
248 256 @line_num_l = $5.to_i
249 257 @line_num_r = $2.to_i
250 258 @parsing = true
251 259 end
252 260 else
253 261 if line =~ /^[^\+\-\s@\\]/
254 262 self.delete(self.keys.sort.last)
255 263 @parsing = false
256 264 return false
257 265 elsif line =~ /^@@ (\+|\-)(\d+)(,\d+)? (\+|\-)(\d+)(,\d+)? @@/
258 266 @line_num_l = $5.to_i
259 267 @line_num_r = $2.to_i
260 268 else
261 269 @nb_line += 1 if parse_line(line, @type)
262 270 end
263 271 end
264 272 return true
265 273 end
266 274
267 275 def inspect
268 276 puts '### DIFF TABLE ###'
269 277 puts "file : #{file_name}"
270 278 self.each do |d|
271 279 d.inspect
272 280 end
273 281 end
274 282
275 283 private
276 284 # Test if is a Side By Side type
277 285 def sbs?(type, func)
278 286 if @start and type == "sbs"
279 287 if @before == func and @second
280 288 tmp_nb_line = @nb_line
281 289 self[tmp_nb_line] = Diff.new
282 290 else
283 291 @second = false
284 292 tmp_nb_line = @start
285 293 @start += 1
286 294 @nb_line -= 1
287 295 end
288 296 else
289 297 tmp_nb_line = @nb_line
290 298 @start = @nb_line
291 299 self[tmp_nb_line] = Diff.new
292 300 @second = true
293 301 end
294 302 unless self[tmp_nb_line]
295 303 @nb_line += 1
296 304 self[tmp_nb_line] = Diff.new
297 305 else
298 306 self[tmp_nb_line]
299 307 end
300 308 end
301 309
302 310 # Escape the HTML for the diff
303 311 def escapeHTML(line)
304 312 CGI.escapeHTML(line)
305 313 end
306 314
307 315 def parse_line (line, type="inline")
308 316 if line[0, 1] == "+"
309 317 diff = sbs? type, 'add'
310 318 @before = 'add'
311 319 diff.line_left = escapeHTML line[1..-1]
312 320 diff.nb_line_left = @line_num_l
313 321 diff.type_diff_left = 'diff_in'
314 322 @line_num_l += 1
315 323 true
316 324 elsif line[0, 1] == "-"
317 325 diff = sbs? type, 'remove'
318 326 @before = 'remove'
319 327 diff.line_right = escapeHTML line[1..-1]
320 328 diff.nb_line_right = @line_num_r
321 329 diff.type_diff_right = 'diff_out'
322 330 @line_num_r += 1
323 331 true
324 332 elsif line[0, 1] =~ /\s/
325 333 @before = 'same'
326 334 @start = false
327 335 diff = Diff.new
328 336 diff.line_right = escapeHTML line[1..-1]
329 337 diff.nb_line_right = @line_num_r
330 338 diff.line_left = escapeHTML line[1..-1]
331 339 diff.nb_line_left = @line_num_l
332 340 self[@nb_line] = diff
333 341 @line_num_l += 1
334 342 @line_num_r += 1
335 343 true
336 344 elsif line[0, 1] = "\\"
337 345 true
338 346 else
339 347 false
340 348 end
341 349 end
342 350 end
343 351 end
344 352 end
345 353 end
@@ -1,178 +1,187
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'redmine/scm/adapters/abstract_adapter'
19 19 require 'rexml/document'
20 20
21 21 module Redmine
22 22 module Scm
23 23 module Adapters
24 24 class SubversionAdapter < AbstractAdapter
25 25
26 26 # SVN executable name
27 27 SVN_BIN = "svn"
28 28
29 29 # Get info about the svn repository
30 30 def info
31 31 cmd = "#{SVN_BIN} info --xml #{target('')}"
32 cmd << " --username #{@login} --password #{@password}" if @login
32 cmd << credentials_string
33 33 info = nil
34 34 shellout(cmd) do |io|
35 35 begin
36 36 doc = REXML::Document.new(io)
37 37 #root_url = doc.elements["info/entry/repository/root"].text
38 38 info = Info.new({:root_url => doc.elements["info/entry/repository/root"].text,
39 39 :lastrev => Revision.new({
40 40 :identifier => doc.elements["info/entry/commit"].attributes['revision'],
41 41 :time => Time.parse(doc.elements["info/entry/commit/date"].text).localtime,
42 42 :author => (doc.elements["info/entry/commit/author"] ? doc.elements["info/entry/commit/author"].text : "")
43 43 })
44 44 })
45 45 rescue
46 46 end
47 47 end
48 48 return nil if $? && $?.exitstatus != 0
49 49 info
50 50 rescue Errno::ENOENT => e
51 51 return nil
52 52 end
53 53
54 54 # Returns the entry identified by path and revision identifier
55 55 # or nil if entry doesn't exist in the repository
56 56 def entry(path=nil, identifier=nil)
57 57 e = entries(path, identifier)
58 58 e ? e.first : nil
59 59 end
60 60
61 61 # Returns an Entries collection
62 62 # or nil if the given path doesn't exist in the repository
63 63 def entries(path=nil, identifier=nil)
64 64 path ||= ''
65 65 identifier = 'HEAD' unless identifier and identifier > 0
66 66 entries = Entries.new
67 67 cmd = "#{SVN_BIN} list --xml #{target(path)}@#{identifier}"
68 cmd << " --username #{@login} --password #{@password}" if @login
68 cmd << credentials_string
69 69 cmd << " 2>&1"
70 70 shellout(cmd) do |io|
71 71 output = io.read
72 72 begin
73 73 doc = REXML::Document.new(output)
74 74 doc.elements.each("lists/list/entry") do |entry|
75 75 entries << Entry.new({:name => entry.elements['name'].text,
76 76 :path => ((path.empty? ? "" : "#{path}/") + entry.elements['name'].text),
77 77 :kind => entry.attributes['kind'],
78 78 :size => (entry.elements['size'] and entry.elements['size'].text).to_i,
79 79 :lastrev => Revision.new({
80 80 :identifier => entry.elements['commit'].attributes['revision'],
81 81 :time => Time.parse(entry.elements['commit'].elements['date'].text).localtime,
82 82 :author => (entry.elements['commit'].elements['author'] ? entry.elements['commit'].elements['author'].text : "")
83 83 })
84 84 })
85 85 end
86 86 rescue Exception => e
87 87 logger.error("Error parsing svn output: #{e.message}")
88 88 logger.error("Output was:\n #{output}")
89 89 end
90 90 end
91 91 return nil if $? && $?.exitstatus != 0
92 92 logger.debug("Found #{entries.size} entries in the repository for #{target(path)}") if logger && logger.debug?
93 93 entries.sort_by_name
94 94 rescue Errno::ENOENT => e
95 95 raise CommandFailed
96 96 end
97 97
98 98 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
99 99 path ||= ''
100 100 identifier_from = 'HEAD' unless identifier_from and identifier_from.to_i > 0
101 101 identifier_to = 1 unless identifier_to and identifier_to.to_i > 0
102 102 revisions = Revisions.new
103 103 cmd = "#{SVN_BIN} log --xml -r #{identifier_from}:#{identifier_to}"
104 cmd << " --username #{@login} --password #{@password}" if @login
104 cmd << credentials_string
105 105 cmd << " --verbose " if options[:with_paths]
106 106 cmd << target(path)
107 107 shellout(cmd) do |io|
108 108 begin
109 109 doc = REXML::Document.new(io)
110 110 doc.elements.each("log/logentry") do |logentry|
111 111 paths = []
112 112 logentry.elements.each("paths/path") do |path|
113 113 paths << {:action => path.attributes['action'],
114 114 :path => path.text,
115 115 :from_path => path.attributes['copyfrom-path'],
116 116 :from_revision => path.attributes['copyfrom-rev']
117 117 }
118 118 end
119 119 paths.sort! { |x,y| x[:path] <=> y[:path] }
120 120
121 121 revisions << Revision.new({:identifier => logentry.attributes['revision'],
122 122 :author => (logentry.elements['author'] ? logentry.elements['author'].text : ""),
123 123 :time => Time.parse(logentry.elements['date'].text).localtime,
124 124 :message => logentry.elements['msg'].text,
125 125 :paths => paths
126 126 })
127 127 end
128 128 rescue
129 129 end
130 130 end
131 131 return nil if $? && $?.exitstatus != 0
132 132 revisions
133 133 rescue Errno::ENOENT => e
134 134 raise CommandFailed
135 135 end
136 136
137 137 def diff(path, identifier_from, identifier_to=nil, type="inline")
138 138 path ||= ''
139 139 if identifier_to and identifier_to.to_i > 0
140 140 identifier_to = identifier_to.to_i
141 141 else
142 142 identifier_to = identifier_from.to_i - 1
143 143 end
144 144 cmd = "#{SVN_BIN} diff -r "
145 145 cmd << "#{identifier_to}:"
146 146 cmd << "#{identifier_from}"
147 147 cmd << "#{target(path)}@#{identifier_from}"
148 cmd << " --username #{@login} --password #{@password}" if @login
148 cmd << credentials_string
149 149 diff = []
150 150 shellout(cmd) do |io|
151 151 io.each_line do |line|
152 152 diff << line
153 153 end
154 154 end
155 155 return nil if $? && $?.exitstatus != 0
156 156 DiffTableList.new diff, type
157 157 rescue Errno::ENOENT => e
158 158 raise CommandFailed
159 159 end
160 160
161 161 def cat(path, identifier=nil)
162 162 identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
163 163 cmd = "#{SVN_BIN} cat #{target(path)}@#{identifier}"
164 cmd << " --username #{@login} --password #{@password}" if @login
164 cmd << credentials_string
165 165 cat = nil
166 166 shellout(cmd) do |io|
167 167 io.binmode
168 168 cat = io.read
169 169 end
170 170 return nil if $? && $?.exitstatus != 0
171 171 cat
172 172 rescue Errno::ENOENT => e
173 173 raise CommandFailed
174 174 end
175
176 private
177
178 def credentials_string
179 str = ''
180 str << " --username #{shell_quote(@login)}" unless @login.blank?
181 str << " --password #{shell_quote(@password)}" unless @login.blank? || @password.blank?
182 str
183 end
175 184 end
176 185 end
177 186 end
178 187 end
General Comments 0
You need to be logged in to leave comments. Login now