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