rapi 0.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.0
1
+ 0.1.0
@@ -0,0 +1,462 @@
1
+ require 'ffi'
2
+ require 'iconv'
3
+
4
+ class RAPI
5
+
6
+ attr_accessor :copy_buffer_size
7
+
8
+ def initialize
9
+ @connected = false
10
+ @copy_buffer_size = 0x1000
11
+ end
12
+
13
+ def connected?
14
+ @connected
15
+ end
16
+
17
+ def connect(timeout_seconds = 1)
18
+ self.disconnect if connected?
19
+
20
+ init = Native::Rapi::RAPIINIT.new
21
+ init[:cbSize] = Native::Rapi::RAPIINIT.size
22
+ ret = Native::Rapi.CeRapiInitEx(init)
23
+ handle_hresult! ret
24
+ init_event = init[:heRapiInit]
25
+
26
+ timeout = timeout_seconds * 4
27
+ infinite_timeout = timeout < 0
28
+
29
+ begin
30
+ ret = Native::Kernel32.WaitForSingleObject(init_event, 250)
31
+
32
+ if ret == Native::WAIT_FAILED || ret == Native::WAIT_ABANDONED
33
+ Native::Kernel32.CloseHandle(init_event)
34
+ Native::Rapi.CeRapiUninit
35
+ raise RAPIException, "Failed to Initialize RAPI"
36
+ end
37
+
38
+ if !infinite_timeout
39
+ if (timeout -= 1) < 0
40
+ Native::Kernel32.CloseHandle(init_event)
41
+ Native::Rapi.CeRapiUninit
42
+ raise RAPIException, "Timeout waiting for device connection"
43
+ end
44
+ end
45
+ end while ret != Native::WAIT_OBJECT_0
46
+
47
+ @connected = true
48
+ Native::Kernel32.CloseHandle(init_event)
49
+
50
+ true
51
+ end
52
+
53
+ def disconnect
54
+ if connected?
55
+ Native::Rapi.CeRapiUninit
56
+ @connected = false
57
+ end
58
+
59
+ true
60
+ end
61
+
62
+ def exist?(remote_file_name)
63
+ check_connection()
64
+
65
+ Native::Rapi::CeGetFileAttributes(to_utf16(remote_file_name)) != 0xFFFFFFFF
66
+ end
67
+
68
+ alias exists? exist?
69
+
70
+ def download(remote_file_name, local_file_name, overwrite = false)
71
+ check_connection()
72
+
73
+ if !overwrite && File.exists?(local_file_name)
74
+ raise RAPIException, "A local file with the given name already exists"
75
+ end
76
+
77
+ remote_file = Native::Rapi.CeCreateFile(to_utf16(remote_file_name), Native::GENERIC_READ, 0, 0, Native::OPEN_EXISTING, Native::FILE_ATTRIBUTE_NORMAL, 0)
78
+ if remote_file == Native::INVALID_HANDLE
79
+ raise RAPIException, "Could not open remote file"
80
+ end
81
+
82
+ File.open(local_file_name, "wb") do |f|
83
+ buffer = FFI::MemoryPointer.new(1, @copy_buffer_size)
84
+ bytes_read_ptr = FFI::MemoryPointer.new(FFI::Type::INT.size)
85
+
86
+ while true
87
+ ret = Native::Rapi.CeReadFile(remote_file, buffer, buffer.size, bytes_read_ptr, 0)
88
+
89
+ bytes_read = bytes_read_ptr.get_int(0)
90
+
91
+ if bytes_read != 0 && ret == 0
92
+ buffer.free
93
+ bytes_read_ptr.free
94
+ Native::Rapi.CeCloseHandle(remote_file)
95
+ raise RAPIException, "Failed to read device data"
96
+ elsif bytes_read == 0
97
+ break
98
+ end
99
+
100
+ f << buffer.get_bytes(0, bytes_read)
101
+ end
102
+
103
+ buffer.free
104
+ bytes_read_ptr.free
105
+ Native::Rapi.CeCloseHandle(remote_file)
106
+ end
107
+
108
+ true
109
+ end
110
+
111
+ def upload(local_file_name, remote_file_name, overwrite = false)
112
+ check_connection()
113
+
114
+ create = overwrite ? Native::CREATE_ALWAYS : Native::CREATE_NEW
115
+ remote_file = Native::Rapi.CeCreateFile(to_utf16(remote_file_name), Native::GENERIC_WRITE, 0, 0, create, Native::FILE_ATTRIBUTE_NORMAL, 0)
116
+
117
+ if remote_file == Native::INVALID_HANDLE
118
+ raise RAPIException, "Could not create remote file"
119
+ end
120
+
121
+ if File.size(local_file_name) != 0
122
+ File.open(local_file_name, "rb") do |f|
123
+ while buffer = f.read(copy_buffer_size)
124
+ if Native::Rapi.CeWriteFile(remote_file, buffer, buffer.size, nil, 0) == 0
125
+ Native::Rapi.CeCloseHandle(remote_file)
126
+ raise RAPIException, "Could not write to remote file"
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ Native::Rapi.CeCloseHandle(remote_file)
133
+
134
+ true
135
+ end
136
+
137
+ def copy(existing_file_name, new_file_name, overwrite = false)
138
+ check_connection()
139
+
140
+ if Native::Rapi.CeCopyFile(to_utf16(existing_file_name), to_utf16(new_file_name), overwrite ? 0 : 1) == 0
141
+ raise RAPIException, "Cannot copy file"
142
+ end
143
+
144
+ true
145
+ end
146
+
147
+ def delete(file_name)
148
+ check_connection()
149
+
150
+ if Native::Rapi.CeDeleteFile(to_utf16(file_name)) == 0
151
+ raise RAPIException, "Could not delete file"
152
+ end
153
+
154
+ true
155
+ end
156
+
157
+ def move(existing_file_name, new_file_name)
158
+ check_connection()
159
+
160
+ if Native::Rapi.CeMoveFile(to_utf16(existing_file_name), to_utf16(new_file_name)) == 0
161
+ raise RAPIException, "Cannot move file"
162
+ end
163
+
164
+ true
165
+ end
166
+
167
+ def get_attributes(file_name)
168
+ check_connection()
169
+
170
+ ret = Native::Rapi.CeGetFileAttributes(to_utf16(file_name))
171
+ if ret == 0xFFFFFFFF
172
+ raise RAPIException, "Could not get file attributes"
173
+ end
174
+
175
+ FileAttributes.new(ret)
176
+ end
177
+
178
+ alias get_attrs get_attributes
179
+
180
+ def set_attributes(file_name, attributes)
181
+ check_connection()
182
+
183
+ if Native::Rapi.CeSetFileAttributes(to_utf16(file_name), attributes.to_i) == 0
184
+ raise RAPIExcpetion, "Cannot set device file attributes"
185
+ end
186
+ end
187
+
188
+ alias set_attrs set_attributes
189
+
190
+ def search(file_name)
191
+ check_connection()
192
+
193
+ find_data = Native::Rapi::CE_FIND_DATA.new
194
+
195
+ file_infos = []
196
+ handle = Native::Rapi.CeFindFirstFile(to_utf16(file_name), find_data)
197
+
198
+ if handle != Native::INVALID_HANDLE
199
+ file_infos << FileInformation.new(find_data)
200
+ find_data.pointer.clear
201
+
202
+ while Native::Rapi.CeFindNextFile(handle, find_data) != 0
203
+ file_infos << FileInformation.new(find_data)
204
+ find_data.pointer.clear
205
+ end
206
+
207
+ Native::Rapi.CeFindClose(handle)
208
+ end
209
+
210
+ file_infos
211
+ end
212
+
213
+ alias glob search
214
+
215
+ def exec(file_name, *args)
216
+ check_connection
217
+
218
+ args = if args.empty?
219
+ nil
220
+ else
221
+ args.join(' ')
222
+ end
223
+
224
+ pi = Native::Rapi::PROCESS_INFORMATION.new
225
+
226
+ if Native::Rapi.CeCreateProcess(to_utf16(file_name), to_utf16(args), nil, nil, 0, 0, nil, nil, nil, pi) == 0
227
+ errnum = Native::Rapi.CeGetLastError
228
+ handle_hresult! errnum
229
+ end
230
+
231
+ ProcessInformation.new(pi)
232
+ end
233
+
234
+ private
235
+
236
+ def check_connection
237
+ unless connected?
238
+ raise RAPIException, "Cannot perform operation while disconnected"
239
+ end
240
+ end
241
+
242
+ def handle_hresult!(hresult)
243
+ if hresult != 0
244
+ msg_ptr = FFI::MemoryPointer.new(FFI::Pointer)
245
+ format = Native::FORMAT_MESSAGE_ALLOCATE_BUFFER | Native::FORMAT_MESSAGE_FROM_SYSTEM | Native::FORMAT_MESSAGE_IGNORE_INSERTS
246
+ len = Native::Kernel32.FormatMessageA(format, nil, hresult, 0, msg_ptr, 0, nil)
247
+ if len == 0
248
+ msg = "Error {hresult} (0x#{hresult.to_s(16).upcase})"
249
+ else
250
+ msg = msg_ptr.get_pointer(0).get_string(0)
251
+ end
252
+ Native::Kernel32.LocalFree(msg_ptr.get_pointer(0))
253
+ msg_ptr.free
254
+ raise RAPIException, msg
255
+ end
256
+ end
257
+
258
+ if RUBY_VERSION =~ /^1\.9\.\d/
259
+ def to_utf16(str)
260
+ return nil if str.nil?
261
+ str.encode("UTF-16LE") + "\0\0".force_encoding("UTF-16LE")
262
+ end
263
+ else
264
+ def to_utf16(str)
265
+ return nil if str.nil?
266
+ Iconv.conv("UTF-16LE", "ASCII", str) + "\0\0"
267
+ end
268
+ end
269
+
270
+ public
271
+
272
+ class ProcessInformation
273
+ attr_reader :process_handle
274
+ attr_reader :thread_handle
275
+ attr_reader :process_id
276
+ attr_reader :thread_id
277
+
278
+ def initialize(process_information)
279
+ @process_handle = process_information[:hProcess]
280
+ @thread_handle = process_information[:hThread]
281
+ @process_id = process_information[:dwProcessId]
282
+ @thread_id = process_information[:dwThreadId]
283
+ end
284
+ end
285
+
286
+ class FileInformation
287
+ attr_reader :attributes
288
+ attr_reader :create_time
289
+ attr_reader :last_access_time
290
+ attr_reader :last_write_time
291
+ attr_reader :size
292
+ attr_reader :name
293
+
294
+ def initialize(ce_find_data)
295
+ @attributes = FileAttributes.new(ce_find_data[:dwFileAttributes])
296
+ @create_time = ce_find_data[:ftCreationTime]
297
+ @last_access_time = ce_find_data[:ftLastAccessTime]
298
+ @last_write_time = ce_find_data[:ftLastWriteTime]
299
+ @name = encode(ce_find_data[:cFileName].to_ptr.get_bytes(0, 260))
300
+ @size = ce_find_data[:nFileSizeHigh] << 32 &&
301
+ ce_find_data[:nFileSizeLow]
302
+ end
303
+
304
+ private
305
+
306
+ if RUBY_VERSION =~ /^1\.9\.\d/
307
+ def encode(path)
308
+ path.force_encoding("UTF-16LE").strip
309
+ end
310
+ else
311
+ def encode(path)
312
+ Iconv.conv("ASCII", "UTF-16LE", path).strip
313
+ end
314
+ end
315
+ end
316
+
317
+ class Enum
318
+
319
+ private
320
+
321
+ def self.enum_attr(name, num)
322
+ name = name.to_s
323
+ define_method(name + "?") do
324
+ @attrs & num != 0
325
+ end
326
+
327
+ define_method(name + "=") do |set|
328
+ if set
329
+ @attrs |= num
330
+ else
331
+ @attrs &= ~num
332
+ end
333
+ end
334
+ end
335
+
336
+ public
337
+
338
+ def initialize(attrs = 0)
339
+ @attrs = attrs.to_i
340
+ end
341
+
342
+ def to_i
343
+ @attrs
344
+ end
345
+ end
346
+
347
+ class FileAttributes < Enum
348
+ enum_attr :readonly, 0x0001
349
+ enum_attr :hidden, 0x0002
350
+ enum_attr :system, 0x0004
351
+ enum_attr :directory, 0x0010
352
+ enum_attr :archive, 0x0020
353
+ enum_attr :in_rom, 0x0040
354
+ enum_attr :normal, 0x0080
355
+ enum_attr :temporary, 0x0100
356
+ enum_attr :sparse, 0x0200
357
+ enum_attr :reparse_point, 0x0400
358
+ enum_attr :compressed, 0x0800
359
+ enum_attr :rom_module, 0x2000
360
+ end
361
+
362
+ class RAPIException < Exception
363
+ end
364
+
365
+ module Native
366
+
367
+ # Winbase.h
368
+ WAIT_ABANDONED = 0x00000080
369
+ WAIT_FAILED = 0xFFFFFFFF
370
+ WAIT_TIMEOUT = 0x00000102
371
+ WAIT_OBJECT_0 = 0x00000000
372
+
373
+ FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
374
+ FORMAT_MESSAGE_FROM_HMODULE = 0x00000800
375
+ FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200
376
+ FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100
377
+
378
+ FILE_SHARE_READ = 0x00000001
379
+ CREATE_NEW = 0x00000001
380
+ CREATE_ALWAYS = 0x00000002
381
+ GENERIC_WRITE = 0x40000000
382
+ GENERIC_READ = 0x80000000
383
+ OPEN_EXISTING = 0x00000003
384
+ FILE_ATTRIBUTE_NORMAL = 0x80
385
+ INVALID_HANDLE = FFI::Pointer.new(-1)
386
+
387
+ class FILETIME
388
+ extend FFI::DataConverter
389
+ native_type :uint64
390
+
391
+ def self.from_native(val, ctx)
392
+ Time.at((val * 1.0e-07) + Time.new(1601).to_f)
393
+ end
394
+
395
+ def self.to_native(val, ctx)
396
+ ((val.to_f - Time.new(1601).to_f) / 1.0e-07).to_i
397
+ end
398
+ end
399
+
400
+ module Rapi
401
+ extend FFI::Library
402
+ ffi_lib 'rapi.dll'
403
+ ffi_convention :stdcall
404
+
405
+ class RAPIINIT < FFI::Struct
406
+ layout :cbSize, :int,
407
+ :heRapiInit, :pointer,
408
+ :hrRapiInit, :int
409
+ end
410
+
411
+ class CE_FIND_DATA < FFI::Struct
412
+ layout :dwFileAttributes, :uint, 0,
413
+ :ftCreationTime, FILETIME, 4,
414
+ :ftLastAccessTime, FILETIME, 12,
415
+ :ftLastWriteTime, FILETIME, 20,
416
+ :nFileSizeHigh, :uint, 28,
417
+ :nFileSizeLow, :uint, 32,
418
+ :dwOID, :uint, 36,
419
+ :cFileName, [:uint8, 260], 40
420
+ end
421
+
422
+ class PROCESS_INFORMATION < FFI::Struct
423
+ layout :hProcess, :pointer,
424
+ :hThread, :pointer,
425
+ :dwProcessId, :uint,
426
+ :dwThreadId, :uint
427
+ end
428
+
429
+ attach_function :CeRapiInitEx, [RAPIINIT.by_ref], :int
430
+ attach_function :CeRapiUninit, [], :int
431
+ attach_function :CeRapiGetError, [], :int
432
+ attach_function :CeCloseHandle, [:pointer], :int
433
+ attach_function :CeWriteFile, [:pointer, :pointer, :int, :pointer, :int], :int
434
+ attach_function :CeReadFile, [:pointer, :pointer, :int, :pointer, :int], :int
435
+ attach_function :CeRapiFreeBuffer, [:pointer], :void
436
+ attach_function :CeGetFileAttributes, [:pointer], :uint
437
+ attach_function :CeCreateFile, [:pointer, :uint, :int, :int, :int, :int, :int], :pointer
438
+ attach_function :CeCopyFile, [:pointer, :pointer, :int], :int
439
+ attach_function :CeDeleteFile, [:pointer], :int
440
+ attach_function :CeGetFileAttributes, [:pointer], :uint
441
+ attach_function :CeSetFileAttributes, [:pointer, :uint], :int
442
+ attach_function :CeFindFirstFile, [:pointer, CE_FIND_DATA.by_ref], :pointer
443
+ attach_function :CeFindNextFile, [:pointer, CE_FIND_DATA.by_ref], :int
444
+ attach_function :CeFindClose, [:pointer], :int
445
+ attach_function :CeCreateProcess, [:pointer, :pointer, :pointer, :pointer, :int, :int, :pointer, :pointer, :pointer, PROCESS_INFORMATION.ptr], :int
446
+ attach_function :CeGetLastError, [], :int
447
+ end
448
+
449
+ module Kernel32
450
+ extend FFI::Library
451
+ ffi_lib 'kernel32'
452
+ ffi_convention :stdcall
453
+
454
+ @blocking = true
455
+ attach_function :WaitForSingleObject, [:pointer, :uint], :uint
456
+ attach_function :FormatMessageW, [:int, :pointer, :int, :int, :pointer, :int, :pointer], :int
457
+ attach_function :FormatMessageA, [:int, :pointer, :int, :int, :pointer, :int, :pointer], :int
458
+ attach_function :CloseHandle, [:pointer], :int
459
+ attach_function :LocalFree, [:pointer], :pointer
460
+ end
461
+ end
462
+ end
@@ -0,0 +1,8 @@
1
+ class CERegistryKey
2
+ ERROR_NO_MORE_ITEMS = 259
3
+
4
+ attr_reader :name
5
+
6
+ def initialize(root_key, name, writable, is_root)
7
+ end
8
+ end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{rapi}
8
- s.version = "0.0.0"
8
+ s.version = "0.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Charles Strahan"]
12
- s.date = %q{2011-05-26}
12
+ s.date = %q{2011-05-30}
13
13
  s.description = %q{A Remote API (RAPI) interface.}
14
14
  s.email = %q{charles.c.strahan@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -24,6 +24,7 @@ Gem::Specification.new do |s|
24
24
  "Rakefile",
25
25
  "VERSION",
26
26
  "lib/rapi.rb",
27
+ "lib/rapi/ce_registry_key.rb",
27
28
  "rapi.gemspec",
28
29
  "test/helper.rb",
29
30
  "test/test_rapi.rb"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rapi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,12 +9,12 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-05-26 00:00:00.000000000 -05:00
12
+ date: 2011-05-30 00:00:00.000000000 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: ffi
17
- requirement: &22632792 !ruby/object:Gem::Requirement
17
+ requirement: &21938064 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ~>
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: 1.0.7
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *22632792
25
+ version_requirements: *21938064
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: shoulda
28
- requirement: &22632228 !ruby/object:Gem::Requirement
28
+ requirement: &21937572 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ! '>='
@@ -33,10 +33,10 @@ dependencies:
33
33
  version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
- version_requirements: *22632228
36
+ version_requirements: *21937572
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: bundler
39
- requirement: &22631664 !ruby/object:Gem::Requirement
39
+ requirement: &21936936 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ~>
@@ -44,10 +44,10 @@ dependencies:
44
44
  version: 1.0.0
45
45
  type: :development
46
46
  prerelease: false
47
- version_requirements: *22631664
47
+ version_requirements: *21936936
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: jeweler
50
- requirement: &22630896 !ruby/object:Gem::Requirement
50
+ requirement: &21936348 !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
53
53
  - - ~>
@@ -55,10 +55,10 @@ dependencies:
55
55
  version: 1.6.0
56
56
  type: :development
57
57
  prerelease: false
58
- version_requirements: *22630896
58
+ version_requirements: *21936348
59
59
  - !ruby/object:Gem::Dependency
60
60
  name: rcov
61
- requirement: &22630380 !ruby/object:Gem::Requirement
61
+ requirement: &21935700 !ruby/object:Gem::Requirement
62
62
  none: false
63
63
  requirements:
64
64
  - - ! '>='
@@ -66,7 +66,7 @@ dependencies:
66
66
  version: '0'
67
67
  type: :development
68
68
  prerelease: false
69
- version_requirements: *22630380
69
+ version_requirements: *21935700
70
70
  description: A Remote API (RAPI) interface.
71
71
  email: charles.c.strahan@gmail.com
72
72
  executables: []
@@ -82,6 +82,7 @@ files:
82
82
  - Rakefile
83
83
  - VERSION
84
84
  - lib/rapi.rb
85
+ - lib/rapi/ce_registry_key.rb
85
86
  - rapi.gemspec
86
87
  - test/helper.rb
87
88
  - test/test_rapi.rb
@@ -101,7 +102,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
101
102
  version: '0'
102
103
  segments:
103
104
  - 0
104
- hash: 180456047
105
+ hash: 285988977
105
106
  required_rubygems_version: !ruby/object:Gem::Requirement
106
107
  none: false
107
108
  requirements: