winrm 0.0.6 → 1.0.0rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,356 @@
1
+ module WinRM
2
+ # This is the main class that does the SOAP request/response logic. There are a few helper classes, but pretty
3
+ # much everything comes through here first.
4
+ class WinRMWebService
5
+
6
+ DEFAULT_TIMEOUT = 'PT60S'
7
+ DEFAULT_MAX_ENV_SIZE = 153600
8
+ DEFAULT_LOCALE = 'en-US'
9
+
10
+ # @param [String,URI] endpoint the WinRM webservice endpoint
11
+ # @param [Symbol] transport either :kerberos(default)/:ssl/:plaintext
12
+ # @param [Hash] opts Misc opts for the various transports.
13
+ # @see WinRM::HTTP::HttpTransport
14
+ # @see WinRM::HTTP::HttpGSSAPI
15
+ # @see WinRM::HTTP::HttpSSL
16
+ def initialize(endpoint, transport = :kerberos, opts = {})
17
+ @timeout = DEFAULT_TIMEOUT
18
+ @max_env_sz = DEFAULT_MAX_ENV_SIZE
19
+ @locale = DEFAULT_LOCALE
20
+ case transport
21
+ when :kerberos
22
+ # TODO: check fo keys and throw error if missing
23
+ @xfer = HTTP::HttpGSSAPI.new(endpoint, opts[:realm], opts[:service], opts[:keytab])
24
+ when :plaintext
25
+ @xfer = HTTP::HttpPlaintext.new(endpoint, opts[:user], opts[:pass])
26
+ when :ssl
27
+ @xfer = HTTP::HttpSSL.new(endpoint, opts[:user], opts[:pass], opts[:ca_trust_path])
28
+ end
29
+ end
30
+
31
+ # Operation timeout
32
+ def op_timeout(sec)
33
+ @timeout = Iso8601Duration.sec_to_dur(sec)
34
+ end
35
+
36
+ # Max envelope size
37
+ def max_env_size(sz)
38
+ @max_env_sz = sz
39
+ end
40
+
41
+ # Default locale
42
+ def locale(locale)
43
+ @locale = locale
44
+ end
45
+
46
+ # Create a Shell on the destination host
47
+ # @param [String<optional>] i_stream Which input stream to open. Leave this alone unless you know what you're doing
48
+ # @param [String<optional>] o_stream Which output stream to open. Leave this alone unless you know what you're doing
49
+ # @return [String] The ShellId from the SOAP response. This is our open shell instance on the remote machine.
50
+ def open_shell(i_stream = 'stdin', o_stream = 'stdout stderr')
51
+ s = Savon::SOAP::XML.new
52
+ s.version = 2
53
+ s.namespaces.merge!(namespaces)
54
+ h_opts = { "#{NS_WSMAN_DMTF}:OptionSet" => { "#{NS_WSMAN_DMTF}:Option" => ['FALSE',437],
55
+ :attributes! => {"#{NS_WSMAN_DMTF}:Option" => {'Name' => ['WINRS_NOPROFILE','WINRS_CODEPAGE']}}}}
56
+ s.header.merge!(merge_headers(header,resource_uri_cmd,action_create,h_opts))
57
+ s.input = "#{NS_WIN_SHELL}:Shell"
58
+ s.body = { "#{NS_WIN_SHELL}:InputStreams" => i_stream,
59
+ "#{NS_WIN_SHELL}:OutputStreams" => o_stream}
60
+
61
+ resp = send_message(s.to_xml)
62
+ (resp/"//*[@Name='ShellId']").text
63
+ end
64
+
65
+ # Run a command on a machine with an open shell
66
+ # @param [String] shell_id The shell id on the remote machine. See #open_shell
67
+ # @param [String] command The command to run on the remote machine
68
+ # @return [String] The CommandId from the SOAP response. This is the ID we need to query in order to get output.
69
+ def run_command(shell_id, command)
70
+ s = Savon::SOAP::XML.new
71
+ s.version = 2
72
+ s.namespaces.merge!(namespaces)
73
+ h_opts = { "#{NS_WSMAN_DMTF}:OptionSet" => {
74
+ "#{NS_WSMAN_DMTF}:Option" => ['TRUE','FALSE'],
75
+ :attributes! => {"#{NS_WSMAN_DMTF}:Option" => {'Name' => ['WINRS_CONSOLEMODE_STDIN','WINRS_SKIP_CMD_SHELL']}}}
76
+ }
77
+ s.header.merge!(merge_headers(header,resource_uri_cmd,action_command,h_opts,selector_shell_id(shell_id)))
78
+ s.input = "#{NS_WIN_SHELL}:CommandLine"
79
+ s.body = { "#{NS_WIN_SHELL}:Command" => "\"#{command}\"" }
80
+
81
+ resp = send_message(s.to_xml)
82
+ (resp/"//#{NS_WIN_SHELL}:CommandId").text
83
+ end
84
+
85
+ # Get the Output of the given shell and command
86
+ # @param [String] shell_id The shell id on the remote machine. See #open_shell
87
+ # @param [String] command_id The command id on the remote machine. See #run_command
88
+ # @return [Hash] Returns a Hash with a key :exitcode and :data. Data is an Array of Hashes where the cooresponding key
89
+ # is either :stdout or :stderr. The reason it is in an Array so so we can get the output in the order it ocurrs on
90
+ # the console.
91
+ def get_command_output(shell_id, command_id, &block)
92
+ s = Savon::SOAP::XML.new
93
+ s.version = 2
94
+ s.namespaces.merge!(namespaces)
95
+ s.header.merge!(merge_headers(header,resource_uri_cmd,action_receive,selector_shell_id(shell_id)))
96
+ s.input = "#{NS_WIN_SHELL}:Receive"
97
+ s.body = { "#{NS_WIN_SHELL}:DesiredStream" => 'stdout stderr',
98
+ :attributes! => {"#{NS_WIN_SHELL}:DesiredStream" => {'CommandId' => command_id}}}
99
+
100
+ resp = send_message(s.to_xml)
101
+ output = {:data => []}
102
+ (resp/"//#{NS_WIN_SHELL}:Stream").each do |n|
103
+ next if n.text.nil? || n.text.empty?
104
+ stream = {n['Name'].to_sym => Base64.decode64(n.text)}
105
+ output[:data] << stream
106
+ yield stream[:stdout], stream[:stderr] if block_given?
107
+ end
108
+
109
+ # We may need to get additional output if the stream has not finished.
110
+ # The CommandState will change from Running to Done like so:
111
+ # @example
112
+ # from...
113
+ # <rsp:CommandState CommandId="..." State="http://schemas.microsoft.com/wbem/wsman/1/windows/shell/CommandState/Running"/>
114
+ # to...
115
+ # <rsp:CommandState CommandId="..." State="http://schemas.microsoft.com/wbem/wsman/1/windows/shell/CommandState/Done">
116
+ # <rsp:ExitCode>0</rsp:ExitCode>
117
+ # </rsp:CommandState>
118
+ if((resp/"//#{NS_WIN_SHELL}:ExitCode").empty?)
119
+ output.merge!(get_command_output(shell_id,command_id,&block)) do |key, old_data, new_data|
120
+ old_data += new_data
121
+ end
122
+ else
123
+ output[:exitcode] = (resp/"//#{NS_WIN_SHELL}:ExitCode").text.to_i
124
+ end
125
+ output
126
+ end
127
+
128
+ # Clean-up after a command.
129
+ # @see #run_command
130
+ # @param [String] shell_id The shell id on the remote machine. See #open_shell
131
+ # @param [String] command_id The command id on the remote machine. See #run_command
132
+ # @return [true] This should have more error checking but it just returns true for now.
133
+ def cleanup_command(shell_id, command_id)
134
+ s = Savon::SOAP::XML.new
135
+ s.version = 2
136
+ s.namespaces.merge!(namespaces)
137
+ s.header.merge!(merge_headers(header,resource_uri_cmd,action_signal,selector_shell_id(shell_id)))
138
+
139
+ # Signal the Command references to terminate (close stdout/stderr)
140
+ s.input = ["#{NS_WIN_SHELL}:Signal", {'CommandId' => command_id}]
141
+
142
+ s.body = { "#{NS_WIN_SHELL}:Code" => 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/signal/terminate' }
143
+ resp = send_message(s.to_xml)
144
+ true
145
+ end
146
+
147
+ # Close the shell
148
+ # @param [String] shell_id The shell id on the remote machine. See #open_shell
149
+ # @return [true] This should have more error checking but it just returns true for now.
150
+ def close_shell(shell_id)
151
+ s = Savon::SOAP::XML.new
152
+ s.version = 2
153
+ s.namespaces.merge!(namespaces)
154
+ s.namespaces.merge!(Savon::SOAP::XML::SchemaTypes)
155
+ s.header.merge!(merge_headers(header,resource_uri_cmd,action_delete,selector_shell_id(shell_id)))
156
+
157
+ # Because Savon does not support a nil Body we have to build it ourselves.
158
+ s.xml do |b|
159
+ b.tag!('env:Envelope', s.namespaces) do
160
+ b.tag!('env:Header') do |bh|
161
+ bh << Gyoku.xml(s.header) unless s.header.empty?
162
+ end
163
+ if(s.input.nil?)
164
+ b.tag! 'env:Body'
165
+ else
166
+ b.tag! 'env:Body' do |bb|
167
+ bb.tag! s.input do |bbb|
168
+ bbb << Gyoku.xml(s.body) if s.body
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end
174
+
175
+ resp = send_message(s.to_xml)
176
+
177
+ resp.remove_namespaces!
178
+ (resp/"//Fault").empty?
179
+ end
180
+
181
+ # Run a CMD command
182
+ # @param [String] command The command to run on the remote system
183
+ # @return [Hash] :stdout and :stderr
184
+ def run_cmd(command, &block)
185
+ shell_id = open_shell
186
+ command_id = run_command(shell_id, command)
187
+ command_output = get_command_output(shell_id, command_id, &block)
188
+ cleanup_command(shell_id, command_id)
189
+ close_shell(shell_id)
190
+ command_output
191
+ end
192
+ alias :cmd :run_cmd
193
+
194
+
195
+ # Run a Powershell script that resides on the local box.
196
+ # @param [IO,String] script_file an IO reference for reading the Powershell script or the actual file contents
197
+ # @return [Hash] :stdout and :stderr
198
+ def run_powershell_script(script_file, &block)
199
+ # if an IO object is passed read it..otherwise assume the contents of the file were passed
200
+ script = script_file.kind_of?(IO) ? script_file.read : script_file
201
+
202
+ script = script.chars.to_a.join("\x00").chomp
203
+ if(defined?(script.encode))
204
+ script = script.encode('ASCII-8BIT')
205
+ script = Base64.strict_encode64(script)
206
+ else
207
+ script = Base64.encode64(script).chomp
208
+ end
209
+
210
+ shell_id = open_shell
211
+ command_id = run_command(shell_id, "powershell -encodedCommand #{script}")
212
+ command_output = get_command_output(shell_id, command_id, &block)
213
+ cleanup_command(shell_id, command_id)
214
+ close_shell(shell_id)
215
+ command_output
216
+ end
217
+
218
+
219
+ # Run a WQL Query
220
+ # @see http://msdn.microsoft.com/en-us/library/aa394606(VS.85).aspx
221
+ # @param [String] wql The WQL query
222
+ # @return [Array<Hash>] Returns an array of Hashes that contain the key/value pairs returned from the query.
223
+ def run_wql(wql)
224
+ header = {}.merge(resource_uri_wmi).merge(action_enumerate)
225
+
226
+ begin
227
+ resp = invoke("#{NS_ENUM}:Enumerate", {:soap_action => :auto, :http_options => nil, :soap_header => header}) do |enum|
228
+ enum.add("#{NS_WSMAN_DMTF}:OptimizeEnumeration")
229
+ enum.add("#{NS_WSMAN_DMTF}:MaxElements",'32000')
230
+ mattr = nil
231
+ enum.add("#{NS_WSMAN_DMTF}:Filter", wql) do |filt|
232
+ filt.set_attr('Dialect','http://schemas.microsoft.com/wbem/wsman/1/WQL')
233
+ end
234
+ end
235
+ rescue Handsoap::Fault => e
236
+ raise WinRMWebServiceError, e.reason
237
+ end
238
+
239
+ query_response = []
240
+ (resp/"//#{NS_ENUM}:EnumerateResponse//#{NS_WSMAN_DMTF}:Items/*").each do |i|
241
+ qitem = {}
242
+ (i/'*').each do |si|
243
+ qitem[si.node_name] = si.to_s
244
+ end
245
+ query_response << qitem
246
+ end
247
+ query_response
248
+ end
249
+
250
+
251
+
252
+ private
253
+
254
+ def namespaces
255
+ {'xmlns:a' => 'http://schemas.xmlsoap.org/ws/2004/08/addressing',
256
+ 'xmlns:b' => 'http://schemas.dmtf.org/wbem/wsman/1/cimbinding.xsd',
257
+ 'xmlns:n' => 'http://schemas.xmlsoap.org/ws/2004/09/enumeration',
258
+ 'xmlns:x' => 'http://schemas.xmlsoap.org/ws/2004/09/transfer',
259
+ 'xmlns:w' => 'http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd',
260
+ 'xmlns:p' => 'http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd',
261
+ 'xmlns:rsp' => 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell'}
262
+ end
263
+
264
+ def header
265
+ { "#{NS_ADDRESSING}:To" => "#{@xfer.endpoint.to_s}",
266
+ "#{NS_ADDRESSING}:ReplyTo" => {
267
+ "#{NS_ADDRESSING}:Address" => 'http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous',
268
+ :attributes! => {"#{NS_ADDRESSING}:Address" => {'mustUnderstand' => true}}},
269
+ "#{NS_WSMAN_DMTF}:MaxEnvelopeSize" => @max_env_sz,
270
+ "#{NS_ADDRESSING}:MessageID" => "uuid:#{UUID.generate.upcase}",
271
+ "#{NS_WSMAN_DMTF}:Locale/" => '',
272
+ "#{NS_WSMAN_MSFT}:DataLocale/" => '',
273
+ "#{NS_WSMAN_DMTF}:OperationTimeout" => @timeout,
274
+ :attributes! => {
275
+ "#{NS_WSMAN_DMTF}:MaxEnvelopeSize" => {'mustUnderstand' => true},
276
+ "#{NS_WSMAN_DMTF}:Locale/" => {'xml:lang' => @locale, 'mustUnderstand' => false},
277
+ "#{NS_WSMAN_MSFT}:DataLocale/" => {'xml:lang' => @locale, 'mustUnderstand' => false}
278
+ }}
279
+ end
280
+
281
+ # merge the various header hashes and make sure we carry all of the attributes
282
+ # through instead of overwriting them.
283
+ def merge_headers(*headers)
284
+ hdr = {}
285
+ headers.each do |h|
286
+ hdr.merge!(h) do |k,v1,v2|
287
+ v1.merge!(v2) if k == :attributes!
288
+ end
289
+ end
290
+ hdr
291
+ end
292
+
293
+ def send_message(message)
294
+ resp = @xfer.send_request(message)
295
+
296
+ errors = resp/"//#{NS_SOAP_ENV}:Body/#{NS_SOAP_ENV}:Fault/*"
297
+ if errors.empty?
298
+ return resp
299
+ else
300
+ resp.root.add_namespace(NS_WSMAN_FAULT,'http://schemas.microsoft.com/wbem/wsman/1/wsmanfault')
301
+ fault = (errors/"//#{NS_WSMAN_FAULT}:WSManFault").first
302
+ raise WinRMWSManFault, "[WSMAN ERROR CODE: #{fault['Code']}]: #{fault.text}"
303
+ end
304
+ end
305
+
306
+ # Helper methods for SOAP Headers
307
+
308
+ def resource_uri_cmd
309
+ {"#{NS_WSMAN_DMTF}:ResourceURI" => 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd',
310
+ :attributes! => {"#{NS_WSMAN_DMTF}:ResourceURI" => {'mustUnderstand' => true}}}
311
+ end
312
+
313
+ def resource_uri_wmi(namespace = 'root/cimv2/*')
314
+ {"#{NS_WSMAN_DMTF}:ResourceURI" => "http://schemas.microsoft.com/wbem/wsman/1/wmi/#{namespace}",
315
+ :attributes! => {"#{NS_WSMAN_DMTF}:ResourceURI" => {'mustUnderstand' => true}}}
316
+ end
317
+
318
+ def action_create
319
+ {"#{NS_ADDRESSING}:Action" => 'http://schemas.xmlsoap.org/ws/2004/09/transfer/Create',
320
+ :attributes! => {"#{NS_ADDRESSING}:Action" => {'mustUnderstand' => true}}}
321
+ end
322
+
323
+ def action_delete
324
+ {"#{NS_ADDRESSING}:Action" => 'http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete',
325
+ :attributes! => {"#{NS_ADDRESSING}:Action" => {'mustUnderstand' => true}}}
326
+ end
327
+
328
+ def action_command
329
+ {"#{NS_ADDRESSING}:Action" => 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Command',
330
+ :attributes! => {"#{NS_ADDRESSING}:Action" => {'mustUnderstand' => true}}}
331
+ end
332
+
333
+ def action_receive
334
+ {"#{NS_ADDRESSING}:Action" => 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive',
335
+ :attributes! => {"#{NS_ADDRESSING}:Action" => {'mustUnderstand' => true}}}
336
+ end
337
+
338
+ def action_signal
339
+ {"#{NS_ADDRESSING}:Action" => 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Signal',
340
+ :attributes! => {"#{NS_ADDRESSING}:Action" => {'mustUnderstand' => true}}}
341
+ end
342
+
343
+ def action_enumerate
344
+ {"#{NS_ADDRESSING}:Action" => 'http://schemas.xmlsoap.org/ws/2004/09/enumeration/Enumerate',
345
+ :attributes! => {"#{NS_ADDRESSING}:Action" => {'mustUnderstand' => true}}}
346
+ end
347
+
348
+ def selector_shell_id(shell_id)
349
+ {"#{NS_WSMAN_DMTF}:SelectorSet" =>
350
+ {"#{NS_WSMAN_DMTF}:Selector" => shell_id, :attributes! => {"#{NS_WSMAN_DMTF}:Selector" => {'Name' => 'ShellId'}}}
351
+ }
352
+ end
353
+
354
+ end # WinRMWebService
355
+ end # WinRM
356
+
data/winrm.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'date'
3
+
4
+ version = File.read(File.expand_path("../VERSION", __FILE__)).strip
5
+
6
+ Gem::Specification.new do |s|
7
+ s.platform = Gem::Platform::RUBY
8
+ s.name = 'winrm'
9
+ s.version = version
10
+ s.date = Date.today.to_s
11
+
12
+ s.author = 'Dan Wanek'
13
+ s.email = 'dan.wanek@gmail.com'
14
+ s.homepage = "http://github.com/zenchild/WinRM"
15
+
16
+ s.summary = 'Ruby library for Windows Remote Management'
17
+ s.description = <<-EOF
18
+ Ruby library for Windows Remote Management
19
+ EOF
20
+
21
+ s.files = `git ls-files`.split(/\n/)
22
+ s.require_path = "lib"
23
+ s.rdoc_options = %w(-x test/ -x examples/)
24
+ s.extra_rdoc_files = %w(README COPYING.txt)
25
+
26
+ s.required_ruby_version = '>= 1.9.0'
27
+ s.add_runtime_dependency 'gssapi'
28
+ s.add_runtime_dependency 'nokogiri'
29
+ s.add_runtime_dependency 'httpclient'
30
+ s.add_runtime_dependency 'rubyntlm'
31
+ s.add_runtime_dependency 'uuid'
32
+ s.add_runtime_dependency 'savon'
33
+ end
metadata CHANGED
@@ -1,12 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: winrm
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 0
8
- - 6
9
- version: 0.0.6
4
+ prerelease: 5
5
+ version: 1.0.0rc1
10
6
  platform: ruby
11
7
  authors:
12
8
  - Dan Wanek
@@ -14,19 +10,17 @@ autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
12
 
17
- date: 2010-10-22 00:00:00 -05:00
13
+ date: 2011-02-28 00:00:00 -06:00
18
14
  default_executable:
19
15
  dependencies:
20
16
  - !ruby/object:Gem::Dependency
21
- name: handsoap
17
+ name: gssapi
22
18
  prerelease: false
23
19
  requirement: &id001 !ruby/object:Gem::Requirement
24
20
  none: false
25
21
  requirements:
26
22
  - - ">="
27
23
  - !ruby/object:Gem::Version
28
- segments:
29
- - 0
30
24
  version: "0"
31
25
  type: :runtime
32
26
  version_requirements: *id001
@@ -38,8 +32,6 @@ dependencies:
38
32
  requirements:
39
33
  - - ">="
40
34
  - !ruby/object:Gem::Version
41
- segments:
42
- - 0
43
35
  version: "0"
44
36
  type: :runtime
45
37
  version_requirements: *id002
@@ -51,8 +43,6 @@ dependencies:
51
43
  requirements:
52
44
  - - ">="
53
45
  - !ruby/object:Gem::Version
54
- segments:
55
- - 0
56
46
  version: "0"
57
47
  type: :runtime
58
48
  version_requirements: *id003
@@ -64,8 +54,6 @@ dependencies:
64
54
  requirements:
65
55
  - - ">="
66
56
  - !ruby/object:Gem::Version
67
- segments:
68
- - 0
69
57
  version: "0"
70
58
  type: :runtime
71
59
  version_requirements: *id004
@@ -77,11 +65,20 @@ dependencies:
77
65
  requirements:
78
66
  - - ">="
79
67
  - !ruby/object:Gem::Version
80
- segments:
81
- - 0
82
68
  version: "0"
83
69
  type: :runtime
84
70
  version_requirements: *id005
71
+ - !ruby/object:Gem::Dependency
72
+ name: savon
73
+ prerelease: false
74
+ requirement: &id006 !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: "0"
80
+ type: :runtime
81
+ version_requirements: *id006
85
82
  description: " Ruby library for Windows Remote Management\n"
86
83
  email: dan.wanek@gmail.com
87
84
  executables: []
@@ -96,16 +93,18 @@ files:
96
93
  - README
97
94
  - Rakefile
98
95
  - VERSION
99
- - lib/extensions/string.rb
100
- - lib/soap/exceptions/exceptions.rb
101
- - lib/soap/soap_provider.rb
102
- - lib/soap/winrm_service.rb
103
96
  - lib/winrm.rb
97
+ - lib/winrm/exceptions/exceptions.rb
98
+ - lib/winrm/helpers/iso8601_duration.rb
99
+ - lib/winrm/http/transport.rb
100
+ - lib/winrm/soap_provider.rb
101
+ - lib/winrm/winrm_service.rb
104
102
  - preamble
105
103
  - test/spec/powershell_tests.spec
106
104
  - test/spec/spec.opts
107
105
  - test/spec/test.ps1
108
106
  - test/spec/wql_tests.spec
107
+ - winrm.gemspec
109
108
  has_rdoc: true
110
109
  homepage: http://github.com/zenchild/WinRM
111
110
  licenses: []
@@ -123,23 +122,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
123
122
  requirements:
124
123
  - - ">="
125
124
  - !ruby/object:Gem::Version
126
- segments:
127
- - 1
128
- - 8
129
- - 7
130
- version: 1.8.7
125
+ version: 1.9.0
131
126
  required_rubygems_version: !ruby/object:Gem::Requirement
132
127
  none: false
133
128
  requirements:
134
- - - ">="
129
+ - - ">"
135
130
  - !ruby/object:Gem::Version
136
- segments:
137
- - 0
138
- version: "0"
131
+ version: 1.3.1
139
132
  requirements: []
140
133
 
141
134
  rubyforge_project:
142
- rubygems_version: 1.3.7
135
+ rubygems_version: 1.5.0
143
136
  signing_key:
144
137
  specification_version: 3
145
138
  summary: Ruby library for Windows Remote Management