]> git.saurik.com Git - safemode-ios.git/blob - MobileSafety.mm
Replace Cycript with Substrate in license.
[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 @class SBButtonBar;
30 @class SBStatusBar;
31 @class SBUIController;
32
33 Class $SafeModeAlertItem;
34 Class $SBAlertItemsController;
35
36 @interface SBAlertItem : NSObject {
37 }
38 - (UIAlertView *) alertSheet;
39 - (void) dismiss;
40 @end
41
42 @interface SBAlertItemsController : NSObject {
43 }
44 + (SBAlertItemsController *) sharedInstance;
45 - (void) activateAlertItem:(SBAlertItem *)item;
46 @end
47
48 @interface SBStatusBarTimeView : UIView {
49 }
50 - (id) textFont;
51 @end
52
53 @interface UIApplication (CydiaSubstrate)
54 - (void) applicationOpenURL:(id)url;
55 @end
56
57 @interface UIAlertView (CydiaSubstrate)
58 - (void) setForceHorizontalButtonsLayout:(BOOL)force;
59 - (void) setBodyText:(NSString *)body;
60 - (void) setNumberOfRows:(NSInteger)rows;
61 @end
62
63 void SafeModeAlertItem$alertSheet$buttonClicked$(id self, SEL sel, id sheet, int button) {
64 switch (button) {
65 case 1:
66 break;
67
68 case 2:
69 exit(0);
70 break;
71
72 case 3:
73 [[UIApplication sharedApplication] applicationOpenURL:[NSURL URLWithString:@"http://cydia.saurik.com/safemode/"]];
74 break;
75 }
76
77 [self dismiss];
78 }
79
80 void SafeModeAlertItem$configure$requirePasscodeForActions$(id self, SEL sel, BOOL configure, BOOL require) {
81 UIAlertView *sheet([self alertSheet]);
82 [sheet setDelegate:self];
83 [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."];
84 [sheet addButtonWithTitle:@"OK"];
85 [sheet addButtonWithTitle:@"Restart"];
86 [sheet addButtonWithTitle:@"Help"];
87 [sheet setNumberOfRows:1];
88 if ([sheet respondsToSelector:@selector(setForceHorizontalButtonsLayout:)])
89 [sheet setForceHorizontalButtonsLayout:YES];
90 }
91
92 void SafeModeAlertItem$performUnlockAction(id self, SEL sel) {
93 [[$SBAlertItemsController sharedInstance] activateAlertItem:self];
94 }
95
96 static void MSAlert() {
97 if ($SafeModeAlertItem == nil)
98 $SafeModeAlertItem = objc_lookUpClass("SafeModeAlertItem");
99 if ($SafeModeAlertItem == nil) {
100 $SafeModeAlertItem = objc_allocateClassPair(objc_getClass("SBAlertItem"), "SafeModeAlertItem", 0);
101 if ($SafeModeAlertItem == nil)
102 return;
103
104 class_addMethod($SafeModeAlertItem, @selector(alertSheet:buttonClicked:), (IMP) &SafeModeAlertItem$alertSheet$buttonClicked$, "v@:@i");
105 class_addMethod($SafeModeAlertItem, @selector(configure:requirePasscodeForActions:), (IMP) &SafeModeAlertItem$configure$requirePasscodeForActions$, "v@:cc");
106 class_addMethod($SafeModeAlertItem, @selector(performUnlockAction), (IMP) SafeModeAlertItem$performUnlockAction, "v@:");
107 objc_registerClassPair($SafeModeAlertItem);
108 }
109
110 if ($SBAlertItemsController != nil)
111 [[$SBAlertItemsController sharedInstance] activateAlertItem:[[[$SafeModeAlertItem alloc] init] autorelease]];
112 }
113
114 MSHook(void, SBStatusBar$touchesEnded$withEvent$, SBStatusBar *self, SEL sel, id touches, id event) {
115 MSAlert();
116 _SBStatusBar$touchesEnded$withEvent$(self, sel, touches, event);
117 }
118
119 MSHook(void, SBStatusBar$mouseDown$, SBStatusBar *self, SEL sel, void *event) {
120 MSAlert();
121 _SBStatusBar$mouseDown$(self, sel, event);
122 }
123
124 MSHook(void, UIStatusBar$touchesBegan$withEvent$, id self, SEL sel, void *arg0, void *arg1) {
125 MSAlert();
126 _UIStatusBar$touchesBegan$withEvent$(self, sel, arg0, arg1);
127 }
128
129 MSHook(void, SBStatusBarDataManager$_updateTimeString, id self, SEL sel) {
130 if (char *_data = &MSHookIvar<char>(self, "_data")) {
131 char *timeString(_data + 20);
132 strcpy(timeString, "Exit Safe Mode");
133 }
134 }
135
136 static void SBIconController$showInfoAlertIfNeeded(id self, SEL sel) {
137 static bool loaded = false;
138 if (loaded)
139 return;
140 loaded = true;
141 MSAlert();
142 }
143
144 MSHook(int, SBButtonBar$maxIconColumns, SBButtonBar *self, SEL sel) {
145 static int max;
146 if (max == 0) {
147 max = _SBButtonBar$maxIconColumns(self, sel);
148 if (NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults])
149 if (NSDictionary *iconState = [defaults objectForKey:@"iconState"])
150 if (NSDictionary *buttonBar = [iconState objectForKey:@"buttonBar"])
151 if (NSArray *iconMatrix = [buttonBar objectForKey:@"iconMatrix"])
152 if ([iconMatrix count] != 0)
153 if (NSArray *row = [iconMatrix objectAtIndex:0]) {
154 int count([row count]);
155 if (max < count)
156 max = count;
157 }
158 } return max;
159 }
160
161 MSHook(id, SBUIController$init, SBUIController *self, SEL sel) {
162 if ((self = _SBUIController$init(self, sel)) != nil) {
163 UIView *&_contentLayer(MSHookIvar<UIView *>(self, "_contentLayer"));
164 UIView *&_contentView(MSHookIvar<UIView *>(self, "_contentView"));
165
166 UIView *layer;
167 if (&_contentLayer != NULL)
168 layer = _contentLayer;
169 else if (&_contentView != NULL)
170 layer = _contentView;
171 else
172 layer = nil;
173
174 if (layer != nil)
175 [layer setBackgroundColor:[UIColor darkGrayColor]];
176 } return self;
177 }
178
179 #define Paper_ "/Library/MobileSubstrate/MobilePaper.png"
180
181 MSHook(UIImage *, UIImage$defaultDesktopImage, UIImage *self, SEL sel) {
182 return [UIImage imageWithContentsOfFile:@Paper_];
183 }
184
185 MSHook(void, SBStatusBarTimeView$tile, SBStatusBarTimeView *self, SEL sel) {
186 NSString *&_time(MSHookIvar<NSString *>(self, "_time"));
187 CGRect &_textRect(MSHookIvar<CGRect>(self, "_textRect"));
188 if (_time != nil)
189 [_time release];
190 _time = [@"Exit Safe Mode" retain];
191 id font([self textFont]);
192 CGSize size([_time sizeWithFont:font]);
193 CGRect frame([self frame]);
194 _textRect.size = size;
195 _textRect.origin.x = (frame.size.width - size.width) / 2;
196 _textRect.origin.y = (frame.size.height - size.height) / 2;
197 }
198
199 #define Dylib_ "/Library/MobileSubstrate/MobileSubstrate.dylib"
200
201 MSInitialize {
202 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
203
204 NSLog(@"MS:Warning: Entering Safe Mode");
205
206 _SBButtonBar$maxIconColumns = MSHookMessage(objc_getClass("SBButtonBar"), @selector(maxIconColumns), &$SBButtonBar$maxIconColumns);
207 _SBUIController$init = MSHookMessage(objc_getClass("SBUIController"), @selector(init), &$SBUIController$init);
208 _SBStatusBar$touchesEnded$withEvent$ = MSHookMessage(objc_getClass("SBStatusBar"), @selector(touchesEnded:withEvent:), &$SBStatusBar$touchesEnded$withEvent$);
209 _SBStatusBar$mouseDown$ = MSHookMessage(objc_getClass("SBStatusBar"), @selector(mouseDown:), &$SBStatusBar$mouseDown$);
210 _SBStatusBarDataManager$_updateTimeString = MSHookMessage(objc_getClass("SBStatusBarDataManager"), @selector(_updateTimeString), &$SBStatusBarDataManager$_updateTimeString);
211 _SBStatusBarTimeView$tile = MSHookMessage(objc_getClass("SBStatusBarTimeView"), @selector(tile), &$SBStatusBarTimeView$tile);
212
213 _UIStatusBar$touchesBegan$withEvent$ = MSHookMessage(objc_getClass("UIStatusBar"), @selector(touchesBegan:withEvent:), &$UIStatusBar$touchesBegan$withEvent$);
214
215 _UIImage$defaultDesktopImage = MSHookMessage(object_getClass(objc_getClass("UIImage")), @selector(defaultDesktopImage), &$UIImage$defaultDesktopImage);
216
217 char *dil = getenv("DYLD_INSERT_LIBRARIES");
218 if (dil == NULL)
219 NSLog(@"MS:Error: DYLD_INSERT_LIBRARIES is unset?");
220 else {
221 NSArray *dylibs([[NSString stringWithUTF8String:dil] componentsSeparatedByString:@":"]);
222 NSUInteger index([dylibs indexOfObject:@ Dylib_]);
223 if (index == NSNotFound)
224 NSLog(@"MS:Error: dylib not in DYLD_INSERT_LIBRARIES?");
225 else if ([dylibs count] == 1)
226 unsetenv("DYLD_INSERT_LIBRARIES");
227 else {
228 NSMutableArray *value([[[NSMutableArray alloc] init] autorelease]);
229 [value setArray:dylibs];
230 [value removeObjectAtIndex:index];
231 setenv("DYLD_INSERT_LIBRARIES", [[value componentsJoinedByString:@":"] UTF8String], !0);
232 }
233 }
234
235 $SBAlertItemsController = objc_getClass("SBAlertItemsController");
236
237 if (Class _class = objc_getClass("SBIconController")) {
238 SEL sel(@selector(showInfoAlertIfNeeded));
239 if (Method method = class_getInstanceMethod(_class, sel))
240 method_setImplementation(method, (IMP) &SBIconController$showInfoAlertIfNeeded);
241 }
242
243 [pool release];
244 }