rapi 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: