]> git.saurik.com Git - safemode-ios.git/blame - MobileSafety.mm
Simulate Safe Mode inheritence using MobileSafety.
[safemode-ios.git] / MobileSafety.mm
CommitLineData
df972f42 1/* Cydia Substrate - Powerful Code Insertion Platform
30a4ee1a 2 * Copyright (C) 2008-2012 Jay Freeman (saurik)
25f84761
JF
3*/
4
df972f42 5/* GNU Lesser General Public License, Version 3 {{{ */
25f84761 6/*
5bb6a76c 7 * Substrate is free software: you can redistribute it and/or modify it under
df972f42
JF
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.
25f84761 11 *
5bb6a76c 12 * Substrate is distributed in the hope that it will be useful, but WITHOUT
df972f42
JF
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.
25f84761 16 *
df972f42 17 * You should have received a copy of the GNU Lesser General Public License
5bb6a76c 18 * along with Substrate. If not, see <http://www.gnu.org/licenses/>.
df972f42
JF
19**/
20/* }}} */
25f84761 21
9065bb44
JF
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
25f84761
JF
38#import <CoreFoundation/CoreFoundation.h>
39#import <Foundation/Foundation.h>
40#import <CoreGraphics/CGGeometry.h>
39f17851
JF
41#import <UIKit/UIKit.h>
42
f6ceb738 43#include "CydiaSubstrate.h"
25f84761 44
d641d519
JF
45MSClassHook(UIStatusBar)
46
47MSClassHook(UIImage)
48MSMetaClassHook(UIImage)
49
f7673201
JF
50MSClassHook(AAAccountManager)
51MSMetaClassHook(AAAccountManager)
52
e9502538 53MSClassHook(BBSectionInfo)
e06a3b1a 54MSClassHook(BKSApplicationLaunchSettings)
e9502538 55
d641d519
JF
56MSClassHook(SBAlertItemsController)
57MSClassHook(SBButtonBar)
f7673201 58MSClassHook(SBIconController)
d641d519
JF
59MSClassHook(SBStatusBar)
60MSClassHook(SBStatusBarDataManager)
61MSClassHook(SBStatusBarTimeView)
62MSClassHook(SBUIController)
2c75d26c 63
39f17851 64Class $SafeModeAlertItem;
39f17851 65
2c75d26c
JF
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
88d77501
JF
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
39f17851
JF
93void SafeModeAlertItem$alertSheet$buttonClicked$(id self, SEL sel, id sheet, int button) {
94 switch (button) {
95 case 1:
96 break;
97
98 case 2:
0625b5c3 99 // XXX: there are better ways of restarting SpringBoard that would actually save state
39f17851
JF
100 exit(0);
101 break;
102
103 case 3:
88d77501 104 [[UIApplication sharedApplication] applicationOpenURL:[NSURL URLWithString:@"http://cydia.saurik.com/safemode/"]];
39f17851
JF
105 break;
106 }
107
108 [self dismiss];
109}
110
111void SafeModeAlertItem$configure$requirePasscodeForActions$(id self, SEL sel, BOOL configure, BOOL require) {
2c75d26c 112 UIAlertView *sheet([self alertSheet]);
0625b5c3 113
39f17851 114 [sheet setDelegate:self];
a6712361 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."];
39f17851
JF
116 [sheet addButtonWithTitle:@"OK"];
117 [sheet addButtonWithTitle:@"Restart"];
118 [sheet addButtonWithTitle:@"Help"];
119 [sheet setNumberOfRows:1];
0625b5c3 120
e5a54d69
JF
121 if ([sheet respondsToSelector:@selector(setForceHorizontalButtonsLayout:)])
122 [sheet setForceHorizontalButtonsLayout:YES];
39f17851
JF
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)
a938d168 144 [[$SBAlertItemsController sharedInstance] activateAlertItem:[[[$SafeModeAlertItem alloc] init] autorelease]];
39f17851
JF
145}
146
0625b5c3
JF
147
148// XXX: on iOS 5.0, we really would prefer avoiding
149
d641d519 150MSInstanceMessageHook2(void, SBStatusBar, touchesEnded,withEvent, id, touches, id, event) {
b4f9ed46 151 MSAlert();
d641d519 152 MSOldCall(touches, event);
b4f9ed46
JF
153}
154
d641d519 155MSInstanceMessageHook1(void, SBStatusBar, mouseDown, void *, event) {
39f17851 156 MSAlert();
d641d519 157 MSOldCall(event);
39f17851
JF
158}
159
d641d519 160MSInstanceMessageHook2(void, UIStatusBar, touchesBegan,withEvent, void *, touches, void *, event) {
e5a54d69 161 MSAlert();
d641d519 162 MSOldCall(touches, event);
e5a54d69
JF
163}
164
0625b5c3
JF
165
166// this fairly complex code came from Grant, to solve the "it Safe Mode"-in-bar bug
167
d641d519 168MSInstanceMessageHook0(void, SBStatusBarDataManager, _updateTimeString) {
22a7ee91
JF
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");
e5a54d69
JF
189}
190
0625b5c3 191
f7673201
JF
192static bool alerted_;
193
194static void AlertIfNeeded() {
195 if (alerted_)
39f17851 196 return;
f7673201 197 alerted_ = true;
39f17851 198 MSAlert();
25f84761
JF
199}
200
0625b5c3
JF
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
f7673201
JF
205MSClassMessageHook0(void, AAAccountManager, showMobileMeOfferIfNecessary) {
206 AlertIfNeeded();
207}
208
0625b5c3
JF
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
f7673201
JF
214MSInstanceMessageHook0(void, SBIconController, showInfoAlertIfNeeded) {
215 AlertIfNeeded();
216}
217
0625b5c3
JF
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
d641d519 223MSInstanceMessageHook0(int, SBButtonBar, maxIconColumns) {
25f84761
JF
224 static int max;
225 if (max == 0) {
d641d519 226 max = MSOldCall();
25f84761
JF
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
0625b5c3 240
d641d519
JF
241MSInstanceMessageHook0(id, SBUIController, init) {
242 if ((self = MSOldCall()) != nil) {
b4f9ed46
JF
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;
25f84761
JF
257}
258
1c74ea29 259#define Paper_ "/Library/MobileSubstrate/MobileSafety.png"
dbbe0f32 260
d641d519 261MSClassMessageHook0(UIImage *, UIImage, defaultDesktopImage) {
dbbe0f32
JF
262 return [UIImage imageWithContentsOfFile:@Paper_];
263}
264
d641d519 265MSInstanceMessageHook0(void, SBStatusBarTimeView, tile) {
39f17851
JF
266 NSString *&_time(MSHookIvar<NSString *>(self, "_time"));
267 CGRect &_textRect(MSHookIvar<CGRect>(self, "_textRect"));
25f84761 268 if (_time != nil)
39f17851 269 [_time release];
dbbe0f32 270 _time = [@"Exit Safe Mode" retain];
2c75d26c 271 id font([self textFont]);
88d77501 272 CGSize size([_time sizeWithFont:font]);
39f17851 273 CGRect frame([self frame]);
39f17851
JF
274 _textRect.size = size;
275 _textRect.origin.x = (frame.size.width - size.width) / 2;
276 _textRect.origin.y = (frame.size.height - size.height) / 2;
25f84761 277}
e9502538 278
0625b5c3
JF
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
e9502538
JF
283MSInstanceMessageHook0(BOOL, BBSectionInfo, showsInNotificationCenter) {
284 return NO;
285}
e06a3b1a
JF
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}