]> git.saurik.com Git - safemode-ios.git/blob - MobileSafety.mm
Fix the Safe Mode UI on iOS 4.0.
[safemode-ios.git] / MobileSafety.mm
1 /* Cydia Substrate - Meta-Library Insert for iPhoneOS
2 * Copyright (C) 2008-2010 Jay Freeman (saurik)
3 */
4
5 /*
6 * Redistribution and use in source and binary
7 * forms, with or without modification, are permitted
8 * provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the
11 * above copyright notice, this list of conditions
12 * and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the
14 * above copyright notice, this list of conditions
15 * and the following disclaimer in the documentation
16 * and/or other materials provided with the
17 * distribution.
18 * 3. The name of the author may not be used to endorse
19 * or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38 #import <CoreFoundation/CoreFoundation.h>
39 #import <Foundation/Foundation.h>
40 #import <CoreGraphics/CGGeometry.h>
41 #import <UIKit/UIKit.h>
42
43 #import <SpringBoard/SBAlertItem.h>
44 #import <SpringBoard/SBAlertItemsController.h>
45 #import <SpringBoard/SBButtonBar.h>
46 #import <SpringBoard/SBStatusBarController.h>
47 #import <SpringBoard/SBStatusBarDataManager.h>
48 #import <SpringBoard/SBStatusBarTimeView.h>
49 #import <SpringBoard/SBUIController.h>
50
51 #include <substrate.h>
52
53 Class $SafeModeAlertItem;
54 Class $SBAlertItemsController;
55
56 void SafeModeAlertItem$alertSheet$buttonClicked$(id self, SEL sel, id sheet, int button) {
57 switch (button) {
58 case 1:
59 break;
60
61 case 2:
62 exit(0);
63 break;
64
65 case 3:
66 [UIApp applicationOpenURL:[NSURL URLWithString:@"http://cydia.saurik.com/safemode/"]];
67 break;
68 }
69
70 [self dismiss];
71 }
72
73 void SafeModeAlertItem$configure$requirePasscodeForActions$(id self, SEL sel, BOOL configure, BOOL require) {
74 id sheet([self alertSheet]);
75 [sheet setDelegate:self];
76 [sheet setBodyText:@"We apologize for the inconvenience, but SpringBoard has just crashed.\n\nMobileSubstrate /did not/ cause this problem: it has protected you from it.\n\nYour device is now running in Safe Mode. All extensions that support this safety system are disabled.\n\nReboot (or restart SpringBoard) to return to the normal mode. To return to this dialog touch the status bar."];
77 [sheet addButtonWithTitle:@"OK"];
78 [sheet addButtonWithTitle:@"Restart"];
79 [sheet addButtonWithTitle:@"Help"];
80 [sheet setNumberOfRows:1];
81 if ([sheet respondsToSelector:@selector(setForceHorizontalButtonsLayout:)])
82 [sheet setForceHorizontalButtonsLayout:YES];
83 }
84
85 void SafeModeAlertItem$performUnlockAction(id self, SEL sel) {
86 [[$SBAlertItemsController sharedInstance] activateAlertItem:self];
87 }
88
89 static void MSAlert() {
90 if ($SafeModeAlertItem == nil)
91 $SafeModeAlertItem = objc_lookUpClass("SafeModeAlertItem");
92 if ($SafeModeAlertItem == nil) {
93 $SafeModeAlertItem = objc_allocateClassPair(objc_getClass("SBAlertItem"), "SafeModeAlertItem", 0);
94 if ($SafeModeAlertItem == nil)
95 return;
96
97 class_addMethod($SafeModeAlertItem, @selector(alertSheet:buttonClicked:), (IMP) &SafeModeAlertItem$alertSheet$buttonClicked$, "v@:@i");
98 class_addMethod($SafeModeAlertItem, @selector(configure:requirePasscodeForActions:), (IMP) &SafeModeAlertItem$configure$requirePasscodeForActions$, "v@:cc");
99 class_addMethod($SafeModeAlertItem, @selector(performUnlockAction), (IMP) SafeModeAlertItem$performUnlockAction, "v@:");
100 objc_registerClassPair($SafeModeAlertItem);
101 }
102
103 if ($SBAlertItemsController != nil)
104 [[$SBAlertItemsController sharedInstance] activateAlertItem:[[[$SafeModeAlertItem alloc] init] autorelease]];
105 }
106
107 MSHook(void, SBStatusBar$touchesEnded$withEvent$, SBStatusBar *self, SEL sel, id touches, id event) {
108 MSAlert();
109 _SBStatusBar$touchesEnded$withEvent$(self, sel, touches, event);
110 }
111
112 MSHook(void, SBStatusBar$mouseDown$, SBStatusBar *self, SEL sel, GSEventRef event) {
113 MSAlert();
114 _SBStatusBar$mouseDown$(self, sel, event);
115 }
116
117 MSHook(void, UIStatusBar$touchesBegan$withEvent$, id self, SEL sel, void *arg0, void *arg1) {
118 MSAlert();
119 _UIStatusBar$touchesBegan$withEvent$(self, sel, arg0, arg1);
120 }
121
122 MSHook(void, SBStatusBarDataManager$_updateTimeString, id self, SEL sel) {
123 if (char *_data = &MSHookIvar<char>(self, "_data")) {
124 char *timeString(_data + 20);
125 strcpy(timeString, "Exit Safe Mode");
126 }
127 }
128
129 static void SBIconController$showInfoAlertIfNeeded(id self, SEL sel) {
130 static bool loaded = false;
131 if (loaded)
132 return;
133 loaded = true;
134 MSAlert();
135 }
136
137 MSHook(int, SBButtonBar$maxIconColumns, SBButtonBar *self, SEL sel) {
138 static int max;
139 if (max == 0) {
140 max = _SBButtonBar$maxIconColumns(self, sel);
141 if (NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults])
142 if (NSDictionary *iconState = [defaults objectForKey:@"iconState"])
143 if (NSDictionary *buttonBar = [iconState objectForKey:@"buttonBar"])
144 if (NSArray *iconMatrix = [buttonBar objectForKey:@"iconMatrix"])
145 if ([iconMatrix count] != 0)
146 if (NSArray *row = [iconMatrix objectAtIndex:0]) {
147 int count([row count]);
148 if (max < count)
149 max = count;
150 }
151 } return max;
152 }
153
154 MSHook(id, SBUIController$init, SBUIController *self, SEL sel) {
155 if ((self = _SBUIController$init(self, sel)) != nil) {
156 UIView *&_contentLayer(MSHookIvar<UIView *>(self, "_contentLayer"));
157 UIView *&_contentView(MSHookIvar<UIView *>(self, "_contentView"));
158
159 UIView *layer;
160 if (&_contentLayer != NULL)
161 layer = _contentLayer;
162 else if (&_contentView != NULL)
163 layer = _contentView;
164 else
165 layer = nil;
166
167 if (layer != nil)
168 [layer setBackgroundColor:[UIColor darkGrayColor]];
169 } return self;
170 }
171
172 #define Paper_ "/Library/MobileSubstrate/MobilePaper.png"
173
174 MSHook(UIImage *, UIImage$defaultDesktopImage, UIImage *self, SEL sel) {
175 return [UIImage imageWithContentsOfFile:@Paper_];
176 }
177
178 MSHook(void, SBStatusBarTimeView$tile, SBStatusBarTimeView *self, SEL sel) {
179 NSString *&_time(MSHookIvar<NSString *>(self, "_time"));
180 CGRect &_textRect(MSHookIvar<CGRect>(self, "_textRect"));
181 if (_time != nil)
182 [_time release];
183 _time = [@"Exit Safe Mode" retain];
184 GSFontRef font([self textFont]);
185 CGSize size([_time sizeWithFont:(id)font]);
186 CGRect frame([self frame]);
187 _textRect.size = size;
188 _textRect.origin.x = (frame.size.width - size.width) / 2;
189 _textRect.origin.y = (frame.size.height - size.height) / 2;
190 }
191
192 #define Dylib_ "/Library/MobileSubstrate/MobileSubstrate.dylib"
193
194 MSInitialize {
195 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
196
197 NSLog(@"MS:Warning: Entering Safe Mode");
198
199 _SBButtonBar$maxIconColumns = MSHookMessage(objc_getClass("SBButtonBar"), @selector(maxIconColumns), &$SBButtonBar$maxIconColumns);
200 _SBUIController$init = MSHookMessage(objc_getClass("SBUIController"), @selector(init), &$SBUIController$init);
201 _SBStatusBar$touchesEnded$withEvent$ = MSHookMessage(objc_getClass("SBStatusBar"), @selector(touchesEnded:withEvent:), &$SBStatusBar$touchesEnded$withEvent$);
202 _SBStatusBar$mouseDown$ = MSHookMessage(objc_getClass("SBStatusBar"), @selector(mouseDown:), &$SBStatusBar$mouseDown$);
203 _SBStatusBarDataManager$_updateTimeString = MSHookMessage(objc_getClass("SBStatusBarDataManager"), @selector(_updateTimeString), &$SBStatusBarDataManager$_updateTimeString);
204 _SBStatusBarTimeView$tile = MSHookMessage(objc_getClass("SBStatusBarTimeView"), @selector(tile), &$SBStatusBarTimeView$tile);
205
206 _UIStatusBar$touchesBegan$withEvent$ = MSHookMessage(objc_getClass("UIStatusBar"), @selector(touchesBegan:withEvent:), &$UIStatusBar$touchesBegan$withEvent$);
207
208 _UIImage$defaultDesktopImage = MSHookMessage(object_getClass(objc_getClass("UIImage")), @selector(defaultDesktopImage), &$UIImage$defaultDesktopImage);
209
210 char *dil = getenv("DYLD_INSERT_LIBRARIES");
211 if (dil == NULL)
212 NSLog(@"MS:Error: DYLD_INSERT_LIBRARIES is unset?");
213 else {
214 NSArray *dylibs([[NSString stringWithUTF8String:dil] componentsSeparatedByString:@":"]);
215 NSUInteger index([dylibs indexOfObject:@ Dylib_]);
216 if (index == NSNotFound)
217 NSLog(@"MS:Error: dylib not in DYLD_INSERT_LIBRARIES?");
218 else if ([dylibs count] == 1)
219 unsetenv("DYLD_INSERT_LIBRARIES");
220 else {
221 NSMutableArray *value([[[NSMutableArray alloc] init] autorelease]);
222 [value setArray:dylibs];
223 [value removeObjectAtIndex:index];
224 setenv("DYLD_INSERT_LIBRARIES", [[value componentsJoinedByString:@":"] UTF8String], !0);
225 }
226 }
227
228 $SBAlertItemsController = objc_getClass("SBAlertItemsController");
229
230 if (Class _class = objc_getClass("SBIconController")) {
231 SEL sel(@selector(showInfoAlertIfNeeded));
232 if (Method method = class_getInstanceMethod(_class, sel))
233 method_setImplementation(method, (IMP) &SBIconController$showInfoAlertIfNeeded);
234 }
235
236 [pool release];
237 }