idevice 1.1.5.0

Sign up to get free protection for your applications and to get access to all the features.
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