]> git.saurik.com Git - safemode-ios.git/blob - MobileSafety.mm
Modernized MobileSafety.
[safemode-ios.git] / MobileSafety.mm
1 /* Cydia Substrate - Powerful Code Insertion Platform
2 * Copyright (C) 2008-2010 Jay Freeman (saurik)
3 */
4
5 /* GNU Lesser General Public License, Version 3 {{{ */
6 /*
7 * Substrate is free software: you can redistribute it and/or modify it under
8 * the terms of the GNU Lesser General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * Substrate is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 * License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with Substrate. If not, see <http://www.gnu.org/licenses/>.
19 **/
20 /* }}} */
21
22 #import <CoreFoundation/CoreFoundation.h>
23 #import <Foundation/Foundation.h>
24 #import <CoreGraphics/CGGeometry.h>
25 #import <UIKit/UIKit.h>
26
27 #include "CydiaSubstrate.h"
28
29 MSClassHook(UIStatusBar)
30
31 MSClassHook(UIImage)
32 MSMetaClassHook(UIImage)
33
34 MSClassHook(SBAlertItemsController)
35 MSClassHook(SBButtonBar)
36 MSClassHook(SBStatusBar)
37 MSClassHook(SBStatusBarDataManager)
38 MSClassHook(SBStatusBarTimeView)
39 MSClassHook(SBUIController)
40
41 Class $SafeModeAlertItem;
42
43 @interface SBAlertItem : NSObject {
44 }
45 - (UIAlertView *) alertSheet;
46 - (void) dismiss;
47 @end
48
49 @interface SBAlertItemsController : NSObject {
50 }
51 + (SBAlertItemsController *) sharedInstance;
52 - (void) activateAlertItem:(SBAlertItem *)item;
53 @end
54
55 @interface SBStatusBarTimeView : UIView {
56 }
57 - (id) textFont;
58 @end
59
60 @interface UIApplication (CydiaSubstrate)
61 - (void) applicationOpenURL:(id)url;
62 @end
63
64 @interface UIAlertView (CydiaSubstrate)
65 - (void) setForceHorizontalButtonsLayout:(BOOL)force;
66 - (void) setBodyText:(NSString *)body;
67 - (void) setNumberOfRows:(NSInteger)rows;
68 @end
69
70 void SafeModeAlertItem$alertSheet$buttonClicked$(id self, SEL sel, id sheet, int button) {
71 switch (button) {
72 case 1:
73 break;
74
75 case 2:
76 exit(0);
77 break;
78
79 case 3:
80 [[UIApplication sharedApplication] applicationOpenURL:[NSURL URLWithString:@"http://cydia.saurik.com/safemode/"]];
81 break;
82 }
83
84 [self dismiss];
85 }
86
87 void SafeModeAlertItem$configure$requirePasscodeForActions$(id self, SEL sel, BOOL configure, BOOL require) {
88 UIAlertView *sheet([self alertSheet]);
89 [sheet setDelegate:self];
90 [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."];
91 [sheet addButtonWithTitle:@"OK"];
92 [sheet addButtonWithTitle:@"Restart"];
93 [sheet addButtonWithTitle:@"Help"];
94 [sheet setNumberOfRows:1];
95 if ([sheet respondsToSelector:@selector(setForceHorizontalButtonsLayout:)])
96 [sheet setForceHorizontalButtonsLayout:YES];
97 }
98
99 void SafeModeAlertItem$performUnlockAction(id self, SEL sel) {
100 [[$SBAlertItemsController sharedInstance] activateAlertItem:self];
101 }
102
103 static void MSAlert() {
104 if ($SafeModeAlertItem == nil)
105 $SafeModeAlertItem = objc_lookUpClass("SafeModeAlertItem");
106 if ($SafeModeAlertItem == nil) {
107 $SafeModeAlertItem = objc_allocateClassPair(objc_getClass("SBAlertItem"), "SafeModeAlertItem", 0);
108 if ($SafeModeAlertItem == nil)
109 return;
110
111 class_addMethod($SafeModeAlertItem, @selector(alertSheet:buttonClicked:), (IMP) &SafeModeAlertItem$alertSheet$buttonClicked$, "v@:@i");
112 class_addMethod($SafeModeAlertItem, @selector(configure:requirePasscodeForActions:), (IMP) &SafeModeAlertItem$configure$requirePasscodeForActions$, "v@:cc");
113 class_addMethod($SafeModeAlertItem, @selector(performUnlockAction), (IMP) SafeModeAlertItem$performUnlockAction, "v@:");
114 objc_registerClassPair($SafeModeAlertItem);
115 }
116
117 if ($SBAlertItemsController != nil)
118 [[$SBAlertItemsController sharedInstance] activateAlertItem:[[[$SafeModeAlertItem alloc] init] autorelease]];
119 }
120
121 MSInstanceMessageHook2(void, SBStatusBar, touchesEnded,withEvent, id, touches, id, event) {
122 MSAlert();
123 MSOldCall(touches, event);
124 }
125
126 MSInstanceMessageHook1(void, SBStatusBar, mouseDown, void *, event) {
127 MSAlert();
128 MSOldCall(event);
129 }
130
131 MSInstanceMessageHook2(void, UIStatusBar, touchesBegan,withEvent, void *, touches, void *, event) {
132 MSAlert();
133 MSOldCall(touches, event);
134 }
135
136 MSInstanceMessageHook0(void, SBStatusBarDataManager, _updateTimeString) {
137 if (char *_data = &MSHookIvar<char>(self, "_data")) {
138 char *timeString(_data + 20);
139 strcpy(timeString, "Exit Safe Mode");
140 }
141 }
142
143 static void SBIconController$showInfoAlertIfNeeded(id self, SEL sel) {
144 static bool loaded = false;
145 if (loaded)
146 return;
147 loaded = true;
148 MSAlert();
149 }
150
151 MSInstanceMessageHook0(int, SBButtonBar, maxIconColumns) {
152 static int max;
153 if (max == 0) {
154 max = MSOldCall();
155 if (NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults])
156 if (NSDictionary *iconState = [defaults objectForKey:@"iconState"])
157 if (NSDictionary *buttonBar = [iconState objectForKey:@"buttonBar"])
158 if (NSArray *iconMatrix = [buttonBar objectForKey:@"iconMatrix"])
159 if ([iconMatrix count] != 0)
160 if (NSArray *row = [iconMatrix objectAtIndex:0]) {
161 int count([row count]);
162 if (max < count)
163 max = count;
164 }
165 } return max;
166 }
167
168 MSInstanceMessageHook0(id, SBUIController, init) {
169 if ((self = MSOldCall()) != nil) {
170 UIView *&_contentLayer(MSHookIvar<UIView *>(self, "_contentLayer"));
171 UIView *&_contentView(MSHookIvar<UIView *>(self, "_contentView"));
172
173 UIView *layer;
174 if (&_contentLayer != NULL)
175 layer = _contentLayer;
176 else if (&_contentView != NULL)
177 layer = _contentView;
178 else
179 layer = nil;
180
181 if (layer != nil)
182 [layer setBackgroundColor:[UIColor darkGrayColor]];
183 } return self;
184 }
185
186 #define Paper_ "/Library/MobileSubstrate/MobileSafety.png"
187
188 MSClassMessageHook0(UIImage *, UIImage, defaultDesktopImage) {
189 return [UIImage imageWithContentsOfFile:@Paper_];
190 }
191
192 MSInstanceMessageHook0(void, SBStatusBarTimeView, tile) {
193 NSString *&_time(MSHookIvar<NSString *>(self, "_time"));
194 CGRect &_textRect(MSHookIvar<CGRect>(self, "_textRect"));
195 if (_time != nil)
196 [_time release];
197 _time = [@"Exit Safe Mode" retain];
198 id font([self textFont]);
199 CGSize size([_time sizeWithFont:font]);
200 CGRect frame([self frame]);
201 _textRect.size = size;
202 _textRect.origin.x = (frame.size.width - size.width) / 2;
203 _textRect.origin.y = (frame.size.height - size.height) / 2;
204 }
205
206 #define Dylib_ "/Library/MobileSubstrate/MobileSubstrate.dylib"
207
208 MSInitialize {
209 NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]);
210
211 NSLog(@"MS:Warning: Entering Safe Mode");
212
213 char *dil(getenv("DYLD_INSERT_LIBRARIES"));
214 if (dil == NULL)
215 NSLog(@"MS:Error: DYLD_INSERT_LIBRARIES is unset?");
216 else {
217 NSArray *dylibs([[NSString stringWithUTF8String:dil] componentsSeparatedByString:@":"]);
218 NSUInteger index([dylibs indexOfObject:@ Dylib_]);
219 if (index == NSNotFound)
220 NSLog(@"MS:Error: dylib not in DYLD_INSERT_LIBRARIES?");
221 else if ([dylibs count] == 1)
222 unsetenv("DYLD_INSERT_LIBRARIES");
223 else {
224 NSMutableArray *value([[[NSMutableArray alloc] init] autorelease]);
225 [value setArray:dylibs];
226 [value removeObjectAtIndex:index];
227 setenv("DYLD_INSERT_LIBRARIES", [[value componentsJoinedByString:@":"] UTF8String], !0);
228 }
229 }
230
231 $SBAlertItemsController = objc_getClass("SBAlertItemsController");
232
233 if (Class _class = objc_getClass("SBIconController")) {
234 SEL sel(@selector(showInfoAlertIfNeeded));
235 if (Method method = class_getInstanceMethod(_class, sel))
236 method_setImplementation(method, (IMP) &SBIconController$showInfoAlertIfNeeded);
237 }
238
239 [pool release];
240 }