idevice 1.1.5.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.
Files changed (62) hide show
  1. data/.gitignore +18 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +4 -0
  4. data/NOTICE +202 -0
  5. data/README.md +74 -0
  6. data/Rakefile +37 -0
  7. data/examples/idevimgmount +58 -0
  8. data/examples/idumplockdownvalues +64 -0
  9. data/examples/ifetchcrashreports +54 -0
  10. data/examples/ilistapps +38 -0
  11. data/examples/ilistudids +26 -0
  12. data/examples/ilog +35 -0
  13. data/examples/installipa +51 -0
  14. data/examples/iremoveapp +42 -0
  15. data/examples/irestart +26 -0
  16. data/examples/iscreenshotr +39 -0
  17. data/idevice.gemspec +33 -0
  18. data/lib/idevice.rb +69 -0
  19. data/lib/idevice/afc.rb +518 -0
  20. data/lib/idevice/c.rb +129 -0
  21. data/lib/idevice/diagnostics_relay.rb +185 -0
  22. data/lib/idevice/file_relay.rb +83 -0
  23. data/lib/idevice/heartbeat.rb +99 -0
  24. data/lib/idevice/house_arrest.rb +138 -0
  25. data/lib/idevice/idevice.rb +208 -0
  26. data/lib/idevice/image_mounter.rb +117 -0
  27. data/lib/idevice/installation_proxy.rb +193 -0
  28. data/lib/idevice/lockdown.rb +350 -0
  29. data/lib/idevice/misagent.rb +112 -0
  30. data/lib/idevice/mobilebackup.rb +183 -0
  31. data/lib/idevice/mobilebackup2.rb +174 -0
  32. data/lib/idevice/mobilesync.rb +306 -0
  33. data/lib/idevice/notification_proxy.rb +168 -0
  34. data/lib/idevice/plist.rb +366 -0
  35. data/lib/idevice/restore.rb +176 -0
  36. data/lib/idevice/sbservices.rb +152 -0
  37. data/lib/idevice/screenshotr.rb +88 -0
  38. data/lib/idevice/version.rb +3 -0
  39. data/lib/idevice/webinspector.rb +96 -0
  40. data/spec/afc_devicespec.rb +409 -0
  41. data/spec/diagnostics_relay_devicespec.rb +125 -0
  42. data/spec/file_relay_devicespec.rb +45 -0
  43. data/spec/heartbeat_devicespec.rb +39 -0
  44. data/spec/idevice_devicespec.rb +93 -0
  45. data/spec/idevice_spec.rb +29 -0
  46. data/spec/image_mounter_devicespec.rb +65 -0
  47. data/spec/installation_proxy_devicespec.rb +54 -0
  48. data/spec/lockdown_devicespec.rb +106 -0
  49. data/spec/misagent_devicespec.rb +43 -0
  50. data/spec/mobilebackup2_devicespec.rb +58 -0
  51. data/spec/mobilebackup_devicespec.rb +41 -0
  52. data/spec/mobilesync_devicespec.rb +62 -0
  53. data/spec/notification_proxy_devicespec.rb +45 -0
  54. data/spec/plist_spec.rb +176 -0
  55. data/spec/restore_devicespec.rb +72 -0
  56. data/spec/samples/plist.bin +0 -0
  57. data/spec/samples/plist.xml +10 -0
  58. data/spec/sbservices_devicespec.rb +64 -0
  59. data/spec/screenshotr_devicespec.rb +39 -0
  60. data/spec/spec_helper.rb +73 -0
  61. data/spec/webinspector_devicespec.rb +36 -0
  62. metadata +233 -0
@@ -0,0 +1,152 @@
1
+ #
2
+ # Copyright (c) 2013 Eric Monti - Bluebox Security
3
+ #
4
+ # Licensed to the Apache Software Foundation (ASF) under one
5
+ # or more contributor license agreements. See the NOTICE file
6
+ # distributed with this work for additional information
7
+ # regarding copyright ownership. The ASF licenses this file
8
+ # to you under the Apache License, Version 2.0 (the
9
+ # "License"); you may not use this file except in compliance
10
+ # with the License. You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing,
15
+ # software distributed under the License is distributed on an
16
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17
+ # KIND, either express or implied. See the License for the
18
+ # specific language governing permissions and limitations
19
+ # under the License.
20
+
21
+ require 'idevice/c'
22
+ require 'idevice/plist'
23
+ require 'idevice/idevice'
24
+ require 'idevice/lockdown'
25
+
26
+ module Idevice
27
+ class SbservicesError < IdeviceLibError
28
+ end
29
+
30
+ SBSError = SbservicesError
31
+
32
+ class SbservicesClient < C::ManagedOpaquePointer
33
+ include LibHelpers
34
+
35
+ def self.release(ptr)
36
+ C.sbservices_client_free(ptr) unless ptr.null?
37
+ end
38
+
39
+ def self.attach(opts={})
40
+ _attach_helper("com.apple.springboardservices", opts) do |idevice, ldsvc, p_sbs|
41
+ err = C.sbservices_client_new(idevice, ldsvc, p_sbs)
42
+ raise SbservicesError, "Springboard Services Error: #{err}" if err != :SUCCESS
43
+
44
+ sbs = p_sbs.read_pointer
45
+ raise SBSError, "sbservices_client_new returned a NULL client" if sbs.null?
46
+ return new(sbs)
47
+ end
48
+ end
49
+
50
+ def get_icon_state
51
+ FFI::MemoryPointer.new(:pointer) do |p_state|
52
+ err = C.sbservices_get_icon_state(self, p_state, nil)
53
+ raise SbservicesError, "Springboard Services Error: #{err}" if err != :SUCCESS
54
+ return p_state.read_pointer.read_plist_t
55
+ end
56
+ end
57
+
58
+ def set_icon_state(newstate)
59
+ err = C.sbservices_set_icon_state(self, Plist_t.from_ruby(newstate))
60
+ raise SbservicesError, "Springboard Services Error: #{err}" if err != :SUCCESS
61
+
62
+ return true
63
+ end
64
+
65
+ def get_icon_pngdata(bundleid)
66
+ FFI::MemoryPointer.new(:pointer) do |p_pngdata|
67
+ FFI::MemoryPointer.new(:uint64) do |p_pngsize|
68
+ err = C.sbservices_get_icon_pngdata(self, bundleid, p_pngdata, p_pngsize)
69
+ raise SbservicesError, "Springboard Services Error: #{err}" if err != :SUCCESS
70
+
71
+ pngdata = p_pngdata.read_pointer
72
+ unless pngdata.null?
73
+ ret=pngdata.read_bytes(p_pngsize.read_uint64)
74
+ C.free(pngdata)
75
+ return ret
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ INTERFACE_ORIENTATIONS = [
82
+ :UNKNOWN, # => 0,
83
+ :PORTRAIT, # => 1,
84
+ :PORTRAIT_UPSIDE_DOWN, # => 2,
85
+ :LANDSCAPE_RIGHT, # => 3,
86
+ :LANDSCAPE_LEFT, # => 4,
87
+ ]
88
+
89
+ def get_interface_orientation
90
+ FFI::MemoryPointer.new(:int) do |p_orientation|
91
+ err = C.sbservices_get_interface_orientation(self, p_orientation)
92
+ raise SbservicesError, "Springboard Services Error: #{err}" if err != :SUCCESS
93
+
94
+ orientation = p_orientation.read_int
95
+ return (INTERFACE_ORIENTATIONS[orientation] or orientation)
96
+ end
97
+ end
98
+
99
+ def get_home_screen_wallpaper_pngdata
100
+ FFI::MemoryPointer.new(:pointer) do |p_pngdata|
101
+ FFI::MemoryPointer.new(:uint64) do |p_pngsize|
102
+ err = C.sbservices_get_home_screen_wallpaper_pngdata(self, p_pngdata, p_pngsize)
103
+ raise SbservicesError, "Springboard Services Error: #{err}" if err != :SUCCESS
104
+
105
+ pngdata = p_pngdata.read_pointer
106
+ unless pngdata.null?
107
+ ret=pngdata.read_bytes(p_pngsize.read_uint64)
108
+ C.free(pngdata)
109
+ return ret
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ end
116
+
117
+ SBSClient = SbservicesClient
118
+
119
+ module C
120
+ ffi_lib 'imobiledevice'
121
+
122
+ typedef enum(
123
+ :SUCCESS , 0,
124
+ :INVALID_ARG , -1,
125
+ :PLIST_ERROR , -2,
126
+ :CONN_FAILED , -3,
127
+ :UNKNOWN_ERROR, -256,
128
+ ), :sbservices_error_t
129
+
130
+ #sbservices_error_t sbservices_client_new(idevice_t device, lockdownd_service_descriptor_t service, sbservices_client_t *client);
131
+ attach_function :sbservices_client_new, [Idevice, LockdownServiceDescriptor, :pointer], :sbservices_error_t
132
+
133
+ #sbservices_error_t sbservices_client_free(sbservices_client_t client);
134
+ attach_function :sbservices_client_free, [SBSClient], :sbservices_error_t
135
+
136
+ #sbservices_error_t sbservices_get_icon_state(sbservices_client_t client, plist_t *state, const char *format_version);
137
+ attach_function :sbservices_get_icon_state, [SBSClient, :pointer, :string], :sbservices_error_t
138
+
139
+ #sbservices_error_t sbservices_set_icon_state(sbservices_client_t client, plist_t newstate);
140
+ attach_function :sbservices_set_icon_state, [SBSClient, Plist_t], :sbservices_error_t
141
+
142
+ #sbservices_error_t sbservices_get_icon_pngdata(sbservices_client_t client, const char *bundleId, char **pngdata, uint64_t *pngsize);
143
+ attach_function :sbservices_get_icon_pngdata, [SBSClient, :string, :pointer, :pointer], :sbservices_error_t
144
+
145
+ #sbservices_error_t sbservices_get_interface_orientation(sbservices_client_t client, sbservices_interface_orientation_t* interface_orientation);
146
+ attach_function :sbservices_get_interface_orientation, [SBSClient, :pointer], :sbservices_error_t
147
+
148
+ #sbservices_error_t sbservices_get_home_screen_wallpaper_pngdata(sbservices_client_t client, char **pngdata, uint64_t *pngsize);
149
+ attach_function :sbservices_get_home_screen_wallpaper_pngdata, [SBSClient, :pointer, :pointer], :sbservices_error_t
150
+
151
+ end
152
+ end
@@ -0,0 +1,88 @@
1
+ #
2
+ # Copyright (c) 2013 Eric Monti - Bluebox Security
3
+ #
4
+ # Licensed to the Apache Software Foundation (ASF) under one
5
+ # or more contributor license agreements. See the NOTICE file
6
+ # distributed with this work for additional information
7
+ # regarding copyright ownership. The ASF licenses this file
8
+ # to you under the Apache License, Version 2.0 (the
9
+ # "License"); you may not use this file except in compliance
10
+ # with the License. You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing,
15
+ # software distributed under the License is distributed on an
16
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17
+ # KIND, either express or implied. See the License for the
18
+ # specific language governing permissions and limitations
19
+ # under the License.
20
+
21
+ require 'idevice/c'
22
+ require 'idevice/idevice'
23
+ require 'idevice/lockdown'
24
+
25
+ module Idevice
26
+
27
+ class ScreenShotrError < IdeviceLibError
28
+ end
29
+
30
+ class ScreenShotrClient < C::ManagedOpaquePointer
31
+ include LibHelpers
32
+
33
+ def self.release(ptr)
34
+ C.screenshotr_client_free(ptr) unless ptr.null?
35
+ end
36
+
37
+ def self.attach(opts={})
38
+ _attach_helper("com.apple.mobile.screenshotr", opts) do |idevice, ldsvc, p_ss|
39
+ err = C.screenshotr_client_new(idevice, ldsvc, p_ss)
40
+ raise ScreenShotrError, "ScreenShotr Error: #{err}" if err != :SUCCESS
41
+
42
+ ss = p_ss.read_pointer
43
+ raies ScreenShotrError, "screenshotr_client_new returned a NULL client" if ss.null?
44
+ return new(ss)
45
+ end
46
+ end
47
+
48
+ def take_screenshot
49
+ FFI::MemoryPointer.new(:pointer) do |p_imgdata|
50
+ FFI::MemoryPointer.new(:uint64) do |p_imgsize|
51
+ err = C.screenshotr_take_screenshot(self, p_imgdata, p_imgsize)
52
+ raise ScreenShotrError, "ScreenShotr Error: #{err}" if err != :SUCCESS
53
+
54
+ imgdata = p_imgdata.read_pointer
55
+ unless imgdata.null?
56
+ ret=imgdata.read_bytes(p_imgsize.read_uint64)
57
+ C.free(imgdata)
58
+ return ret
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ module C
66
+ ffi_lib 'imobiledevice'
67
+
68
+ typedef enum(
69
+ :SUCCESS , 0,
70
+ :INVALID_ARG , -1,
71
+ :PLIST_ERROR , -2,
72
+ :MUX_ERROR , -3,
73
+ :BAD_VERSION , -4,
74
+ :UNKNOWN_ERROR, -256,
75
+ ), :screenshotr_error_t
76
+
77
+ #screenshotr_error_t screenshotr_client_new(idevice_t device, lockdownd_service_descriptor_t service, screenshotr_client_t * client);
78
+ attach_function :screenshotr_client_new, [Idevice, LockdownServiceDescriptor, :pointer], :screenshotr_error_t
79
+
80
+ #screenshotr_error_t screenshotr_client_free(screenshotr_client_t client);
81
+ attach_function :screenshotr_client_free, [ScreenShotrClient], :screenshotr_error_t
82
+
83
+ #screenshotr_error_t screenshotr_take_screenshot(screenshotr_client_t client, char **imgdata, uint64_t *imgsize);
84
+ attach_function :screenshotr_take_screenshot, [ScreenShotrClient, :pointer, :pointer], :screenshotr_error_t
85
+
86
+ end
87
+ end
88
+
@@ -0,0 +1,3 @@
1
+ module Idevice
2
+ VERSION = "1.1.5.0"
3
+ end
@@ -0,0 +1,96 @@
1
+ #
2
+ # Copyright (c) 2013 Eric Monti - Bluebox Security
3
+ #
4
+ # Licensed to the Apache Software Foundation (ASF) under one
5
+ # or more contributor license agreements. See the NOTICE file
6
+ # distributed with this work for additional information
7
+ # regarding copyright ownership. The ASF licenses this file
8
+ # to you under the Apache License, Version 2.0 (the
9
+ # "License"); you may not use this file except in compliance
10
+ # with the License. You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing,
15
+ # software distributed under the License is distributed on an
16
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17
+ # KIND, either express or implied. See the License for the
18
+ # specific language governing permissions and limitations
19
+ # under the License.
20
+
21
+ require 'idevice/c'
22
+ require 'idevice/plist'
23
+ require 'idevice/idevice'
24
+ require 'idevice/lockdown'
25
+
26
+ module Idevice
27
+ class WebInspectorError < IdeviceLibError
28
+ end
29
+
30
+ class WebInspectorClient < C::ManagedOpaquePointer
31
+ include LibHelpers
32
+
33
+ def self.release(ptr)
34
+ C.webinspector_client_free(ptr) unless ptr.null?
35
+ end
36
+
37
+ def self.attach(opts={})
38
+ _attach_helper("com.apple.webinspector", opts) do |idevice, ldsvc, p_wic|
39
+ err = C.webinspector_client_new(idevice, ldsvc, p_wic)
40
+ raise WebInspectorError, "WebInspector error: #{err}" if err != :SUCCESS
41
+
42
+ wic = p_wic.read_pointer
43
+ raise WebInspectorError, "webinspector_client_new returned a NULL client" if wic.null?
44
+
45
+ return new(wic)
46
+ end
47
+ end
48
+
49
+ def send_plist(obj)
50
+ err = C.webinspector_send(self, Plist_t.from_ruby(obj))
51
+ raise WebInspectorError, "WebInspector error: #{err}" if err != :SUCCESS
52
+ return true
53
+ end
54
+
55
+ def receive_plist
56
+ FFI::MemoryPointer.new(:pointer) do |p_plist|
57
+ err = C.webinspector_receive(self, p_plist)
58
+ raise WebInspectorError, "WebInspector error: #{err}" if err != :SUCCESS
59
+ return p_plist.to_pointer.to_plist_t
60
+ end
61
+ end
62
+ end
63
+
64
+ module C
65
+
66
+ typedef enum(
67
+ :SUCCESS , 0,
68
+ :INVALID_ARG , -1,
69
+ :PLIST_ERROR , -2,
70
+ :MUX_ERROR , -3,
71
+ :SSL_ERROR , -4,
72
+ :UNKNOWN_ERROR, -256,
73
+ ), :webinspector_error_t
74
+
75
+ #webinspector_error_t webinspector_client_new(idevice_t device, lockdownd_service_descriptor_t service, webinspector_client_t * client);
76
+ attach_function :webinspector_client_new, [Idevice, LockdownServiceDescriptor, :pointer], :webinspector_error_t
77
+
78
+ #webinspector_error_t webinspector_client_start_service(idevice_t device, webinspector_client_t * client, const char* label);
79
+ attach_function :webinspector_client_start_service, [Idevice, :pointer, :string], :webinspector_error_t
80
+
81
+ #webinspector_error_t webinspector_client_free(webinspector_client_t client);
82
+ attach_function :webinspector_client_free, [WebInspectorClient], :webinspector_error_t
83
+
84
+ #webinspector_error_t webinspector_send(webinspector_client_t client, plist_t plist);
85
+ attach_function :webinspector_send, [WebInspectorClient, Plist_t], :webinspector_error_t
86
+
87
+ #webinspector_error_t webinspector_receive(webinspector_client_t client, plist_t * plist);
88
+ attach_function :webinspector_receive, [WebInspectorClient, :pointer], :webinspector_error_t
89
+
90
+ #webinspector_error_t webinspector_receive_with_timeout(webinspector_client_t client, plist_t * plist, uint32_t timeout_ms);
91
+ attach_function :webinspector_receive_with_timeout, [WebInspectorClient, :pointer, :uint32], :webinspector_error_t
92
+
93
+ end
94
+ end
95
+
96
+
@@ -0,0 +1,409 @@
1
+ #
2
+ # Copyright (c) 2013 Eric Monti - Bluebox Security
3
+ #
4
+ # Licensed to the Apache Software Foundation (ASF) under one
5
+ # or more contributor license agreements. See the NOTICE file
6
+ # distributed with this work for additional information
7
+ # regarding copyright ownership. The ASF licenses this file
8
+ # to you under the Apache License, Version 2.0 (the
9
+ # "License"); you may not use this file except in compliance
10
+ # with the License. You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing,
15
+ # software distributed under the License is distributed on an
16
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17
+ # KIND, either express or implied. See the License for the
18
+ # specific language governing permissions and limitations
19
+ # under the License.
20
+
21
+ require_relative 'spec_helper'
22
+ require 'time'
23
+
24
+ describe Idevice::AFCClient do
25
+ before :all do
26
+ @fromfile = sample_file("plist.bin")
27
+ end
28
+
29
+ before :each do
30
+ @afc = Idevice::AFCClient.attach(idevice:shared_idevice)
31
+ end
32
+
33
+ it "should attach" do
34
+ @afc.should be_a Idevice::AFCClient
35
+ end
36
+
37
+ it "should return device info" do
38
+ result = @afc.device_info
39
+ result.should be_a Hash
40
+ result.keys.sort.should == [:FSBlockSize, :FSFreeBytes, :FSTotalBytes, :Model]
41
+ result[:FSBlockSize].should =~ /^\d+$/
42
+ result[:FSFreeBytes].should =~ /^\d+$/
43
+ result[:FSTotalBytes].should =~ /^\d+$/
44
+ result[:FSTotalBytes].to_i.should > result["FSFreeBytes"].to_i
45
+ end
46
+
47
+ it "should return device info for specific keys" do
48
+ totbytes = @afc.device_info("FSTotalBytes")
49
+ totbytes.should be_a String
50
+ totbytes.should =~ /^\d+$/
51
+
52
+ freebytes = @afc.device_info("FSFreeBytes")
53
+ freebytes.should be_a String
54
+ freebytes.should =~ /^\d+$/
55
+
56
+ totbytes.to_i.should > freebytes.to_i
57
+ end
58
+
59
+ it "should list directory contents" do
60
+ result = @afc.read_directory('/')
61
+ result.should be_a Array
62
+ result[0,2].should == ['.', '..']
63
+ end
64
+
65
+ it "should raise an error listing an invalid directory" do
66
+ lambda{ @afc.read_directory('/TOTALLYNOTREALLYTHERE') }.should raise_error(Idevice::AFCError)
67
+ end
68
+
69
+ it "should get file information" do
70
+ result = @afc.file_info('/')
71
+ result.should be_a Hash
72
+ result.keys.sort.should == [:st_birthtime, :st_blocks, :st_ifmt, :st_mtime, :st_nlink, :st_size]
73
+ result[:st_ifmt].should == :S_IFDIR
74
+ end
75
+
76
+ it "should raise an error getting info for an invalid path" do
77
+ lambda{ @afc.file_info('/TOTALLYNOTREALLYTHERE') }.should raise_error(Idevice::AFCError)
78
+ end
79
+
80
+ it "should remove a file path" do
81
+ remotepath='TOTALLYATESTFILECREATEDTEST'
82
+
83
+ @afc.put_path(@fromfile.to_s, remotepath).should == @fromfile.size
84
+ @afc.remove_path(remotepath).should be_true
85
+ end
86
+
87
+ it "should remove an (empty) directory path" do
88
+ remotepath='TOTALLYATESTDIRCREATEDTEST'
89
+ @afc.make_directory(remotepath).should be_true
90
+ @afc.remove_path(remotepath).should be_true
91
+ end
92
+
93
+ it "should rename a file path" do
94
+ remotepath='TOTALLYATESTFILECREATEDTEST'
95
+ renamepath = remotepath+'2'
96
+
97
+ begin
98
+ @afc.put_path(@fromfile.to_s, remotepath).should == @fromfile.size
99
+ originfo = @afc.file_info(remotepath)
100
+ @afc.rename_path(remotepath, renamepath).should be_true
101
+ lambda{ @afc.file_info(remotepath) }.should raise_error Idevice::AFCError
102
+ info = @afc.file_info(renamepath)
103
+ info.should == originfo
104
+ ensure
105
+ @afc.remove_path(remotepath) rescue nil
106
+ @afc.remove_path(renamepath).should be_true
107
+ end
108
+
109
+ end
110
+
111
+ it "should rename a directory path" do
112
+ remotepath = 'TOTALLYATESTDIRCREATEDTEST'
113
+ renamepath = remotepath+'2'
114
+ begin
115
+ @afc.make_directory(remotepath).should be_true
116
+ originfo = @afc.file_info(remotepath)
117
+ @afc.rename_path(remotepath, renamepath).should be_true
118
+ lambda{ @afc.file_info(remotepath) }.should raise_error Idevice::AFCError
119
+ info = @afc.file_info(renamepath)
120
+ info.should == originfo
121
+ ensure
122
+ @afc.remove_path(remotepath) rescue nil
123
+ @afc.remove_path(renamepath).should be_true
124
+ end
125
+ end
126
+
127
+ it "should make a directory" do
128
+ remotepath = 'TOTALLYATESTDIR'
129
+ begin
130
+ @afc.make_directory(remotepath).should be_true
131
+ result = @afc.file_info(remotepath)
132
+ result[:st_ifmt].should == :S_IFDIR
133
+ ensure
134
+ @afc.remove_path(remotepath) rescue nil
135
+ end
136
+ end
137
+
138
+ it "should make a symbolic link to a directory" do
139
+ begin
140
+ @afc.symlink('.', 'TOTALLYATESTSYMLINKTOCURRENTDIR').should be_true
141
+ result = @afc.file_info('TOTALLYATESTSYMLINKTOCURRENTDIR')
142
+ result[:st_ifmt].should == :S_IFLNK
143
+ ensure
144
+ @afc.remove_path('TOTALLYATESTSYMLINKTOCURRENTDIR') rescue nil
145
+ end
146
+ end
147
+
148
+ it "should make a symbolic link to a file" do
149
+ remotefile = 'TOTALLYATESTFILE3'
150
+ remotelink = 'TOTEALLYATESTSYMLINK3'
151
+
152
+ begin
153
+ @afc.touch(remotefile).should be_true
154
+ @afc.symlink(remotefile, remotelink).should be_true
155
+ @afc.file_info(remotefile)[:st_ifmt].should == :S_IFREG
156
+ @afc.file_info(remotelink)[:st_ifmt].should == :S_IFLNK
157
+
158
+ # opening a symlinked file via AFC seems to give PERM_DENIED ?
159
+ #@afc.open(remotelink,'a'){|f| f.write("meep") }
160
+ #@afc.cat(remotefile).should == "meep"
161
+
162
+ #@afc.file_info(remotefile)[:st_ifmt].should == :S_IFREG
163
+ #@afc.file_info(remotelink)[:st_ifmt].should == :S_IFLNK
164
+ ensure
165
+ @afc.remove_path(remotefile) rescue nil
166
+ @afc.remove_path(remotelink) rescue nil
167
+ end
168
+ end
169
+
170
+ it "should make a hard link to a file" do
171
+ remotefile = 'TOTALLYATESTFILE2'
172
+ remotelink = 'TOTEALLYATESTHARDLINK'
173
+
174
+ begin
175
+ @afc.touch(remotefile).should be_true
176
+ @afc.hardlink(remotefile, remotelink).should be_true
177
+ @afc.file_info(remotefile)[:st_ifmt].should == :S_IFREG
178
+ @afc.file_info(remotelink)[:st_ifmt].should == :S_IFREG
179
+
180
+ @afc.open(remotelink,'a'){|f| f.write("meep") }
181
+ @afc.cat(remotefile).should == "meep"
182
+
183
+ @afc.file_info(remotefile)[:st_ifmt].should == :S_IFREG
184
+ @afc.file_info(remotelink)[:st_ifmt].should == :S_IFREG
185
+ ensure
186
+ @afc.remove_path(remotefile) rescue nil
187
+ @afc.remove_path(remotelink) rescue nil
188
+ end
189
+ end
190
+
191
+ it "should raise an error removing a non-existent path" do
192
+ lambda{ @afc.remove_path('TOTALLYNOTREALLYTHERE') }.should raise_error(Idevice::AFCError)
193
+ end
194
+
195
+ it "should put a file and cat it" do
196
+ remotepath = 'TESTFILEUPLOAD'
197
+
198
+ begin
199
+ @afc.put_path(@fromfile.to_s, remotepath).should == @fromfile.size
200
+ @afc.cat(remotepath).should == @fromfile.read()
201
+ ensure
202
+ @afc.remove_path(remotepath) rescue nil
203
+ end
204
+ end
205
+
206
+ it "should put a file and cat it with a small chunk size" do
207
+ remotepath = 'TESTFILEUPLOAD'
208
+
209
+ begin
210
+ gotblock=false
211
+ @afc.put_path(@fromfile.to_s, remotepath, 2) do |chunksz|
212
+ gotblock=true
213
+ (0..2).should include(chunksz)
214
+ end.should == @fromfile.size
215
+ gotblock.should be_true
216
+
217
+ catsize = 0
218
+ catbuf = StringIO.new
219
+ gotblock=false
220
+
221
+ @afc.cat(remotepath, 2) do |chunk|
222
+ catbuf << chunk
223
+ catsize += chunk.size
224
+ gotblock=true
225
+ (0..2).should include(chunk.size)
226
+ end
227
+
228
+ catsize.should == @fromfile.size
229
+ catbuf.string.should == @fromfile.read()
230
+ gotblock.should be_true
231
+ ensure
232
+ @afc.remove_path(remotepath) rescue nil
233
+ end
234
+ end
235
+
236
+ it "should get a file" do
237
+ tmpfile = Tempfile.new('TESTFILEUPLOADFORGETlocal')
238
+ tmppath = tmpfile.path
239
+ tmpfile.close
240
+ remotepath = 'TESTFILEUPLOADFORGET'
241
+ begin
242
+ @afc.put_path(@fromfile.to_s, remotepath).should == @fromfile.size
243
+ @afc.getpath(remotepath, tmppath).should == @fromfile.size
244
+ File.read(tmppath).should == @fromfile.read()
245
+ ensure
246
+ @afc.remove_path(remotepath) rescue nil
247
+ File.unlink(tmppath)
248
+ end
249
+ end
250
+
251
+ it "should truncate a file path" do
252
+ remotepath = 'TESTFILEUPLOADFORTRUNCATE'
253
+
254
+ begin
255
+ (@fromfile.size > 10).should be_true
256
+ @afc.put_path(@fromfile.to_s, remotepath).should == @fromfile.size
257
+ @afc.size(remotepath).should == @fromfile.size
258
+ @afc.truncate(remotepath, 10).should be_true
259
+ @afc.size(remotepath).should == 10
260
+ ensure
261
+ @afc.remove_path(remotepath) rescue nil
262
+ end
263
+ end
264
+
265
+ it "should get file mtime" do
266
+ res = @afc.mtime('.')
267
+ res.should be_a Time
268
+ # between first iphone release and 30-sec from now
269
+ res.should > Time.parse("June 29, 2007")
270
+ res.should < (Time.now+30)
271
+ end
272
+
273
+ it "should get file ctime" do
274
+ res = @afc.mtime('.')
275
+ res.should be_a Time
276
+ # between first iphone release and 30-sec from now
277
+ res.should > Time.parse("June 29, 2007")
278
+ res.should < (Time.now+30)
279
+ end
280
+
281
+ it "should touch a file" do
282
+ remotefile = "TESTFILETOUCH"
283
+ begin
284
+ @afc.touch(remotefile).should be_true
285
+ @afc.cat(remotefile).should == ""
286
+ @afc.ctime(remotefile).should == @afc.mtime(remotefile)
287
+ ensure
288
+ @afc.remove_path(remotefile) rescue nil
289
+ end
290
+ end
291
+
292
+ it "should set file time" do
293
+ remotefile = "TESTINGFILETIMESETTING"
294
+ settime = Time.parse("June 29, 2007 4:20 UTC")
295
+ begin
296
+ @afc.touch(remotefile).should be_true
297
+ @afc.ctime(remotefile).should_not == settime
298
+ @afc.mtime(remotefile).should_not == settime
299
+
300
+ @afc.set_file_time(remotefile, settime).should be_true
301
+ @afc.ctime(remotefile).should == settime
302
+ @afc.mtime(remotefile).should == settime
303
+ ensure
304
+ @afc.remove_path(remotefile) rescue nil
305
+ end
306
+ end
307
+
308
+ it "should open a file and read all its contents" do
309
+ remotepath = 'TESTFILEUPLOADFOROPENANDREAD'
310
+
311
+ begin
312
+ @afc.put_path(@fromfile.to_s, remotepath).should == @fromfile.size
313
+ dat = @afc.open(remotepath, 'r') { |f| f.read() }
314
+ dat.should == @fromfile.read()
315
+ ensure
316
+ @afc.remove_path(remotepath) rescue nil
317
+ end
318
+ end
319
+
320
+ it "should open a file and read a few bytes from it" do
321
+ remotepath = 'TESTFILEUPLOADFOROPENANDREAD'
322
+
323
+ begin
324
+ @afc.put_path(@fromfile.to_s, remotepath).should == @fromfile.size
325
+ @afc.open(remotepath, 'r') do |f|
326
+ f.read(6).should == "bplist"
327
+ end
328
+ ensure
329
+ @afc.remove_path(remotepath) rescue nil
330
+ end
331
+ end
332
+
333
+ it "should open a file skip ahead a few bytes offset and read from it" do
334
+ remotepath = 'TESTFILEUPLOADFOROPENANDREADOFFSET'
335
+
336
+ begin
337
+ @afc.put_path(@fromfile.to_s, remotepath).should == @fromfile.size
338
+ @afc.open(remotepath, 'r') do |f|
339
+ f.pos=2
340
+ f.read(4).should == "list"
341
+ end
342
+ ensure
343
+ @afc.remove_path(remotepath) rescue nil
344
+ end
345
+ end
346
+
347
+ it "should open a file and write to it" do
348
+ remotepath = 'TESTFILEUPLOADFOROPENANDWRITE'
349
+ testdata = "hellotest"
350
+
351
+ begin
352
+ @afc.open(remotepath, 'w') do |f|
353
+ f.write(testdata).should == testdata.size
354
+ end
355
+ @afc.cat(remotepath).should == testdata
356
+ ensure
357
+ @afc.remove_path(remotepath) rescue nil
358
+ end
359
+ end
360
+
361
+ it "should open a file and append to it" do
362
+ remotepath = 'TESTFILEUPLOADFOROPENANDAPEND'
363
+ testdata = "hellotest"
364
+
365
+ begin
366
+ @afc.open(remotepath, 'w') do |f|
367
+ f.write(testdata).should == testdata.size
368
+ end
369
+ @afc.cat(remotepath).should == testdata
370
+ @afc.open(remotepath, 'a') do |f|
371
+ f.pos.should == 0
372
+ f.pos+= testdata.size
373
+ f.pos.should == testdata.size
374
+ f.write(testdata).should == testdata.size
375
+ end
376
+ @afc.cat(remotepath).should == testdata + testdata
377
+ ensure
378
+ @afc.remove_path(remotepath) rescue nil
379
+ end
380
+ end
381
+
382
+ it "should open a file jump around positions and read/write to it" do
383
+ remotepath = 'TESTFILEUPLOADFOROPENANDWRITE'
384
+ testdata = "hellotest"
385
+
386
+ begin
387
+ @afc.open(remotepath, 'w') do |f|
388
+ f.write(testdata).should == testdata.size
389
+ end
390
+ @afc.open(remotepath, 'r+') do |f|
391
+ f.pos.should == 0
392
+ f.read().should == testdata
393
+ f.pos.should == testdata.size
394
+ f.rewind
395
+ f.pos.should == 0
396
+ f.seek(0, :SEEK_END)
397
+ f.pos.should == testdata.size
398
+ f.write(testdata).should == testdata.size
399
+ f.pos.should == testdata.size*2
400
+ f.rewind
401
+ f.read().should == testdata*2
402
+ end
403
+ @afc.cat(remotepath).should == testdata*2
404
+ ensure
405
+ @afc.remove_path(remotepath) rescue nil
406
+ end
407
+ end
408
+
409
+ end