]> git.saurik.com Git - safemode-ios.git/blob - MobileSafety.mm
4a373d85c5fca5d9b5f0b3019271b01cc3513c27
[safemode-ios.git] / MobileSafety.mm
1 /* Cydia Substrate - Powerful Code Insertion Platform
2 * Copyright (C) 2008-2012 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 %apt Package: com.saurik.substrate.safemode
23 %apt Author: Jay Freeman (saurik) <saurik@saurik.com>
24
25 %apt Name: Substrate Safe Mode
26 %apt Description: safe mode safety extension (safe)
27
28 %apt Depends: mobilesubstrate (>= 0.9.3367+38)
29
30 %fflag 1
31 %fflag 2
32
33 %bundle com.apple.springboard
34
35 %flag -framework Foundation
36 %flag -framework UIKit
37
38 #import <CoreFoundation/CoreFoundation.h>
39 #import <Foundation/Foundation.h>
40 #import <CoreGraphics/CGGeometry.h>
41 #import <UIKit/UIKit.h>
42
43 #include "CydiaSubstrate.h"
44
45 MSClassHook(UIStatusBar)
46
47 MSClassHook(UIImage)
48 MSMetaClassHook(UIImage)
49
50 MSClassHook(AAAccountManager)
51 MSMetaClassHook(AAAccountManager)
52
53 MSClassHook(BBSectionInfo)
54 MSClassHook(BKSApplicationLaunchSettings)
55
56 MSClassHook(SBAlertItemsController)
57 MSClassHook(SBButtonBar)
58 MSClassHook(SBIconController)
59 MSClassHook(SBStatusBar)
60 MSClassHook(SBStatusBarDataManager)
61 MSClassHook(SBStatusBarTimeView)
62 MSClassHook(SBUIController)
63
64 Class $SafeModeAlertItem;
65
66 @interface SBAlertItem : NSObject {
67 }
68 - (UIAlertView *) alertSheet;
69 - (void) dismiss;
70 @end
71
72 @interface SBAlertItemsController : NSObject {
73 }
74 + (SBAlertItemsController *) sharedInstance;
75 - (void) activateAlertItem:(SBAlertItem *)item;
76 @end
77
78 @interface SBStatusBarTimeView : UIView {
79 }
80 - (id) textFont;
81 @end
82
83 @interface UIApplication (CydiaSubstrate)
84 - (void) applicationOpenURL:(id)url;
85 @end
86
87 @interface UIAlertView (CydiaSubstrate)
88 - (void) setForceHorizontalButtonsLayout:(BOOL)force;
89 - (void) setBodyText:(NSString *)body;
90 - (void) setNumberOfRows:(NSInteger)rows;
91 @end
92
93 void SafeModeAlertItem$alertSheet$buttonClicked$(id self, SEL sel, id sheet, int button) {
94 switch (button) {
95 case 1:
96 break;
97
98 case 2:
99 if (kCFCoreFoundationVersionNumber > 700)
100 system("killall backboardd");
101 else
102 // XXX: there are better ways of restarting SpringBoard that would actually save state
103 exit(0);
104 break;
105
106 case 3:
107 [[UIApplication sharedApplication] applicationOpenURL:[NSURL URLWithString:@"http://cydia.saurik.com/safemode/"]];
108 break;
109 }
110
111 [self dismiss];
112 }
113
114 void SafeModeAlertItem$configure$requirePasscodeForActions$(id self, SEL sel, BOOL configure, BOOL require) {
115 UIAlertView *sheet([self alertSheet]);
116
117 [sheet setDelegate:self];
118 [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.\n\nTap \"Help\" below for more tips."];
119 [sheet addButtonWithTitle:@"OK"];
120 [sheet addButtonWithTitle:@"Restart"];
121 [sheet addButtonWithTitle:@"Help"];
122 [sheet setNumberOfRows:1];
123
124 if ([sheet respondsToSelector:@selector(setForceHorizontalButtonsLayout:)])
125 [sheet setForceHorizontalButtonsLayout:YES];
126 }
127
128 void SafeModeAlertItem$performUnlockAction(id self, SEL sel) {
129 [[$SBAlertItemsController sharedInstance] activateAlertItem:self];
130 }
131
132 static void MSAlert() {
133 if ($SafeModeAlertItem == nil)
134 $SafeModeAlertItem = objc_lookUpClass("SafeModeAlertItem");
135 if ($SafeModeAlertItem == nil) {
136 $SafeModeAlertItem = objc_allocateClassPair(objc_getClass("SBAlertItem"), "SafeModeAlertItem", 0);
137 if ($SafeModeAlertItem == nil)
138 return;
139
140 class_addMethod($SafeModeAlertItem, @selector(alertSheet:buttonClicked:), (IMP) &SafeModeAlertItem$alertSheet$buttonClicked$, "v@:@i");
141 class_addMethod($SafeModeAlertItem, @selector(configure:requirePasscodeForActions:), (IMP) &SafeModeAlertItem$configure$requirePasscodeForActions$, "v@:cc");
142 class_addMethod($SafeModeAlertItem, @selector(performUnlockAction), (IMP) SafeModeAlertItem$performUnlockAction, "v@:");
143 objc_registerClassPair($SafeModeAlertItem);
144 }
145
146 if ($SBAlertItemsController != nil)
147 [[$SBAlertItemsController sharedInstance] activateAlertItem:[[[$SafeModeAlertItem alloc] init] autorelease]];
148 }
149
150
151 // XXX: on iOS 5.0, we really would prefer avoiding
152
153 MSInstanceMessageHook2(void, SBStatusBar, touchesEnded,withEvent, id, touches, id, event) {
154 MSAlert();
155 MSOldCall(touches, event);
156 }
157
158 MSInstanceMessageHook1(void, SBStatusBar, mouseDown, void *, event) {
159 MSAlert();
160 MSOldCall(event);
161 }
162
163 MSInstanceMessageHook2(void, UIStatusBar, touchesBegan,withEvent, void *, touches, void *, event) {
164 MSAlert();
165 MSOldCall(touches, event);
166 }
167
168
169 // this fairly complex code came from Grant, to solve the "it Safe Mode"-in-bar bug
170
171 MSInstanceMessageHook0(void, SBStatusBarDataManager, _updateTimeString) {
172 char *_data(&MSHookIvar<char>(self, "_data"));
173 if (_data == NULL)
174 return;
175
176 Ivar _itemIsEnabled(object_getInstanceVariable(self, "_itemIsEnabled", NULL));
177 if (_itemIsEnabled == NULL)
178 return;
179
180 Ivar _itemIsCloaked(object_getInstanceVariable(self, "_itemIsCloaked", NULL));
181 if (_itemIsCloaked == NULL)
182 return;
183
184 size_t enabledOffset(ivar_getOffset(_itemIsEnabled));
185 size_t cloakedOffset(ivar_getOffset(_itemIsCloaked));
186 if (enabledOffset >= cloakedOffset)
187 return;
188
189 size_t offset(cloakedOffset - enabledOffset);
190 char *timeString(_data + offset);
191 strcpy(timeString, "Exit Safe Mode");
192 }
193
194
195 static bool alerted_;
196
197 static void AlertIfNeeded() {
198 if (alerted_)
199 return;
200 alerted_ = true;
201 MSAlert();
202 }
203
204
205 // on iOS 4.3 and above we can use this advertisement, which seems to check every time the user unlocks
206 // XXX: verify that this still works on iOS 5.0
207
208 MSClassMessageHook0(void, AAAccountManager, showMobileMeOfferIfNecessary) {
209 AlertIfNeeded();
210 }
211
212
213 // -[SBIconController showInfoAlertIfNeeded] explains how to drag icons around the iPhone home screen
214 // it used to be shown to users when they unlocked their screen for the first time, and happened every unlock
215 // however, as of iOS 4.3, it got relegated to only appearing once the user installed an app or web clip
216
217 MSInstanceMessageHook0(void, SBIconController, showInfoAlertIfNeeded) {
218 AlertIfNeeded();
219 }
220
221
222 // the icon state, including crazy configurations like Five Icon Dock, is stored in SpringBoard's defaults
223 // unfortunately, SpringBoard on iOS 2.0 and 2.1 (maybe 2.2 as well) buffer overrun with more than 4 icons
224 // there is a third party package called IconSupport that remedies this, but not everyone is using it yet
225
226 MSInstanceMessageHook0(int, SBButtonBar, maxIconColumns) {
227 static int max;
228 if (max == 0) {
229 max = MSOldCall();
230 if (NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults])
231 if (NSDictionary *iconState = [defaults objectForKey:@"iconState"])
232 if (NSDictionary *buttonBar = [iconState objectForKey:@"buttonBar"])
233 if (NSArray *iconMatrix = [buttonBar objectForKey:@"iconMatrix"])
234 if ([iconMatrix count] != 0)
235 if (NSArray *row = [iconMatrix objectAtIndex:0]) {
236 int count([row count]);
237 if (max < count)
238 max = count;
239 }
240 } return max;
241 }
242
243
244 MSInstanceMessageHook0(id, SBUIController, init) {
245 if ((self = MSOldCall()) != nil) {
246 UIView *&_contentLayer(MSHookIvar<UIView *>(self, "_contentLayer"));
247 UIView *&_contentView(MSHookIvar<UIView *>(self, "_contentView"));
248
249 UIView *layer;
250 if (&_contentLayer != NULL)
251 layer = _contentLayer;
252 else if (&_contentView != NULL)
253 layer = _contentView;
254 else
255 layer = nil;
256
257 if (layer != nil)
258 [layer setBackgroundColor:[UIColor darkGrayColor]];
259 } return self;
260 }
261
262 #define Paper_ "/Library/MobileSubstrate/MobileSafety.png"
263
264 MSClassMessageHook0(UIImage *, UIImage, defaultDesktopImage) {
265 return [UIImage imageWithContentsOfFile:@Paper_];
266 }
267
268 MSInstanceMessageHook0(void, SBStatusBarTimeView, tile) {
269 NSString *&_time(MSHookIvar<NSString *>(self, "_time"));
270 CGRect &_textRect(MSHookIvar<CGRect>(self, "_textRect"));
271 if (_time != nil)
272 [_time release];
273 _time = [@"Exit Safe Mode" retain];
274 id font([self textFont]);
275 CGSize size([_time sizeWithFont:font]);
276 CGRect frame([self frame]);
277 _textRect.size = size;
278 _textRect.origin.x = (frame.size.width - size.width) / 2;
279 _textRect.origin.y = (frame.size.height - size.height) / 2;
280 }
281
282
283 // notification widgets ("wee apps" or "bulletin board sections") are capable of crashing SpringBoard
284 // unfortunately, which ones are in use are stored in SpringBoard's defaults, so we need to turn them off
285
286 MSInstanceMessageHook0(BOOL, BBSectionInfo, showsInNotificationCenter) {
287 return NO;
288 }
289
290
291 // on iOS 6.0, Apple split parts of SpringBoard into a daemon called backboardd, including app launches
292 // in order to allow safe mode to propogate into applications, we need to then tell backboardd here
293 // XXX: (all of this should be replaced, however, with per-process launchd-mediated exception handling)
294
295 MSInstanceMessageHook1(void, BKSApplicationLaunchSettings, setEnvironment, NSDictionary *, original) {
296 if (original == nil)
297 return MSOldCall(nil);
298
299 NSMutableDictionary *modified([original mutableCopy]);
300 [modified setObject:@"1" forKey:@"_MSSafeMode"];
301 return MSOldCall(modified);
302 }